TAPENADE is a Automatic Differentiation (AD) tool for FORTRAN. It generates differentiation code by source code transformation. This post gives examples of TAPENADE usage.
TAPENADE Example
For illustration of TAPENADE usage, following test expression is selected
This expression/function is coded in FORTRAN as a subroutine which takes 3 arguments whose in/out behaviour is stated by intent directives
! FILE : test1.f90 subroutine test1(x,y,z) real, intent(in) :: x,y real, intent(out) :: z z = sin(x+2*y)*cos(x-sin(y)); end subroutine test1
TAPENADE executed to differentiate test1 subroutine (in tangent mode) generates following differentiation code
! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of test1 in forward (tangent) mode: ! variations of output variables: z ! with respect to input variables: x y SUBROUTINE TEST1_D(x, xd, y, yd, z, zd) IMPLICIT NONE REAL, INTENT(IN) :: x, y REAL, INTENT(IN) :: xd, yd REAL, INTENT(OUT) :: z REAL, INTENT(OUT) :: zd REAL :: arg1 REAL :: arg1d INTRINSIC COS INTRINSIC SIN arg1d = xd - yd*COS(y) arg1 = x - SIN(y) zd = (xd+2*yd)*COS(x+2*y)*COS(arg1) - SIN(x+2*y)*arg1d*SIN(arg1) z = SIN(x+2*y)*COS(arg1) END SUBROUTINE TEST1_D
Note that each input and output variable that user specified caused insertion of “differentials” of those variables, that is, the input code
subroutine test1(x,y,z)
results in
SUBROUTINE TEST1_D(x, xd, y, yd, z, zd)
In this example,
dx,dy
for input arguments x,y
dz
for output argument z
are inserted just after the original argument.
Actual differential of test1 function is (total differential)
with
Note that in order to compute partial derivatives and , the generated derivative routine test1_d must be called twice
! derivative computation point x = ... y = ... xd = 1.0 yd = 0.0 CALL TEST1_D(x, xd, y, yd, z, zd) ! z and zd ~ dz/dx is obtained xd = 0.0 yd = 1.0 CALL TEST1_D(x, xd, y, yd, z, zd) ! z and zd ~ dz/dy is obtained
In order to avoid such multiple calls, TAPENADE has an alternate differentiation mode called “Tangent Vectorial Mode”
If TAPENADE code is generated with this option then following differentiation code is obtained
! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of test1 in forward (tangent) mode: (multi-directional mode) ! variations of output variables: z ! with respect to input variables: x y SUBROUTINE TEST1_DV(x, xd, y, yd, z, zd, nbdirs) USE DIFFSIZES ! Hint: nbdirsmax should be the maximum number of differentiation directions IMPLICIT NONE REAL, INTENT(IN) :: x, y REAL, INTENT(IN) :: xd(nbdirsmax), yd(nbdirsmax) REAL, INTENT(OUT) :: z REAL, INTENT(OUT) :: zd(nbdirsmax) REAL :: arg1 REAL :: arg1d(nbdirsmax) REAL :: arg2 REAL :: arg2d(nbdirsmax) INTEGER :: nd INTEGER :: nbdirs INTRINSIC COS INTRINSIC SIN arg1 = x + 2*y arg2 = x - SIN(y) DO nd=1,nbdirs arg1d(nd) = xd(nd) + 2*yd(nd) arg2d(nd) = xd(nd) - yd(nd)*COS(y) zd(nd) = arg1d(nd)*COS(arg1)*COS(arg2) - SIN(arg1)*arg2d(nd)*SIN(& & arg2) END DO z = SIN(arg1)*COS(arg2) END SUBROUTINE TEST1_DV
Note that generated code executes differentiation code for all given differentiation directions inside a loop. This, in effect, corresponds to calling “single-direction” mode successively, but with the direction loop inside the differentiation code, subroutine arguments are not passed to calls again and again, increasing the efficiency of code. Another benefit of this mode is hiding details of successive calls.
Following (incomplete) program illustrates use of “multi-directional” mode.
integer, parameter :: nbdirs = 8 real*8 xd(nbdirs),yd(nbdirs) xd = (/1.0, 0.0/) yd = (/0.0, 1.0/) call TEST1_DV(x, xd, y, yd, z, zd, nbdirs) ! zd contains all partial derivatives
TAPENADE GUI Usage
First click add button
Select the source file(s) test.f90
In listbox component all source code added is shown
Click Parse Button. On the left drop-down list box, subroutines/functions available in parsed source codes will appear
In input variables, click Add and select x,y
Pressing OK will add selected variable(s) to input variables listbox component
In output variable, click Add and select z
Pressing OK will add selected variable(s) to output variables listbox component
Finally select differentiation mode and click differentiate. This will generate code and automatically open html report page (if browser is properly defined in environment)
Reverse Mode
Reverse mode first illustrated by a sample routine from INRIA report RR-5237 . This routine implements function
if
otherwise
Exact gradient of this expression is
if
subroutine sub1(x,y,z,o1) real, intent(in) :: x,y,z real, intent(out) :: o1 x = y*x r = x*x+y*y ! x**2*y**2+y**2 if (r>10.0) then r = 10.0 end if o1 = r*z ! z*(x**2*y**2+y**2) end subroutine sub1
differentiating this routine in reverse mode using TAPENADE yields
! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of sub1 in reverse (adjoint) mode: ! gradient, with respect to input variables: x y z o1 ! of linear combination of output variables: o1 SUBROUTINE SUB1_B(x, xb, y, yb, z, zb, o1, o1b) IMPLICIT NONE REAL :: x, y, z REAL :: xb, yb, zb REAL :: o1 REAL :: o1b REAL :: r REAL :: rb INTEGER :: branch CALL PUSHREAL4(x) x = y*x r = x*x + y*y IF (r .GT. 10.0) THEN r = 10.0 CALL PUSHINTEGER4(1) ELSE CALL PUSHINTEGER4(0) END IF rb = z*o1b zb = r*o1b CALL POPINTEGER4(branch) IF (.NOT.branch .LT. 1) rb = 0.0 xb = 2*x*rb CALL POPREAL4(x) yb = x*xb + 2*y*rb xb = y*xb o1b = 0.0 END SUBROUTINE SUB1_B
Note that TAPENADE does not include evaluation of original function in reverse-mode. Some literature states that this TAPENADE specific.
Driver program for reverse differentiation code is given below. Program computes gradient at which bypass the if-statement in the code. The gradient of linear combination selected output variables o1
is returned in differentials of input variables xb,yb,zb
. Weightings are specified in differentials of output variables o1b
. So since in this example there is one output variable, o1b=1
yields gradient of o1
in xb,yb,zb
.
program usesub1b implicit none real x,xb,y,yb,z,zb,o1,o1b o1b = 1.0 x = 1.0 y = 1.5 z = 2.0 call SUB1_B(x, xb, y, yb, z, zb, o1, o1b) print *, "TAPENADE computed gradient:" write(6,'(3(F12.6))') xb,yb,zb print *, "exacy gradient (without if):" write(6,'(3(F12.6))') z*2*x*y**2, z*(x**2+1)*2*y, (x**2+1)*y**2 end program usesub1b
Note that reverse mode differentiation code contains PUSH/POP (store value-retrieve value) operations. To my knowledge, this is the case when some branching like if-statement, subroutine-call occurs in “source” code. Again to my knowledge, number of occurrences of PUSH/POP operations is dependent on the AD tool reverse mode differentiation strategies “store-all”, “re-compute-all”,… . These PUSH/POP operations are defined on separate source files (ADFirstAidKit.tar.gz) available for download at TAPENADE site. Compilation is described in TAPENADE tutorial. For this example, only adBuffer.f,adStack.c
is needed. Following the instructions in tutorial code is compiled by following commands
rem from http://www-sop.inria.fr/tropics/tapenade/tutorial.html gfortran -c usesub1b.f90 gfortran -c sub1_b.f90 gfortran -c adBuffer.f gcc -c adStack.c gfortran sub1_b.o adBuffer.o adStack.o usesub1b.o -o usesub1b
output of executable is as follows
TAPENADE computed gradient: 9.000000 12.000000 4.500000 exacy gradient (without if): 9.000000 12.000000 4.500000
Second example is given to illustrate concept of gradient of linear combination of output variables. In this example two input variables map to . This function code is given below
subroutine samplefun(x,y,z1,z2) real, intent(in) :: x,y real, intent(out) :: z1,z2 z1 = 0.5*(x**2-y**2) z2 = 0.5*(x**2+y**2) end subroutine samplefun
and its reverse-mode differentiation is obtained as
! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of samplefun in reverse (adjoint) mode: ! gradient, with respect to input variables: x y z1 z2 ! of linear combination of output variables: z1 z2 SUBROUTINE SAMPLEFUN_B(x, xb, y, yb, z1, z1b, z2, z2b) IMPLICIT NONE REAL, INTENT(IN) :: x, y REAL :: xb, yb REAL :: z1, z2 REAL :: z1b, z2b xb = 0.5*2*x*z1b + 0.5*2*x*z2b yb = 0.5*2*y*z2b - 0.5*2*y*z1b z1b = 0.0 z2b = 0.0 END SUBROUTINE SAMPLEFUN_B
Define linear combination of two outputs as then gradient of this new quantity is
To check this in differentiation code, manually call SAMPLEFUN_B
with z1b=w1, z2b=w2
to get
xb = 0.5*2*x*z1b + 0.5*2*x*z2b => 0.5*2*x*w1 + 0.5*2*x*w2
yb = 0.5*2*y*z2b - 0.5*2*y*z1b => 0.5*2*y*z2b - 0.5*2*y*z1b
which is recognized as actual gradient as required.
Missing functions
TAPENADE handles all common mathematical functions sin, cos, exp, sqrt in the differentiation process, but if some function is not known to TAPENADE and/or its implementation is missing, instead of giving an error and quitting, TAPENADE continues with generation of differentiation code using the apparent signature of the function. Then user is responsible for supplying differentiation code of the function with missing implementation. To exemplify situation, the function
that computes the sinc-of-radial distance in nD space (using the DOT_PRODUCT) is differentiated with TAPENADE. Code to be differentiated is given below
! FILE:radsinc.f90 subroutine radsinc(r,n,rs) integer, intent(in) :: n real, intent(in) :: r(n) real, intent(out) :: rs real rad rad = sqrt(dot_product(r,r)) if (rad==0) then rs = 1.0 else rs = sin( rad ) / rad end if end subroutine radsinc
and differentiation code in tangent mode is obtained as
! FILE: radsinc_d.f90 ! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of radsinc in forward (tangent) mode: ! variations of output variables: r rs ! with respect to input variables: r SUBROUTINE RADSINC_D(r, rd, n, rs, rsd) IMPLICIT NONE INTEGER, INTENT(IN) :: n REAL, INTENT(IN) :: r(n) REAL, INTENT(IN) :: rd(n) REAL, INTENT(OUT) :: rs REAL, INTENT(OUT) :: rsd REAL :: rad REAL :: radd EXTERNAL DOT_PRODUCT EXTERNAL DOT_PRODUCT_D REAL :: DOT_PRODUCT REAL :: DOT_PRODUCT_D REAL :: result1 REAL :: result1d INTRINSIC SIN INTRINSIC SQRT result1d = DOT_PRODUCT_D(r, rd, r, rd, result1) IF (result1 .EQ. 0.0) THEN radd = 0.0 ELSE radd = result1d/(2.0*SQRT(result1)) END IF rad = SQRT(result1) IF (rad .EQ. 0) THEN rs = 1.0 rsd = 0.0 ELSE rsd = (radd*COS(rad)*rad-SIN(rad)*radd)/rad**2 rs = SIN(rad)/rad END IF END SUBROUTINE RADSINC_D
Actual differential of the function is
Since the function DOT_PRODUCT is not in TAPENADE standard function list and no implementation is available, TAPENADE cannot generate a differentiation code for function DOT_PRODUCT. So it just emits the differential code result1d = DOT_PRODUCT_D(r, rd, r, rd, result1)
(line 25) and expects the user to supply this function at compile time. For this reason, it informs the compiler that actual object code is defined elsewhere by inserting EXTERNAL
declaration(s) in the source code (line 18). Derivative of dot product can be obtained from
which is translated to code as follows
! FILE: DOT_PRODUCT_D.f90 real function dot_product_d(a, ad, b, bd) implicit none real, intent(in), dimension(:) :: a, b, ad, bd dot_product_d = dot_product(ad,b) + dot_product(a,bd) end function dot_product_d
From the generated code, it is observed that TAPENADE generated code computes function and its derivative in the same routine, so if conformance to originally generated code is sought then above definition is updated as
! FILE: DOT_PRODUCT_D.f90 real function dot_product_d(a, ad, b, bd,dotprod) implicit none real, intent(in), dimension(:) :: a, b, ad, bd real, intent(out) :: dotprod dotprod = dot_product(a,b) dot_product_d = dot_product(ad,b) + dot_product(a,bd) end function dot_product_d
To test the TAPENADE generated routine following test program is used
! FILE: main.f90 program main implicit none interface real function dot_product_d(a, ad, b, bd,dotprod) implicit none real, intent(in), dimension(:) :: a, b, ad, bd real, intent(out) :: dotprod end function dot_product_d end interface real, dimension(4) :: r, rd real :: rs, rsd, rstrue, rsdtrue, rdotr, rdotrd, rdotr2 real :: dotprod,dotprod_d r = (/ 1., 2., 3., 4. /) ! vector in 4D rd = (/ 40, 30, 20, 10 /) ! rate(/differential) of vector ! check dot product rdotr = dot_product(r,r) rdotrd = dot_product(r,rd) print *, "r-dot-dr:", rdotrd print *, "r-dot-r :", rdotr ! check derivative of dot product dotprod_d = dot_product_d(r, rd, r, rd, rdotr2) print *, "r-dot-r :", rdotr2 print *, "d(r-dot-r):", dotprod_d ! call TAPENADE generated routine ! SUBROUTINE RADSINC_D(r, rd, n, rs, rsd) call RADSINC_D(r, rd, 4, rs, rsd) ! compute analytical result rsdtrue = (rdotrd/rdotr**1.5)*(cos(sqrt(rdotr))*sqrt(rdotr)-sin(sqrt(rdotr))) rstrue = sin(sqrt(rdotr))/sqrt(rdotr) print *, "analytical results:" write (*,'(A8,F16.8,X,A8,F16.8)') "sinc = ",rstrue, "dsinc = ",rsdtrue print *, "TAPENADE results:" write (*,'(A8,F16.8,X,A8,F16.8)') "sinc = ",rs, "dsinc = ",rsd end program main
Compilation command is
gfortran dotproduct_d.f90 radsinc_d.f90 main.f90 -o main.exe
Program tests the routine for the inputs
output of program is given below
r-dot-dr: 200.00000 r-dot-r : 30.000000 r-dot-r : 30.000000 d(r-dot-r): 400.00000 analytical results: sinc = -0.13172643 dsinc = 5.49430418 TAPENADE results: sinc = -0.13172643 dsinc = 5.49430418
showing that the results agree
Note that main program contains an interface declaration for dot_product_d
. This is required for routines with assumed shaped array arguments as explained in Fortran Bugs/Surprises. Since TAPENADE generated routine also uses the routine dot_product_d
same interface is added to SUBROUTINE RADSINC_D
.This eliminates some of the previous declarations added by TAPENADE. Modified version of TAPENADE code is given below
! FILE: RAD_SINC.f90 ! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of radsinc in forward (tangent) mode: ! variations of output variables: r rs ! with respect to input variables: r SUBROUTINE RADSINC_D(r, rd, n, rs, rsd) IMPLICIT NONE INTEGER, INTENT(IN) :: n REAL, INTENT(IN) :: r(n) REAL, INTENT(IN) :: rd(n) REAL, INTENT(OUT) :: rs REAL, INTENT(OUT) :: rsd REAL :: rad REAL :: radd interface real function dot_product_d(a, ad, b, bd,dotprod) implicit none real, intent(in), dimension(:) :: a, b, ad, bd real, intent(out) :: dotprod end function dot_product_d end interface ! Following declaration no more needed due to above interface definition !EXTERNAL DOT_PRODUCT !EXTERNAL DOT_PRODUCT_D !REAL :: DOT_PRODUCT !REAL :: DOT_PRODUCT_D REAL :: result1 REAL :: result1d INTRINSIC SIN INTRINSIC SQRT result1d = DOT_PRODUCT_D(r, rd, r, rd, result1) IF (result1 .EQ. 0.0) THEN radd = 0.0 ELSE radd = result1d/(2.0*SQRT(result1)) END IF rad = SQRT(result1) IF (rad .EQ. 0) THEN rs = 1.0 rsd = 0.0 ELSE rsd = (radd*COS(rad)*rad-SIN(rad)*radd)/rad**2 rs = SIN(rad)/rad END IF END SUBROUTINE RADSINC_D
Lines 18-24 are the modifications to original code and 27-30 are the commented lines.
Similar Technique
For some cases, need for derivative of missing function may be avoided by some minor modifications to source code. For example suppose that there is a routine that assembles a linear system (a matrix and a RHS vector) and solves linear system using a subroutine from compiled library whose source code is not available
subroutine SOLVE(input1, input2, x, n) integer, intent(in) :: n real, intent(in) :: input1, input2 real, intent(out) :: x(n) real :: A(n,n), b(n) call assemble(input1, input2, A, b, n) call solveLinearSystem(A,b,x) end subroutine SOLVE
when differentiated in tangent mode, following code is obtained from TAPENADE
! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of solve in forward (tangent) mode: ! variations of output variables: x input1 input2 ! with respect to input variables: input1 input2 SUBROUTINE SOLVE_D(input1, input1d, input2, input2d, x, xd, n) IMPLICIT NONE INTEGER, INTENT(IN) :: n REAL, INTENT(IN) :: input1, input2 REAL, INTENT(IN) :: input1d, input2d REAL, INTENT(OUT) :: x(n) REAL, INTENT(OUT) :: xd(n) REAL :: a(n, n), b(n) REAL :: ad(n, n), bd(n) EXTERNAL ASSEMBLE EXTERNAL ASSEMBLE_D EXTERNAL SOLVELINEARSYSTEM EXTERNAL SOLVELINEARSYSTEM_D ad = 0.0 bd = 0.0 CALL ASSEMBLE_D(input1, input1d, input2, input2d, a, ad, b, bd, n) xd = 0.0 CALL SOLVELINEARSYSTEM_D(a, ad, b, bd, x, xd) END SUBROUTINE SOLVE_D
but as stated in beginning, there is no way to obtain or too difficult/impractical to obtain differentiation code SOLVELINEARSYSTEM_D
. Trick is to obtain derivatives by some manipulation on the total differential of original expression which yields
To utilize this formula, TAPENADE code is modified as follows
! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of solve in forward (tangent) mode: ! variations of output variables: x input1 input2 ! with respect to input variables: input1 input2 SUBROUTINE SOLVE_D(input1, input1d, input2, input2d, x, xd, n) IMPLICIT NONE INTEGER, INTENT(IN) :: n REAL, INTENT(IN) :: input1, input2 REAL, INTENT(IN) :: input1d, input2d REAL, INTENT(OUT) :: x(n) REAL, INTENT(OUT) :: xd(n) REAL :: a(n, n), b(n) REAL :: ad(n, n), bd(n) EXTERNAL ASSEMBLE EXTERNAL ASSEMBLE_D EXTERNAL SOLVELINEARSYSTEM EXTERNAL SOLVELINEARSYSTEM_D ad = 0.0 bd = 0.0 CALL ASSEMBLE_D(input1, input1d, input2, input2d, a, ad, b, bd, n) xd = 0.0 !CALL SOLVELINEARSYSTEM_D(a, ad, b, bd, x, xd) CALL SOLVELINEARSYSTEM(a, b, x) CALL SOLVELINEARSYSTEM(a, bd-matmul(ad,x), xd) END SUBROUTINE SOLVE_D
Case of MATMUL function
MATMUL function is similar to DOT_PRODUCT in that both are inner-products, essentially. Former one is acting on second and first order tensors and latter one acting on first order tensors (vectors). So similar to DOT_PRODUCT example above, manual declaration for differentiation of MATMUL would be,
which is coded as
function matmul_d(a,ad,b,bd,ab) implicit none real, intent(in), dimension(:,:) :: a,b,ad,bd real, intent(out), dimension( size(a,1), size(b,2) ) :: ab real matmul_d( size(a,1), size(b,2) ) ab = matmul(a,b) matmul_d = matmul(ad,b) + matmul(a,bd) end function matmul_d
Such declaration poses some Fortran 90 problems. In fortran(90) MATMUL can be used to multiply vector-matrix, matrix-matrix and matrix-vector, but above declaration gives signature for matrix-matrix version. One possible solution is to use generic-interfaces but i did not try this yet; instead i devised example consisting of only matrix-matrix multiplications. Example i choose is quadratic form given as
whose (exact) differential is given by
quadratic function is coded in FORTRAN as follows
subroutine quadraticForm(A,x,QF) implicit none real, intent(in), dimension(2,2) :: A real, intent(in), dimension(2,1) :: x real, intent(out), dimension(1,1) :: QF real :: xp(2,1), xpt(1,2) xp = matmul(A,x) xpt = transpose(xp) QF = matmul(xpt,x) end subroutine quadraticForm
Note that vectors are replaced with column or row vector versions to conform to MATMUL_D declaration. Code obtained from tangent mode differentiation of TAPENADE is modified to yield following
! Generated by TAPENADE (INRIA, Tropics team) ! Tapenade 3.3 (r3163) - 09/25/2009 09:03 ! ! Differentiation of quadraticform in forward (tangent) mode: ! variations of output variables: qf x a ! with respect to input variables: x a SUBROUTINE QUADRATICFORM_D(a, ad, x, xd, qf, qfd) IMPLICIT NONE INTERFACE function matmul_d(a,ad,b,bd,ab) implicit none real, intent(in), dimension(:,:) :: a,b,ad,bd real, intent(out), dimension( size(a,1), size(b,2) ) :: ab real matmul_d( size(a,1), size(b,2) ) end function matmul_d function dot_product_d(a,ad,b,bd,ab) implicit none real dot_product_d real, intent(in), dimension(:) :: a,ad,b,bd real, intent(out) :: ab end function dot_product_d function transpose_d(v, vd, vt) implicit none real, dimension(:,:), intent(in) :: v, vd real, dimension(size(v,2), size(v,1) ), intent(out) :: vt real, dimension( size(v,2), size(v,1) ) :: transpose_d end function transpose_d END INTERFACE REAL, DIMENSION(2, 2), INTENT(IN) :: a REAL, DIMENSION(2, 2), INTENT(IN) :: ad REAL, DIMENSION(2, 1), INTENT(IN) :: x REAL, DIMENSION(2, 1), INTENT(IN) :: xd REAL, DIMENSION(1, 1), INTENT(OUT) :: qf REAL, DIMENSION(1, 1), INTENT(OUT) :: qfd REAL :: xp(2, 1), xpt(1, 2) REAL :: xpd(2, 1), xptd(1, 2) ! EXTERNAL MATMUL ! EXTERNAL MATMUL_D ! EXTERNAL TRANSPOSE ! EXTERNAL TRANSPOSE_D ! REAL :: MATMUL ! REAL :: MATMUL_D ! REAL :: TRANSPOSE ! REAL :: TRANSPOSE_D xpd = MATMUL_D(a, ad, x, xd, xp) xptd = TRANSPOSE_D(xp, xpd, xpt) qfd = MATMUL_D(xpt, xptd, x, xd, qf) END SUBROUTINE QUADRATICFORM_D
Test code is given below
program main implicit none real :: A(2,2), AD(2,2), x(2), xd(2), qf, qfd A = reshape( (/ 1,2,2,1 /), (/2,2/) ) AD = reshape( (/ -3,4,5,1 /), (/2,2/) ) x = (/ 3,4 /) xd = (/ 7,2 /) call quadraticForm(A,x,QF) print *, A print *, AD print *, qf ! SUBROUTINE QUADRATICFORM_D(a, ad, x, xd, qf, qfd) call QUADRATICFORM_D(A, AD, x, xd, qf, qfd) print *, qfd end program main
Test inputs are
and outputs are
A : 1.0000000 2.0000000 2.0000000 1.0000000 AD : -3.0000000 4.0000000 5.0000000 1.0000000 QF : 73.000000 dQF: 291.00000
Manual calculation (not shown here) results in 291, also.
Compilation and extra code is given below
rem compilation of MATMUL example gfortran main.f90 quadraticForm.f90 quadraticform_d.f90 missingfunctions.f90 -o main.exe
! FILE: MissingFunctions.f90 function matmul_d(a,ad,b,bd,ab) implicit none real, intent(in), dimension(:,:) :: a,b,ad,bd real, intent(out), dimension( size(a,1), size(b,2) ) :: ab real matmul_d( size(a,1), size(b,2) ) ab = matmul(a,b) matmul_d = matmul(ad,b) + matmul(a,bd) end function matmul_d real function dot_product_d(a,ad,b,bd,ab) implicit none real, intent(in), dimension(:) :: a,ad,b,bd real, intent(out) :: ab ab = dot_product(a,b) dot_product_d = dot_product(ad,b) + dot_product(a,bd) end function dot_product_d function transpose_d(v, vd, vt) implicit none real, dimension(:,:), intent(in) :: v, vd real, dimension(size(v,2), size(v,1) ) :: transpose_d real, dimension(size(v,2), size(v,1) ), intent(out) :: vt vt = transpose(v) transpose_d = transpose(vd) end function transpose_d
Notes:
I have obtained strange results (automatically declared local variables of wrong size) from TAPENADE, when not used local variables for transposed variables. I don’t know the reason, but such behaviour of TAPENADE can avoided by some minor revisions to available code, as in the case of this example.