|
Frequently Asked Questions on TAPENADELast modified: Wed Feb 8 11:09:03 CET 2012 by the developers of Tapenade
|
|
to the "tapenade-users" mailing list
to share with other users, to get the latest news, and to
submit
your questions and remarks !
Acronym :

| What does TAPENADE mean? Why did you choose this name? |
|
Well, we are all searching for the ultimate Automatic Differentiation
tool, but we would be silly to claim that we found it! Therefore,
TAPENADE could mean "Tangent and Adjoint PENultimate Automatic
Differentiation Engine".
Honestly, we arranged this acronym after we chose the name! We simply liked the name TAPENADE because it is a typical ingredient of our local cuisine. It is made with capers, olives, and anchovies, and it makes a perfect apéritif in summer, on small toasts with a glass of cold rosé wine. Just in case you couldn't find some in your local grocery, here is a recipe of Tapenade from a famous book of provençal cuisine. |
Languages accepted by Tapenade parsers, Syntax Errors :

|
After calling for differentiation of my files, I get the "Something went wrong during differentiation" web page, which tells me that there is a syntax error in my files. What can I do ? |
Yes, this happens in many cases. First notice that there are many
sorts of so-called "syntax errors", which result in slightly different
error messages. You may get one or many of the following:
Our Fortran parser accepts all of the Fortran77 standard, plus most of the usual constructor's extensions, present or past (vax, sun, gould, connexion-machine, cray). Beware of possible conflicts between the standard and the extensions! For example in the standard, white-spaces are not significant: if the text functional is found in the code, it is seen as the keyword function followed by the identifier al, probably leading to a syntax error ! Therefore you mustn't call one of your code identifiers functional or subroutines. We couldn't change this rule because some old programs did strip away white spaces! Check that your file really complies with the standard or the constructor's extension. If this is the case, Tapenade should parse it correctly. If it does not, there is probably a bug in our parser. Please send us a message so that we can fix it quickly. Our Fortran95 parser accepts of course Fortran90, and also the HPF extensions. Be careful with comments delimitor in the free format style: comments must start with a "!". Our C parser accepts ANSI C plus a few common extensions, BUT it doesn't accept C++ input. If you really think your program complies with these standards, and our parser rejects it, please send us a message so that we can fix our parser. Notice furthermore that our parser does not support the cpp directives. For instance, we cannot parse files containing #define or #ifdef directives. Please run your files through cpp before sending them to Tapenade. If the error you get is an internal error, please send us a message so that we can fix it ! |
Syntax of directives that modify differentiation (
|
I get the following error when Tapenade parses my source: "File: Fortran Parser: Lexical error at line nn in foo.f95, Unknown keyword : c$ad nocheckpoint" More generally, what is the exact syntax of the directives that I can use in my source to alter its differentiation? |
Tapenade understands a small number of directives that can be used to alter the
differentiated result. These directives must be provided in the source code as
single-line comments that begin with $AD.
Since the syntax of comments changes with the language, the syntax of tapenade directives
changes accordingly, e.g. for directive "nocheckpoint":
|
Language accepted by the A.D. engine ; Limitations:

| I can see that Tapenade differentiates Fortran77 programs. What about Fortran95 or C ? What about their various constructors' dialects ? Do you have plans for object-oriented languages ? Do you differentiate programs with pointers, with records, with modules? |
We are talking here of actual differentiation, not parsing.
About parsing, see the "Syntax" question.
We tried to differentiate correctly the standard features of Fortran
(77 and 95) and of C, plus most of the usual
constructor's extensions, present or past
(vax, sun, gould, connexion-machine, cray).
As far as differentiation is concerned, most of these extensions
have little effect anyway.
Of course there are possible bugs and a few restrictions that
will be lifted progressively.
We do not differentiate pure C++ constructs (Tapenade doesn't even parse them!).
In the following list, parts in black are what Tapenade should differentiate
correctly, parts in red are what is not
differentiated:
Specifically for the reverse mode: a program piece that will be checkpointed (e.g. a procedure call) must be reentrant. This means that there must be a way to call it twice, at the cost of some manipulations to restore the initial state. For instance a procedure that modifies an internal hidden variable or state (think of IO pointers in files, or private counter variables...) is not reentrant. In this case, you must either transform the program piece to make it reentrant, or tell Tapenade not to checkpoint it. Differentiation of object-oriented languages is still not in our immediate goals, and neither are the templates of C++. |
Black-box procedures, unknown external procedures:

|
Tapenade complains about unknown external or intrinsic procedures,
how can I teach it about them ? I need to define a new set of library files or modify the existing *Lib files. What is the syntax in there and what does it mean? |
|
If the program you want to differentiate contains calls to
EXTERNALs, INTRINSICs, library, or other "Black-Box" routines, then you may need to provide
TAPENADE with some information about these Black-Box routines.
You may also have to do this if we forgot to add into the standard
library files the information on some intrinsic subroutines
that you actually use. It can also happen that you have the source of a procedure, but you don't want to give it to TAPENADE, because TAPENADE would then differentiate it in its own standard manner, and you know how to differentiate it better by hand! So make it a Black-Box by just forgetting to provide its source. Some intrinsic functions, such as MAX, are not well treated when they have an unusual number of argument. For instance, MAX(x, y+z, 0.0) will not be analyzed, nor differentiated corectly. The workaroud is either to replace this by two nested calls to MAX, or to treat the 3-arguments MAX as another Black-Box function. So, for whatever reason, TAPENADE has no source for a subroutine or function (suppose it is called BBOX). This has some unfortunate consequences. First, during type-checking, TAPENADE cannot compare the number and types of the actual arguments to the number and types of the formal arguments of BBOX. Furthermore, it doesn't know which arguments will be read and/or overwritten. More specifically for AD, TAPENADE doesn't know how to propagate activity through BBOX, i.e. which outputs of BBOX depend in a differentiable manner on which inputs of BBOX. Last but not least, TAPENADE doesn't know the name of the differentiated version of BBOX, in the case where you have programmed it already. Notice that, although all this information is missing, TAPENADE will often generate good enough code, thanks to a number of conservative assumptions:
SPECIFYING BLACK-BOX ROUTINES IN *Lib FILES:The "*Lib" files contain information about library, intrinsic, external routines or any other Black-Box routine. This will help Tapenade produce more efficient code. For instance, there is one such file "F77GeneralLib" already in you installation "lib/" directory. You may modify it. You may also create your own files such as "MyAppliGeneralLib", in any "MyDirectory", and then you will pass these file names to your tapenade command using the -ext option.tapenade -ext MyDirectory/MyAppliGeneralLib ...Each entry is about one subroutine or function, and gives info about number, type, intent of arguments, and possibly info related to AD. For example you might type, for a subroutine named BBOX and for a function called FBBOX: subroutine bbox:
external:
shape:(param 1,
param 2,
common /c1/[0,*[,
common /c2/[0,16[,
common /c2/[16,176[,
param 3,
param 4,
param 5)
type :(ident real,
ident real,
arrayType(ident real,dimColons(dimColon(none(),none()))),
modifiedTypeName(modifiers(ident double), ident real),
arrayType(modifiedTypeName(modifiers(ident double), ident real),
dimColons(dimColon(none(),none()))),
ident boolean,
arrayType(ident character,dimColons(dimColon(none(),none()))),
ident character)
ReadNotWritten: (1, 0, 0, 0, 0, 0, 1, 1)
NotReadThenWritten: (0, 0, 1, 0, 0, 0, 0, 0)
ReadThenWritten : (0, 1, 0, 1, 1, 1, 0, 0)
deps: (id,
1, 1, 0, 0, 0, 0, 0, 0,
1, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
id,
id)
function fbbox:
intrinsic:
shape: (param 1, param 2, result)
type: (ident real, ident real, modifiedTypeName(modifiers(ident double), ident real))
ReadNotWritten: (1,1,0)
NotReadThenWritten:(0,0,1)
NotReadNotWritten: (0,1,0)
derivative: binary(call(ident fbbox, expressions(metavar X, metavar Y)),
mul(realCst 2.0, metavar X),
expressions(binary(metavar X, none(), add(intCst 5, metavar Y)),
binary(metavar Y, none(), div(realCst 1.0, metavar Y))))
The "external:" line states that the procedure is external. For an intrinsic,
just replace this line by "intrinsic:".The "shape:" part is necessary, because it defines the (arbitrary) order in which arguments will be referred to in the sequel. Its syntax is either param N that designates the N-th formal parameter, or result for the returned value if BBOX is a function, or common /xx/[0,8[ for an argument which is found from offset 0 to offset 8 in common /xx/. All next lines are optional. If they are missing, the corresponding conservative assumptions are made. The "type:" part gives the types of each argument, in the same order. The syntax of a type is rather heavy: we didn't find the time to make it cleaner yet, sorry! Actually it's the internal tree representation of the type inside Tapenade memory. In BNF-like, a TYPE can be:
TYPE ::= ident NAME
(NAME belonging to {real, complex, integer, character, boolean},
for primitive types)
TYPE ::= modifiedTypeName(modifiers(MODIFIERS), TYPE)
(MODIFIERS being a list of mostly ident and intCst such as double or 16
for primitive type size modifiers)
TYPE ::= arrayType(TYPE, dimColons(DIMS))
(for arrays with a fixed number of dimensions)
DIMS ::= DIM
DIMS ::= DIM , DIMS
DIM ::= dimColon(BOUND, BOUND)
BOUND ::= none()
(for an unspecified dimension lower or upper bound)
BOUND ::= intCst N
TYPE ::= arrayType(TYPE, dimColons())
(for arrays with any number of dimensions)
TYPE ::= pointerType(TYPE)
(for a pointer to another type (available in near future))
TYPE ::= void()
(void type, mostly means you don't want to care about it)
For example, real*8 is written modifiedTypeName(modifiers(intCst 8), ident real) Arrays are built with the combinator "arrayType", that binds the array elements' type with a list of dimensions ("dimColon") which are pairs of the lower and upper bound. So far, these bounds are not very useful, so you might just as well leave them as "none()" or even use the arrayType(TYPE, dimColons()) form. Take a look at F77GeneralLib to find more examples. This mechanism is far from being complete. For example, there is still no syntax for structured argument types ("derived types"), although they are handled well inside Tapenade. Nothing exists for named arguments, nor for a variable number of arguments. For a variable number of argument, you may have to create a *Lib entry for each number of arguments that actually occurs in your code. In addition to this, there is our "metavar" type: TYPE ::= metavar NAMEThis metavar type represents any type. If it appears with the same NAME several times in the list of types of the present procedure, each occurrence must match the same type. The value of NAME just indicates which "sort" of type should be there, i.e. if actual pattern-matching fails to find a solution type for the metavar, then TAPENADE will choose ident NAME. Otherwise you may name your metavar "foo" instead of "real", and it should work just the same. This mechanism is by no means complete, but it helps. Look at function "SUM" in F77GeneralLib for a less trivial use of metavars: in SUM, the metavar lets you specify that it takes an array of REAL and returns a REAL, or takes an array of REAL*8 and returns a REAL*8, or even an array of INTEGER and returns an INTEGER. The "ReadNotWritten, NotReadThenWritten, ReadThenWritten, NotReadNotWritten" info gives the read-write signature of each argument. There is 1 for one argument when there exists at least one execution case of the procedure for which at least a part of this argument is "affectedInThisWay", where "affectedInThisWay" is the name of the info (i.e. one of ReadNotWritten, NotReadThenWritten, ReadThenWritten, NotReadNotWritten). This is a new notation, valid from version 3.6 on. This new notation replaces the previous notation (R, NR, W, NW) which had a different meaning, less accurate. Although the previous notation is still accepted, we advise you to switch rapidly to the new notation in your home-made libraries. The "deps:" entry is related to AD. It is the differentiable dependency matrix, of each output value with respect to input values, for each argument. The matrix is square (id elements count for one row), and the order of the rows is the order of the parameters as listed in the "shape:" entry for this procedure. The same holds for the order of the columns. For example, the element in row 2, column 5 specifies whether the output value of param 2 depends on (i.e. "has a non trivial-zero derivative with respect to") the input value of the variable from offset 16 to 176 in common /c2/. Here there is a zero, meaning a trivial zero derivative, i.e. no dependency. For example here, the result in parameter 2 depends on the input parameters 1 and 2, the result in the first variable in common /c2/ doesn't depend on any input, and the result in the tail array of /c2/ depends on the inputs in parameter 2 and in the whole of /c2/. An "id" entry instead of a complete line states that the argument is left unmodified, which is a stronger information than a line with just one "1", because it implies that the partial derivative is certainly "1.0". This is the case here for parameters 1, 4, and 5. On the other hand, parameter 3 is overwritten, but of course depends on nobody, since it has a non-differentiable type. For the same reason, nobody depends on parameters 3, 4, and 5. Notice also that nobody depends on the input in /c1/: it is an output only. The other thing you may want to specify is the actual derivative of the black-box procedure. Three main possibilities:
In this example, one can find, in order, the pattern that must match the actual call, therefore instantiating the metavariables X and Y, then an expression which is a common factor to all partial derivatives (which can be "none()" if there is no common factor), then the list of each partial derivative of the function's result with respect to each metavariable: for each metavariable key, there is an expression that defines the corresponding partial derivative expression, which has to be multiplied by the common factor if present. For instance in the above example, we have expressed that the partial derivative of FBBOX(X,Y) with respect to X is the expression 2.0*X(Y+5) and its partial derivative with respect to Y is the expression 2.0*X/Y. Notice that we didn't give a "deps:" entry, because the default for an intrinsic function is just what we want! More examples can be found in the F77GeneralLib file, for instance the specification of the partial derivatives of COS mentioned above. Please notice that these *Lib files are not able to represent all you would like to. For example, there is no support for procedures with variable number of arguments or named arguments. We hope it can be useful in most situations, though, until we devise a better and more complete syntax for these *Lib files. SPECIFYING BLACK-BOX ROUTINES WITH DUMMY ROUTINES:Sometimes the *Lib files cannot express some behavior of the black-box procedure, Tapenade does not understand this information well and doesn't differentiate the black-box procedure correctly. Sometimes also, the syntax of the *Lib files is really painful and one looks for a simpler method. Here is a possible simpler method that you can use. Write a dummy definition of the problematic black-box procedure, just like a standard procedure, with statements that just "represent" the data dependences of the actual procedure. We mean that the computations inside the dummy procedure may be totally wrong and meaningless, provided they implement the same differentiable data-dependencies between the inputs and the outputs. In other words again, for each output z that depends in a differentiable way on inputs ,say, x and y, just write a statement likez = x*yin the dummy procedure. Give this dummy definition only to the differentiation process, and remove the differentiated dummy procedure from the differentiated code returned by Tapenade. Finally, write by hand the correct differentiated black-box procedure and link it with the differentiated code returned by Tapenade. |
Other Differentiation Modes:

|
I need higher-order derivatives, Hessians, Taylor series. I need the full Jacobian Matrix. |
|
In its present form, Tapenade builds programs that compute
directional derivatives and gradients, i.e. in our jargon the
forward and the reverse mode of A.D.
Tapenade does not provide Jacobians, nor higher-order derivatives,
nor Hessians, nor Taylor series.
However, there are ways to cope with that. For example, one can use the forward mode twice, to get directional second derivatives. We know of some people who have tried that with Tapenade, and apparently it worked. Notice however that this is certainly less efficient than a specific mode for second derivatives. One reason for that is that if you use the forward mode twice, you get two redundant copies of each computation of the first derivatives! Then, from the higher-order directional derivatives, you can derive the higher-order Hessian tensors. This is sometimes more clever than computing these tensors from scratch, because one may take advantage of the tensor's symmetries. This question is discussed for example in GriewankUtkeWalther97 Tapenade does nothing about Taylor series nor intervals. These two could be interesting new modes to include into Tapenade, but this is not in our current plans. Many applications need the Jacobian matrix. Here are some ways to get it:
|
Type mismatches:

|
Tapenade Type-Checker sends me a lot of messages of the form: (TC16) Type mismatch in assignment: REAL*8 receives DOUBLE PRECISION What do they mean? Are they important? What must I do? |
|
These messages may be important, but in many cases they are only
"paranoiac" warnings. Your compiler may perfectly well accept that
an argument of kind DOUBLE PRECISION is passed to a procedure
that expects a REAL*8 or vice-versa. However this is
against the Fortran standard, and it may be the cause of portability
problems in the future. This is why Tapenade complains while
your compiler doesn't.
For instance g77 does not complain on: real*8 var var = 1.0d0but Tapenade does because the standard says that 1.0d0 is DOUBLE PRECISION and not REAL*8. More importantly concerning AD in the reverse mode, the PUSH and POP procedures -- that are used intensively -- rely on an exact knowledge of the size in bytes of their argument. So at the minimum you should check that the sizes of these types match on the machine/compiler you will use, and that these are the sizes that Tapenade will use. To check the sizes on your machine/compiler, refer to the beginning of the FAQ section on validation of the differentiated programs . To modify the sizes used by Tapenade
to adapt them to the results of the above check, use the command-line
options -r8 etc.Anyway, all these precision problems are a nigntmare. We agree that most of the messages that Tapenade sends about this are a nuisance. Most probably you can neglect them. Maybe some day we shall offer an option to hide them away. Nevertheless it still occurs sometimes, especially with Fortran95, that these messages indicate bugs in Tapenade. For instance Tapenade may have lost a dimension of an array. In this case, it is unlikely that differentiation will work. In this situation, when you see that your program is obviously correct and Tapenade complains, please send us a message so that we can improve Tapenade. |
Aliasing problems:

|
I get the following message from Tapenade: (DF02) Potential aliasing in calling function Func, between arguments arguments What does it mean and is it important? |
|
This means that Tapenade detected a memory overlap between
arguments of ranks n1
and n2 of a call to a subroutine or function.
What is aliasing? Suppose a subroutine has many arguments (i.e. formal parameters or commons or globals...) At each call site, each of these formal parameters is given an "actual parameter", i.e. a reference to an actual variable, scalar or array, or an expression, or a constant... Aliasing happens when the same actual parameter is given to two different formal parameters. and one of these two formal parameters is overwritten by the subroutine. More generally, it happens when two actual parameters overlap in memory, and at least one may be overwritten. Please notice that such aliasing results in code that does not conform to the Fortran standard. e.g. see MetcalfReid96 section 5.7.2 page 91. This is an important warning, because Tapenade, like most static analysis tools, analyses each subroutine in its own local context, i.e. assumes that each formal parameter is a different memory location. And this assumption leads to a generated program (differentiated) that will fail if there is aliasing. For example:
subroutine F(real a, real b) {
a = 3*b
}
would be differentiated in the reverse mode as (grossly) :
subroutine F_B(real a, real ab, real b, real bb) {
bb = bb + 3*ab
ab = 0.0
}
and this works fine as long as the actual a
and b are different variables.
But if a=b=x, F does
x = 3*x, and what you want
for F_B is xb = 3*xb.
If you check, this not what you
get by calling the above F_B.
Similar problems arise in most program transformations,
such as parallelization or partial evaluation, or others...
Notice however that aliasing, here detected by a static analysis, is by essence undecidable. Therefore Tapenade may detect aliasing when there is none! Also, it is frequent that, although there is aliasing, the differentiated program will run fine. It is the responsibility of the end-user, who knows the program, to check that his aliasing is harmless. Usually, when aliasing is really a problem, one can easily fix that by introducing temporary variables, so that memory locations of the parameters do not overlap. |
Missing differentiated routines for intrinsic or Black-Box routines:

|
I can't compile my code because it misses the differentiated routines
for some intrinsics, externals, or Black-Box routines. I get a compiler message saying it needs to link with DCMPLX_B How do I define the adjoint of an intrinsic routine? |
|
This is perfectly natural. In your original code were Black-Box routines, such a DCMPLX, i.e. either INTRINSICs or EXTERNALs or other routines for which you didn't provide the source. As a consequence, Tapenade has placed in the differentiated code a call to the differentiated routine DCMPLX_D in tangent mode or DCMPLX_B in adjoint mode, and sent you a message requesting you to define these new routines. Now is the time to do it. Take the example of DCMPLX with 2 arguments:
cc = DCMPLX(rr,ii)
This call builds a DOUBLE COMPLEX number with real part
rr and imaginary part
ii, and puts the result
into variable cc.The variables on which DCMPLX operates are rr, ii and cc. To consider only real numbers, let's split cc into its real and imaginary parts cc.r and cc.i. Since rr and ii may be used later, we consider them also as (unchanged) outputs of DCMPLX. Similarly cc can be seen as an (unused) input. All in all, DCMPLX operates from R4 to R4, with some conventional ordering e.g.: (rr ii cc.r cc.i)We can thus write the Jacobian matrix of DCMPLX, which is in this case simply: (1 0 0 0) (0 1 0 0) (1 0 0 0) (0 1 0 0)as the output cc.r receives exacty rr, the output cc.i receives exacty ii, and neither rr nor ii are modified. The tangent differentiation of DCMPLX just implements the matrix-times-vector product with this Jacobian, i.e. (rrd ) (1 0 0 0) (rrd ) (iid ) (0 1 0 0) (iid ) (ccd.r ) = (1 0 0 0) * (ccd.r ) (ccd.i ) (0 1 0 0) (ccd.i )hence the definition of DCMPLX_D you must provide:
function DCMPLX_D(rr, rrd, ii, iid, cc)
DOUBLE PRECISION rr,rrd,ii,iid
DOUBLE COMPLEX cc, DCMPLX_D
DCMPLX_D = DCMPLX(rrd,iid)
cc = DCMPLX(rr,ii)
end
The adjoint differentiation of DCMPLX just implements the matrix-times-vector product with the transposed Jacobian, i.e. (rrb ) (1 0 1 0) (rrb ) (iib ) (0 1 0 1) (iib ) (ccb.r ) = (0 0 0 0) * (ccb.r ) (ccb.i ) (0 0 0 0) (ccb.i )hence the definition of DCMPLX_B you must provide:
subroutine DCMPLX_B(rr, rrb, ii, iib, ccb)
IMPLICIT NONE
DOUBLE PRECISION rr,rrb,ii,iib
DOUBLE COMPLEX ccb
rrb = rrb + DBLE(ccb)
iib = iib + DIMAG(ccb)
ccb = DCMPLX(0.d0)
end
The above equation is a "vectorial" assignment in R4, which is done "in parallel". Therefore we were careful not to reinitialize ccb before it is used to update rrb and iib. Also, since this is adjoint mode, it is no use to return the value of cc |
Divisions by Zero and NaN's:

| The differentiated program returns NaN's in the derivative variables. However my original program is clean and doesn't return NaN's ! |
|
Differentiation can introduce NaN's in the resulting program, because
programs are sometimes non differentiable.
(You may take a look at the FAQ item about the
domain of validity of derivatives)
In general, AD is in trouble when it comes to
differentiating a function at places where it
is not differentiable. In this situation, we prefer that the differentiated program return NaN, which at least shows that something went wrong. This is probably better than silently returning a wrong derivative that may lead to other wrong results later. The end-used can then analyse the original program, and maybe transform it to make it differentiable. However there are situations where Tapenade can do a slightly better job, automatically. Non-differentiablility often occurs for SQRT, LOG, and exponentiations. For SQRT, we can do something a little more clever than returning NaN, but only in one particular case: Suppose instruction is
a = SQRT(b)
the tangent derivative instruction is
ad = bd/(2.0*SQRT(b))
that returns NaN if b
is 0.0.
But if bd
is also 0.0 at this time,
then we feel it is safe to return
ad = 0.0
On the other hand, we think it is not
safe to test on b or a being 0.0
or close to 0.0.
The reason is that in this case the derivative
is really undefined, so it's up to the user to
do something about it. It would be dangerous
to return ad = 0.0 automatically.
Only the user can do it by hand, and check
that it "works" for the current application.Something similar is done for exponentiations. Nothing is done for LOG. We should probably do the same as for SQRT! A similar reasoning applies to the reverse mode: Tapenade generates statements that detect special cases and build 0.0 derivatives. However there is a weakness in Tapenade when it comes to returning Nan's: unlike what happens in Adifor, there is no warning message printed at the moment when the Nan appears. Therefore, in the very frequent case where the compiled code does not stop when the Nan appears, computation goes on as usual and it is very difficult to find the place where the problem originated. We are aware of this problem and this should be improved. NaN's can also appear for a slightly different reason: for some given inputs, some functions have an output which is large, but not yet in overflow, whereas the derivative is definitely in overflow. Think of 1/x, whose derivative is -1/x**2. Again this requires a special treatment. One way could be to switch to double precision. We recently ran into a very annoying NaN problem related to uninitialized variables. Please avoid using uninitialized variables in your code! Tapenade tries to send messages about uninitialized variables, but this is a typical undecidable question, so we cannot find all of them. The following summarizes the problem that we found on a very large code...
subroutine TOP(x,y)
real x,y
real BAD(2)
call SUB(BAD,x)
y = BAD(1)
end
subroutine SUB(BAD,x)
real x, BAD(2)
BAD(1) = x*x
BAD(2) = BAD(2)*x
end
You can see that array BAD
is not initialized. BAD(2)
may very well contain something bad like a NaN.
However this has no visible impact on this original code
because the resulting x
and y
are not polluted by the NaN. Still this code is going to
silently compute on NaN's, and you don't want this.Now if we differentiate this code in reverse mode, the following statement appears in SUB_B:
xb = xb + bad(2)*badb(2)
and if the initial BAD(2)
is a dirty NaN, then this computes NaN times 0.0,
which on some compilers evaluates to NaN,
and the resulting xb gets
corrupted!
|
Domain of validity of derivatives:

|
What does AD compute when the fonction is non-differentiable? Why is the Tapenade derivative always 0.0 for my dichotomy-based resolution program? To what extent can I trust the derivatives computed by Tapenade? Is there a discontinuity in my function close to the current input? How can I evaluate the differentiability domain around my current input? Is there any support in Tapenade for non-differentiable functions? |
|
If the Tapenade-differentiated code is executed right on a non-differentiable
input, it may happen that the differentiated program returns NaN's as
derivatives. See the FAQ item about
divisions by Zero and NaN's. Also, although it is hard to admit :-), there may be bugs in Tapenade :-( Therefore, you probably need to go through the validation process to make sure that the derivatives are correct, compared to Divided Differences. Although inaccurate and expensive, Divided Differences are robust and therefore return good enough derivatives for validation. However, it may happen that the differentiated program just does not notice that it is on (or close to) a discontinuity. This is not a bug of Tapenade; this comes from the nature of AD. Think of a dichotomy-based program that solves f(x,y)=0, returning y as a function of x. The derivative of y with respect to x is in general nontrivial. Robust Divided Differences will actually return (an approximation of) the correct derivative. But AD will return 0.0, and this can be explained: the dichotomy-based program actually sees f as a piecewise constant function, where each piece is of the size of machine precision. In other words, for values of x that differ only very slightly, the value of y is exactly the same, and thus the derivative is rightly 0.0. Only this is not what you had in mind when you wrote the program! Apart from the immediate rule of thumb which is "don't even think of differentiating dichotomy-based code, don't even write it!", the conclusion is that programming often introduces discontinuities into perfectly differentiable mathematical functions, and AD can't do much about it (true, Divided Differences behave better on this question!). When this happens, the derivatives computed by AD are just useless or plainly wrong. All we can do is warn the user when this happens, which is when there is a non-differentiability "close" to the current point, i.e. around the current input values. In Tapenade, there is an experimental differentiation mode that evaluates the domain of differentiabillity around the current point, following a given direction in the input domain. The command-line argument -directValid builds a special differentiated program which, given a point in the input space and a direction in the input space (very much like in the tangent mode), evaluates the interval of differentiability along this direction. This uses subroutines called VALIDITY_DOMAIN_...(,), which are defined in the file validityTest.f provided in the AD First-Aid kit. After execution of the differentiated program, the interval of differentiability around the given input point and following the given direction is found in COMMON /validity_test_common/ gmin, gmax, infmin, infmax where gmin (resp. gmax) is the lower (resp. upper) bound, and if there is no constraint on the lower (resp. upper) bound, then infmin (resp. infmax) is .TRUE. Caution: If, inside the same execution, you need to perform several independent calls to the "directional validity" differentiated code, for different inputs or for different directions, then don't forget to reset the validity interval to ]-infinity,+infinity[ between two successive calls. Do this by resetting both infmin and infmax to .TRUE. Using this mode in every direction of the Cartesian basis of the input space will give a box inside which derivatives and derivatives-based optimization are valid. |
Differences with Odyssée :

| I have been using Odyssée in the past. What are the modifications from Odyssée to Tapenade ? |
|
Details on this question in the history of successive versions. TAPENADE is the successor of Odyssée. Therefore, TAPENADE is an incremental improvement to Odyssée. However, we had to radically modify the internal structures of Odyssée to take profit of compilation theory and technology. The internal representation of a program is now a Call Graph, with one node per subroutine, and each subroutine is now a Flow Graph, whose nodes are Basic Blocks. Each Basic Block is equipped with a Symbol Table, and Symbol Tables are nested to capture scoping. Basic Blocks contain instructions, represented as Abstract Syntax Trees. Static analyses now run on this structure, and this yields an enormous speedup in differentiation time, with respect to Odyssée. Right from the beginning, this internal representation of programs was designed independently from a particular imperative language, like Fortran or C. For example data structures are already managed. In principle, adaption to C, say, just requires a C front-end and back-end, i.e. a C parser and a C pretty-printer. About the differentiation model, TAPENADE is very similar to Odyssée. It still provides you with the forward and reverse mode of A.D. There are some improvements though:
|
Documentation, Archives of the Mailing list :

|
What is the available documentation on Automatic Differentiation
in general and specifically on Tapenade ? How can I take advantage of the experience from previous users of Tapenade ? |
|
Here is a short introduction to "what is AD?" The web site of the AD community www.autodiff.org contains the most comprehensive documentation on AD.About Tapenade, apart from the tutorial here, you should start by looking at the Tapenade user's manual. Another good starting point is our Documents page. If you subscribe to the tapenade-users mailing list, you will have access to the archive of previous discussions at the address: https://lists-sop.inria.fr/wws/arc/tapenade-users. |
PUSH/POP mechanism :

| Where are the PUSH and POP routines defined? |
|
We provide you with the source of these routines,
which comes as two files
adBuffer.f and adStack.c.
You may download it by clicking on
the "Download PUSH/POP" button in the differentiation result page, or else
In some cases, the compiler may complain that some required PUSH/POP procedure for some unusual type is not in adBuffer.f, e.g. _pushinteger16. In this case edit adBuffer.f and either uncomment the required procedure it if it is there or or create it as indicated (see inside file adBuffer.f). |
Original statements and results disappearing from the Reverse code :

|
My original procedure computed an output Y whereas the reverse differentiated procedure
does not return the correct value of Y. Some statements that are present in the original code have simply disappeared from the reverse code (this may even result in empty loops or if branches). |
|
By default, the reverse mode of Tapenade performs adjoint liveness analysis.
This means that some statements from the original program, which were "live" i.e.
which computed something needed by the rest of the program, are not live anymore
in the reverse differentiated program. This often occurs around the end of a forward sweep, just
before the backward sweep. The reason is: a priori the only result you expect from the reverse
differentiated program is the derivatives. In particular the original result of the original
program is not considered as a necessary result of the reverse
differentiated program. Therefore statements that are needed only for this original result
become dead code. More explanations and a small example are in the
TAPENADE 2.1 user's guide,
specifically on page 33-34. A command-line option can toggle this mechanism off,
see "-nooptim adjointliveness" on page 38. For users who want the reverse differentiated program to preserve the original outputs of the original program, we advise first to toggle adjoint liveness analysis off. However this is not enough, because the PUSH/POP mechanism will naturally destroy several of the program's original results. We advise that you modify the differentiated program by hand, saving the variables you want into temporary variables at the end of the forward sweep, and restoring these variables at the end of the backward sweep. |
Basic and extremely useful code preparation before differentiation

| I have a code I want to differentiate. How should I proceed? |
Just "Differentiating a code" does not mean much, unless you know precisely
which part of the code is the math function you want to differentiate,
which are its "independent" inputs and "dependent" outputs,
and maybe which differentiation mode you want to use.
We advise you to follow the steps below. This way you will be forced to answer
the above questions even before you start playing with Tapenade. These steps will also
vastly ease your life when it comes to validate the differentiated programs.
Finally, they will make our life easier if we are to help you, and if we have to
debug Tapenade!
THE FOLLOWING PREPARATION STEPS WERE USEFULFOR THE OLD VALIDATION METHOD ONLYWe keep them here for the record only, but they are not necessary!
|
Independent and Dependent sets. Initialization and usage of the differentiated parameters

|
What variables should I list in the "-vars" and "-outvars" arguments of
the differentiation command? (also known as the independent and dependent parameters) Also related: for my root procedure, the differentiated procedure that I obtain has additional differentiated parameters. What value should I give to these variables upon calling the differentiated root procedure? What value will I obtain in these variables upon return? |
|
To begin with, what we refer to as "parameters" here is not limited to
formal parameters in the procedure's argument list. The following applies
just as well to global parameters such as those in a COMMON or
in the variables of a USE'd module. If these variables are active,
differentiated variables will appear close to them, and if these active variables
are inputs or outputs, you will have to think about providing their
initial value and retrieving their result value.
Now some notation. Suppose we are calling Tapenade on some root procedure F, and call V some parameter of F. Although we advocate differentiating in a single batch all the subroutines that define the mathematical function of interest, it may help in the following explanation to think of F as only a part of this big mathematical function. Then in general there may be some upstream code U that runs before F is called, and some downstream code D that runs after F is called. The complete mathematical function is implemented by {U; F; D}. This code sequence has an input that we call the "main entry", and an output that we call the "main result". The general goal is to compute the derivatives of the main result with respect to the main entry. Assuming that U and D are also differentiated in some way, the questions are: should V go into the independent and dependent sets, what is the new parameter Vd (or Vb) of the differentiated F_D (or F_B), what value should be provided into it, and what value will it contain upon exit? The header of each differentiated root procedure answers some of these questions. However, some explanations may be useful. Question 1: What should the independent and dependent parameters be set as?It is wise not to think of those as the sets of inputs and outputs of F that are connected differentiably through F. Tapenade can find this by itself ! What Tapenade does expect from you is information about the context outside F. As a rule of thumb:
U(in:a,out:b); F(in:b,out:c); D(in:b,in:c,out:d);b is used differentiably later in D. Therefore, even if F only reads b and does not modify it, b must be in the set of dependents. Another possible mistake comes with loops. If the code looks like U(in:a,out:b); loop {F(in:b,out:c);} D(in:c,out:d);
the downstream code after F includes F itself, because of the loop,
and therefore b must be in the dependents.
Please notice that Tapenade may remove variables from the independents or dependents sets that you provided. Tapenade detects superfluous independents, i.e. inputs that have no differentiable influence on any dependent. Similarly a superfluous dependent is an output that doesn't depend differentiably on any independent and isn't independent itself. Superfluous variables are removed from their set, and a "command" message is issued. If you don't specify any independents (resp. dependents), Tapenade chooses for you the largest possible set, using its dependence analysis to remove superfluous elements. There are also situations where Tapenade adds variables to the independent or dependent sets that you provided for a root procedure. In tangent mode, if V is in the independent set and Vd may be overwritten in F_D, then V is added into the dependent set. Conversely in reverse mode, if V is in the dependent set and Vb may be overwritten in F_B, then V is added into the independent set. In both cases a "command" message is issued. This is Tapenade's way of warning the end-user that, although V was not in the dependent (resp. independent) set provided, the original Vd (resp. Vb) provided will be modified by the derivative procedure, and therefore will loose its original value and should be used with care after that. Question 2: What is the effect of a variable being in the independent and/or dependent sets, on the required initialization and returned value of its derivative?Whatever independents and dependents sets you provided, it is wise to check these sets modified and actually used by Tapenade, as listed in the comment header of the differentiated procedure. These sets determine how the derivatives must be initialized and what they will contain upon exit. This information is also summarized in the comment header of every differentiated root procedure. The following table shows and explains the possible cases. In tangent mode, things are straightforward. In reverse mode things prove a little more surprising, but they are "logical" consequences of the transposition of the Jacobian matrix that is behind the reverse mode. To give a rough idea, in reverse mode the flow of data goes in the reverse direction: If V is an output of F, then F_B must be called with an additional input VB, and conversely if V is an input of F, then F_B will return an additional output VB. When a variable is purely read by F, its derivative is purely incremented (and vice versa)... oh yes, you're right it's complicated! But we never said that the reverse mode is simple, it's only extremely efficient and worth the effort!
Example: consider for instance procedure F below, that should illustrate many of the possible situations.
subroutine F(u2,v1,v3,w2)
real u1,u2,u3,v1,v2,v3,w1,w2,w3
common /cc/u1,u3,v2,w1,w3
c
u3 = u3+v1*u1
u3 = u3-u2*v2
u2 = 3.0*u1*v1
u3 = u3+G(u2)
c
v3 = v3+2.0*v1
v3 = v3-v2
v2 = 3.0*v1
v3 = v3+G(v2)
c
w3 = w3+v1*v2
w3 = w3-w2*u1
w2 = 3.0*w1
w3 = w3+G(w2)
end
real function G(x)
real x
G = x*x
end
Function G is some pure user-defined function.
We built this code so that all *1 parameters are only read
by F, *2 parameters are read, then overwritten and maybe read again,
and *3 parameters are only incremented, which is an interesting case for
reverse differentiation. Parameters are scattered as formal arguments or global variables,
just to show it doesn't matter.
Suppose that the end-user decided that all v* and w* parameters are independents, and all u* and w* are dependents, e.g. using the command-line option:
-head "F(v1 v2 v3 w1 w2 w3)>(u1 u2 u3 w1 w2 w3)"
|
Validation of the differentiated programs

|
Help, the differentiated program crashes! How can I be sure that the differentiated program really computes what I want? How can I check the derivatives and find the place where the differentiated program goes wrong? |
|
This is a difficult issue. Differentiated programs are very long and
complex and you may find it hard to check them by hand, even if
we try to keep them readable.
Here is a proposed method to validate/debug the code produced by Tapenade.
It uses Tapenade options that are available only in the command-line version
(local installation).
It also uses two files that are in the ADFirstAidKid directory of your
local installed Tapenade files.
If you don't have a locally installed Tapenade, please follow
these steps.
Also, to ease our validation mechanism, we strongly advise you
to follow a few preparation steps, before you even start to differentiate
anything.
Validation is by no means easy. Although we try to automate the process, it still requires
good knowledge of the principles of AD, especially for the adjoint mode.
The sad thing with Tapenade's validation mechanism is that you may run into bugs
of Tapenade itself (otherwise you wouldn't be reading this section anyway!) but
you may also run into bugs of the validation mechanism itself.
In the following, words between double quotes and drawn in this color (such as "SHAPE") are indeed templates, that must be instantiated by actual strings depending on the context. Square brackets represent additional arguments, and the star represents repetition. In the example pieces of programs, actual pieces of code are written in this sort of dark red, short strings in double quotes are "patterns" as above, and "entiere lines between double quotes and drawn in the pattern color"describe a piece of code that you must write at that place. The choice of Independent and Dependent parameters may interfere with the validation process. This is explained at the end of this section, but we'd rather warn you right now. Make sure you read this part if validation fails close to the entry-into or exit-from your code. CHECK THE SIZES OF PRIMITIVE TYPES:If the program crashes, there is a small chance that this comes from the sizes of basic types on your system. To test these sizes, go into the ADFirstAidKid directory, then:
if testMemSize on your system gives different sizes, use the type size options that change the default sizes to what your system expects. CHECK THE TANGENT DERIVATIVES WITH RESPECT TO DIVIDED DIFFERENCES:Comparison with divided differences is the standard way to test the tangent derivatives. It amounts to running your original subroutine P with a perturbed input X+ *Xd, then with
X, then computing:![]() You should get nearly the same result as when running the tangent differentiated subroutine P_d(X,Xd,Y) With a "good" epsilon, differences should be only on a few last decimals. There is a lot to say on the choice of a good epsilon. It depends on the algorithm, the data ... and maybe also the phase of the moon! Also, it is certainly more accurate to compute the centered divided differences, but we generally don't need this degree of accuracy for our validation. To run the test, start from the prepared code according to the recommended code preparation. Suppose the "central" part looks like:
... whatever code is before ...
C Beginning of the procedure to differentiate
CALL MYFUNC(x1, x2,..., y1, y2,...)
C End of the procedure to differentiate
... whatever code comes after ...
Define two copies of your complete code, that will differ only
in this "central" part.
$> exeDD1 | exeDD2 using a UNIX pipe "|" (or equivalent). What happens behind the scene is: exeDD1 runs on the input "plus epsilon times direction". At selected moments ("points" in execution), it sends the value of selected variables into std-out, therefore into the pipe. In parallel, exeDD2 runs on the input "without epsilon". At the same selected points, it reads the value of the same selected variables from std-in, therefore from the pipe. Using its own value of the same variable, exeDD2 computes de derivative approximation by divided differences (the "DD derivative") and compares it with the analytical derivative. If the DD derivative is almost zero (i.e. absolute value below the given "almost-zero"), the comparison is not done because Tapenade code sometimes doesn't reinitialize zero derivatives (to save time). Otherwise, if the percentage of difference is more that the "threshold", a message is printed on the screen. What you obtain on the screen looks like what follows. In this example, we introduced an error into the tangent differentiated code, inside the tangent derivative of some function called "POLYSURF":
$> exeDD1 | exeDD2
Starting TGT test, epsilon=0.1E-07, zero=0.1E-04, errmax=10.0%
===========================================================
AT:entry OF MYFUNC
AT:entry OF POLYPERIM
AT:middle OF POLYPERIM
AT:entry OF INCRSQRT
AT:exit OF INCRSQRT
AT:entry OF INCRSQRT
AT:exit OF INCRSQRT
AT:exit OF POLYPERIM
AT:entry OF POLYPERIM
AT:middle OF POLYPERIM
... lots of lines deleted here ...
AT:exit OF POLYPERIM
AT:middle OF MYFUNC
AT:entry OF POLYSURF
AT:middle OF POLYSURF
pp: -0.1000000000000000E+01 (ad) 90.0% DIFF WITH (dd) -0.1000000011686097E+02
AT:entry OF INCRSQRT
pp: 0.3001997501343353E+01 (ad)150.0% DIFF WITH (dd) -0.5998002627904953E+01
AT:exit OF INCRSQRT
polysurf: 0.3001997501343353E+01 (ad)150.0% DIFF WITH (dd) -0.5998002627904953E+01
AT:exit OF POLYSURF
y2: 0.3787447771453199E+13 (ad) 92.3% DIFF WITH (dd) 0.2901041015625000E+12
AT:exit OF MYFUNC
Final result y2: 0.3787447771453199E+13 (ad) 92.34038% DIFF WITH (dd) 0.2901041015625000E+12
===========================================================
From this listing, you can deduce that DD and AD derivatives start to diverge between the "middle" point of subroutine POLYSURF and the following call to subroutine INCRSQRT, and the faulty derivative is that of variable pp. The error in the differentiated code is probably nearby. Now you can start to inspect the code from that point, identify the bug, and maybe send us a bug report ! It is clear what the "entry" and "exit" debug points of a procedure are. The "middle" debug point is placed automatically by Tapenade debug mode. You can easily find it in the differentiated code. For more accuracy, you can add debug points by placing $AD DEBUG-HERE directives in your original source. For instance:
IF (pp.EQ.0) pp=ns
C$AD DEBUG-HERE CP4 cp.eq.4
dx = X(cp)-X(pp)
defines a new debug point named "CP4". The second argument (optional, default is true) of the directive is a test (with no white space in it, please), and debug point is active only when the test is true. If no test is given, the debug point is always active. There can be a third argument (optional, default is false) that, when true, forces the debug point to be active even if debug of the enclosing procedure is not active (see below). By default, debugging occurs on every "active" variable at every debug point of every subroutine called. You can control debugging for the complete call tree below a particular subroutine call, by placing the $AD DEBUG-CALL directive in your original source before this particular call. For instance:
C$AD DEBUG-CALL false
perim = perim+ i*POLYPERIM(X,Y,ns)
turns debugging off for the call tree below this function call. The false can be replaced by something more elaborate (no white space in the test please!). When debugging is turned off inside a particular sub tree of the call tree, no test is made and no message is printed for this sub call tree. However, you have the possibility to force debugging inside this sub call tree by adding a third argument to some $AD DEBUG-HERE or $AD DEBUG-CALL inside. This third argument must be a test. When it evaluates to true, debugging is temporarily turned on again for the debug point or inside the call in question. CHECK THE REVERSE DERIVATIVES USING THE DOT-PRODUCT TEST:The dot-product test relies on the fact that for every program, procedure, or part P of a procedure, for every vector Xd in the space of the inputs of P and for every vector Yb in the space of the outputs of P, the following scalar:We assume that the tangent-differentiated code is correct, for instance because it was validated by the divided-differences method. We propose a method to validate the reverse differentiated program P_B by checking that this dot-product equality holds and if it doesn't, to locate the part of the reverse differentiated program where the equality doesn't hold. Suppose again that the "central" part looks like:
... whatever code is before ...
C Beginning of the procedure to differentiate
CALL MYFUNC(x1, x2,..., y1, y2,...)
C End of the procedure to differentiate
... whatever code comes after ...
Define again two copies of your complete code, that will differ only
in this "central" part.
... whatever code is before ...
Now initialize the TANGENT differentiated variables, e.g.:
(this step is NOT absolutely necessary for debug !)
x1d = 1.d0
DO i=1,ns
x2d(i) = 1.d0
ENDDO
y1d = 0.d0
DO i=1,20
y2d(i) = 0.d0
ENDDO
...
Now prepare for the test:
CALL DEBUG_FWD_INIT(1.d-5, 0.1d0, 0.87d0)
CALL DEBUG_FWD_CALL('MYFUNC')
C Beginning of differentiated part
call MYFUNC_D(x1, x1d, x2, x2d, ..., y1, y1d, y2, y2d, ...)
C End of differentiated part
Now terminate the test:
CALL DEBUG_FWD_EXIT()
CALL DEBUG_FWD_CONCLUDE()
... whatever code comes after ...
$> exeBB1 | exeBB2 using a UNIX pipe "|" (or equivalent). What happens behind the scene is: exeBB1 calls MYFUNC_B, which is conceptually split into a sequence of code pieces between successive points. At the beginning of each (reverse differentiated) piece (actually it's what we call its downstream end, because control flow is reversed...), pseudo-random values (using "seed") are assigned to the current reverse differentiated variables Yb. At the end of the same piece (actually it's what we call its upstream end, because control flow is reversed...), the current reverse differentiated variables Xb are multiplied by dot-product with a randomly-filled Xd. The result is sent into std-out, therefore into the pipe. In parallel, exeBB2 reads from std-in, therefore from the pipe, and reverses the sequence of incoming dot-products. exeBB2 then calls MYFUNC_D, which is split in a way that matches the splitting of MYFUNC_B. At the beginning of each (tangent differentiated) piece, i.e. the upstream end of this piece, the same random values are assigned to the current tangent differentiated variables Xd. At the end of the same piece, i.e. the downstream end of this piece, the current tangent differentiated variables Yd are multiplied by dot-product with the same randomly-filled Yb. The dot-product is compared with the one coming from exeBB1. If the two dot-products are almost zero (i.e. absolute value below the given "almost-zero"), no comparison is done. Otherwise, if the percentage of difference is more that the "threshold", a message is printed on the screen. What you obtain on the screen looks like what follows. In this example, we introduced an error into the reverse differentiated code, inside the reverse derivative of some function called "POLYSURF":
$> exeBB1 | exeBB2
Starting ADJ test, zero=0.1E-04, errmax=10.0%, random_incr=0.87E+00
===========================================================
AT:entry OF MYFUNC
AT:entry OF POLYPERIM
AT:middle OF POLYPERIM
... lots of lines deleted here ...
AT:exit OF POLYPERIM
AT:middle OF MYFUNC
AT:entry OF POLYSURF
AT:middle OF POLYSURF
49.0% DIFFERENCE!! fwd: 0.1156790000000001E+02 bwd: 0.2268820000000001E+02
AT:entry OF INCRSQRT
AT:exit OF INCRSQRT
AT:exit OF POLYSURF
AT:exit OF MYFUNC
End of ADJ test. 1 error(s) found. WARNING: testing alters derivatives!
===========================================================
From this listing, you can deduce that there is an error in the reverse differentiation of the piece of code between the "middle" point of subroutine POLYSURF and the following call to subroutine INCRSQRT. Now you can start to inspect the code from that point, identify the bug, and maybe send us a bug report ! As for the divided-differences test, the "entry" and "exit" debug points are clear enough, and the "middle" debug point is placed arbitrarily by Tapenade. For more accuracy, you can add debug points by placing the same $AD DEBUG-HERE directives in your original source. You can also place $AD DEBUG-CALL directives in your original source to control debugging more finely inside the call tree. Notice however that the third argument of these directives is simply ignored by the dot-product test. In other words, if debugging is turned off for a given sub call tree, there is no way to turn it on locally inside the sub call tree. Notice finally that it is delicate and dangerous to try and add new debug points by hand into the reverse differentiated code. On the other hand, it is rather easy and safe to turn off/on existing debug points inside the reverse differentiated code, without needing to re-differentiate with different $AD DEBUG-**** directives. This may prove handy as running Tapenade on a big code can be long. To do so, you just need to edit the reverse-differentiated code. In it you will find the debug points by their names.
PROBLEMATIC INTERACTION WITH INDEPENDENT AND DEPENDENT PARAMETER SETS:This problem was identified and studied with the help of Nicolas Huneeus, LSCE-IPSL, University of Versailles Saint-Quentin.Tapenade sometimes takes the liberty to modify the Independent and Dependent parameters provided by the end-user. The reasons are described here and, although they are complex, we believe they are justified. This modification can be different in tangent and adjoint modes.
The sign you must look for is the occurrence of one of the messages: Command: Input variable(s) xxxx have no differentiable influence in pppp: removed from independents Command: Output variable(s) xxxx are not influenced differentiably in pppp: removed from dependents Command: Input variable(s) xxxx have their derivative modified in pppp: added to independents Command: Output variable(s) xxxx have their derivative modified in pppp: added to dependents when setting the message level high enough (command-line option -msglevel 20). There are two ways to turn around this problem:
|
Problems with web browsers (Macintosh):

| I'm using Internet Explorer for Macintosh, and Tapenade complains about syntax errors in my Fortran files. However I'm sure the syntax is correct, and/or it works fine through the cut-and-paste interface. |
This bug was found and fixed by David Pearson, University of Reading U.K.
The reason might be that the Fortran files that you are uploading to us are
sent as "application/x-macbinary" instead of "application/octet-stream".
This can be fixed by modifying your browser's preferences:
|
Other AD tools:

| What are the other Automatic Differentiation tools ? How do they compare to Tapenade ? |
This is only our partial vision of the other AD tools.
Maybe a better source is the
www.autodiff.org
site for the Automatic Differentiation community, managed by our
colleagues in Aachen and Argonne.
There are AD tools that rely on program overloading rather than program transformation. In general this makes the tool easier to implement. However some overloading-based AD tools can become very sophisticated and efficient, and represent a fair bit of hard work too. Overloading-based AD tools exist only for target languages that permit some form of overloading, e.g. C++ and Fortran95. Overloading-based AD-tools are particularly adapted for differentiations that are mostly local to each statement, i.e. no fancy control flow rescheduling is allowed. On the other hand, these local transformations can be very complex, more than what transformation-based AD tools generally provide. For instance, overloading-based AD-tools can generally compute not only first, but also second, third derivatives and so on, as well as Taylor expansions or interval arithmetic. Adol-C, from TU Dresden, is an excellent example of overloading-based AD tool. Also FADBAD/TADIFF There are AD tools that transform the original source into a differentiated source. Tapenade is one of those. These tools share their general architecture, with a front-end very much like a compiler, followed with an analysis component, a differentiation component, and finally a back-end that regenerates the differentiated source. They differ in particular in the language that they recognize and differentiate, the AD modes that they provide, and some differences in AD strategies, mostly about the reverse mode. Others transformation-based AD tools are:
There are AD tools that directly interface to an existing compiler. In fact, these are extensions to the compiler so that differentiated code is added at compile time. For instance the NAGWare Fortran95 compiler has AD facilities inside, that are triggered by user directives in the Fortran source. It so far provides tangent-mode differentiation. There are AD tools that target higher-level languages, such as MATLAB. We know of ADiMat, MAD, and INTLAB. Even when they rely on operator overloading, they may embed a fair bit of program analysis to produce efficient differentiated code. |
Directory separator linux/windows:

| I need to use "\" as a separator in my paths, and Tapenade uses the UNIX "/" separator. |
This happens when your files are stored in a WINDOWS (DOS) file system
and you refer to them from a linux system such as cygwin.
Use the "-parserfileseparator" option on the Tapenade command
line to modify the path separator. For instance:
tapenade ... -parserfileseparator "\" ...or tapenade ... -parserfileseparator "/" ... |
JAVA errors: NullPointerException, ArrayOutOfBoundsException, etc:

|
What does it mean when I get System: java.lang.NullPointerException as a message, or similarly an ArrayOutOfBoundsException |
| It just means you ran into one of the remaining bugs in Tapenade. Please send us a bug report, and we will fix it. |
Differentiating multiple top/head procedures, Differentiating Libraries:

|
I want to differentiate many top/head procedures I want to differentiate a top/headprocedure for different activity contexts I want to force the activity context of a given procedure to something larger than Tapenade's choice I want to differentiate every entry of a library |
|
The recent versions of Tapenade allow you to differentiate
several top/head procedures at the same time. This is convenient for differentiating libraries.
The advantage is the following: when two head procedures call
the same internal procedure, the internal procedure is differentiated
only once, with an activity context which is the union of the contexts coming
from each head procedure.
In the older versions of Tapenade, where only one head procedure
could be differentiated, users had to write a dummy top procedure
that called every entry of the library, arranging for the
desired activity context for each call. This could prove tedious. To avoid this tedious task, the syntax of the command line has changed as follows (the old syntax still works, though): The -head option now accepts a string between double quotes, in which you may specify any number of triplets:
tapenade foo.f bar.f toto.f ... -head "f1(y1 y3)/(x2) f2(b2 b4)/(a1 a2 a3)"will differentiate head subroutine f1, differentiating outputs y1 and y3 with respect to x2 and at the same time head subroutine f2 will be differentiated for dependent outputs b2 and b4 with respect to independent inputs a1, a2, and a3. You may split these head subroutines into several -head options. If you use this syntax, the options -outvars and -vars become useless. There is an alternative syntax for people who want to say it the other way around, i.e. for instance -head "f1(x2)>(y1 y3)"In Fortran90, you may want to designate a subroutine or a variable name from a given module. You may do so using a % or a . character, equivalently. For instance: -head "m5%f1(y1 y3 m2%v2%c4)/(x2)"designates the procedure named f1 in module m5, and in the list of dependent outputs, you can see the component c4 of the global variable v2 from module m2. Of course we assume that v2 has public access and it is of a structured type with a field named c4. Finally, it happens that Tapenade finds that some differentiation head procedure actually has a differentiation context smaller than the one given by the user. Suppose for instance that you specify -head "m5.f1(y1 y3 m2.v2)/(x2 x4)"and actually no dependent output depends on x4, and at the same time y3 actually depends on no independent input. Tapenade will "simplify" the command-line options, neglecting variables x4 and y3. This is ok in general, but annoying sometimes. Notice that a message is displayed to explain the situation, but the differentiated subroutine will lack the differentiated arguments for x4 and y3. The command-line option -fixinterface will force the differentiated arguments to be present. Hopefully they are useless, but they will be there. This is useful when feeding programs produced by Tapenade into optimization platforms that require a fixed interface. |
Download Tapenade

| Where can I download a local version of Tapenade? |
| You may download the latest version of Tapenade on your local system. First read our downloading policy, and register. You will get a link to a README.html file. |
Old versions of Tapenade

| How can I get a previous version of Tapenade? |
|
Well, to put it bluntly, you can't ! Sorry but keeping several versions available is a lot of work. More importantly, it contradicts our belief (hope, dream, ?) that successive versions of Tapenade only add new functionalities, improve existing ones, and fix bugs. In the vast majority of cases, this is justified. However, it did happen in the past that switching to a new version posed a problem to a user. For instance when we introduced the PUSHCONTROL() in version 3.5, or the automatic modification of the user-provided independent and dependent sets in version 2.2. In these cases, the problems could be solved by a small change in the end-user's environment, or a new command-line option. We think it was for the better. So if you find yourself in this situation, we encourage you to analyze the problem and tell us which is the difference in the differentiated program that poses a problem. A small standalone source file would be great. Hopefully we can find a way so that you can use the new Tapenade and benefit from next improvements. |
System errors of the "not found" sort:

|
Something went wrong after installation: I get a Exception in thread "main" java.lang.NoClassDefFoundError: toplevel/Differentiator or a message Tool: Parser not found |
|
Check your environment variable (e.g. shell variable) TAPENADE_HOME.
This variable is used in file bin/tapenade (for the "LINUX" case) and in file bin/tapenade.bat (for the "WINDOWS" case). Also when switching to a new version of Tapenade, don't forget to update your TAPENADE_HOME variable to refer to the new version, e.g. TAPENADE_HOME="install_dir"/tapenade3.5 See README.html for more details. |
System errors of the "out of memory" sort:

|
I get a Exception in thread "main" java.lang.OutOfMemoryError The Tapenade server complains my source file is too large! |
|
You are probably differentiating a large program!
The Tapenade server will reject your files if the total size of files submitted is over 300000 characters, as many users can access the server simultaneously and too large files make is swap and slow down, or even crash. If you run into this limit, you must probably download a Tapenade executable on your local system. It is free for research non-commercial use. By the way, it still happens that the server crashes because of too many users differentiating too large files. In this case the server should restart automatically, so please wait for a moment and retry. For a locally installed Tapenade, if the java heap overflows (java.lang.OutOfMemoryError), increase the heap size in the launching script file bin/tapenade (for the "LINUX" case) or in file bin/tapenade.bat (for the "WINDOWS" case). Set HEAP_SIZE=-mx2048m or even HEAP_SIZE=-mx4096m |
Sending remarks, questions, and bug reports:

|
Where should I submit questions about Tapenade ? How do I send bug reports to the Tapenade development team ? |
|
Bug reports should be sent to the developers of Tapenade at this e-mail address: tapenade@lists-sop.inria.fr ![]() On the other hand, questions and remarks should rather be sent to the tapenade-users mailing list. You will have to subscribe
first, then you will be able to submit your questions and remarks
by mailing to
tapenade-users@lists-sop.inria.fr.
Maybe some other user of Tapenade will answer even before we do!
Naturally, frequently asked questions should eventually be included here.
If you really don't want to subscribe, then you may just send an e-mail to the developers of Tapenade ![]() More specifically about bugs, there are some data that we need in order to track the bug efficiently. If the error you got contained the Java execution stack, please forward it to us. Please send us the exact differentiation command that you typed with all its command-line arguments, and the log text that Tapenade produced before it crashes. If you can send us (a reduced version of) the file that causes the bug, that would be just great! If you were using the on-line Tapenade, then you got an "Error" web page. At the end is a useful link (for us developpers): "If everything seems correct, it is probably an internal bug. In that case, please send us this bug report, without modifying the contents of the message. We will examine it promptly.". Please send us this bug report using the button "send us". It will send us the session reference so that we can find its track in our very large local log. Most important for that are the session number and the date/time of the session, for example: Session:ac3eQiTVfzwb, Time:Wed Mar 29 07:59:01 MEST 2006 |
JAVA Version required:

|
I installed the new version of Tapenade and Java now complains. What minimal version of Java jdk is required? |
| A locally installed version of Tapenade requires a reasonably recent version of Java. See the installation README for details. For instance, as of Jan 2008, it requires Java jdk 1.6. |
Wrong external's:

|
My differentiated program does not compile because it contains a external REAL declaration. |
| Tapenade doesn't well recognize the REAL Fortran intrinsic, and therefore considers it as an external. This may also happen for a couple others intrinsics. This bug has still not been fixed. The workaround is simple: just delete the faulty declaration. |
Controlling checkpointing in the reverse mode:

|
My reverse-differentiated program is too slow, and I think it comes from
checkpointing. How can I tell Tapenade to refrain from checkpointing each and every procedure call ? How can I tell Tapenade to use checkpointing on a given piece of code ? |
|
There is a new directive that lets the end-user specify that some
procedure calls must not be checkpointed. If used wisely, this
functionnality may produce a reverse differentiated code that is
far more efficient, sometimes at the cost of an extra memory use.
There are three ways to specify places where checkpointing must not be done.
|
Independent Iterations Loops (
|
What is the |
|
The II-LOOP directive ( A loop can be declared II-LOOP if it has no loop-carried "true" or "flow" data-dependence, i.e. if no loop iteration depends on a value that is computed by another iteration. The following loop is an II-LOOP:
C$AD II-LOOP
DO i=1,N,2
A(i) = 2.0*z
A(i+1) = 3.0*z
tmp1 = B(T(i))
tmp2 = B(K(i))
vv = SQRT(tmp1*tmp2)
C(i) = C(i)+vv
ENDDO
because, as you can check, there is no loop-carried true dependence.
This implies that the iteration order can be freely shuffled.
On the other hand, the following loop is not an II-LOOP:
DO i=3,N-2
A(i) = A(i-2)
B(i) = B(i+2)
prod = prod*C(i)
ENDDO
for at least three reasons:
C$AD II-LOOP
DO i=1,N,2
tmp1 = B(T(i))
tmp2 = B(K(i))
vv = SQRT(tmp1*tmp2)
sum = sum+vv
ENDDO
Because the loop carried dependence on If you add the directive More explanations in the paper on Adjoining Independent Computations |
User-defined additional checkpoints (
|
How can I add reverse-mode checkpoints into my source code? How can I force checkpointing of a given piece of code? |
|
Just place Tapenade directives Adding a user-defined checkpoint around a piec of code P is applicable when P could indeed be extracted as a separate procedure. In the previous versions of Tapenade, you had to actually make P a procedure to trigger checkpointing on it. These directives achieve the same effect without forcing you to make this error-prone transformation.
if (x.gt.10.0) then
C$AD CHECKPOINT-START
x = x*y
if (x.gt.0.0) x =-x
C$AD CHECKPOINT-START
y = y - x*x
y = y*y
C$AD CHECKPOINT-END
x = x + sin(y)
C$AD CHECKPOINT-END
continue
...
endif
It contains two nested checkpoints, both located inside the
"true" branch of a conditional.
The generated code shows the effect of these additional checkpoints.
In the forward sweep:
IF (x .GT. 10.0) THEN
CALL PUSHREAL4(x)
CALL PUSHREAL4(y)
x = x*y
IF (x .GT. 0.0) x = -x
y = y - x*x
y = y*y
x = x + SIN(y)
...
CALL PUSHCONTROL1B(1)
ELSE
CALL PUSHCONTROL1B(0)
END IF
and in the reverse sweep:
CALL POPCONTROL1B(branch)
IF (branch .NE. 0) THEN
...
CALL POPREAL4(y)
CALL POPREAL4(x)
CALL PUSHREAL4(x)
x = x*y
IF (x .GT. 0.0) THEN
x = -x
CALL PUSHCONTROL1B(1)
ELSE
CALL PUSHCONTROL1B(0)
END IF
CALL PUSHREAL4(y)
y = y - x*x
y = y*y
yb = yb + COS(y)*xb
CALL LOOKREAL4(y)
y = y - x*x
yb = 2*y*yb
xb = xb - 2*x*yb
CALL POPREAL4(y)
CALL POPCONTROL1B(branch)
IF (branch .NE. 0) xb = -xb
CALL POPREAL4(x)
yb = yb + x*xb
xb = y*xb
END IF
|
Binomial Checkpointing (
|
My time-stepping loop fills up the memory space during the forward sweep,
and this makes my adjoint code crash. What is the |
|
Binomial checkpointing is the optimal memory/runtime tradeoff
for iterative loops, i.e. loops where the computation of iteration
n depends on the results of previous iterations, and therefore
there is no hope of making these iterations independent and/or run them
in parallel.
A typical example is the time-stepping loop in an instationnary
simulation. Assuming that the final number of iterations N is known, and assuming that each iteration has the same runtime cost, then A. Griewank has given a checkpointing strategy for the iterative loop, which is optimal both in terms of memory used to store the checkpoints, and in terms of runtime i.e. the number of times a given iteration is repeated. A.Griewank has shown that this so-called "binomial" strategy has a cost in memory and a cost in execution time that grow like log(N). More precisely
C$AD BINOMIAL-CKP nstp-stp1+2 4 stp1
do stp=stp1,nstp
x = x+y
y = y-x*x
v3 = 1.0
do i=3,98
j = i-stp/10
v1 = A(j)*B(i)
v2 = B(j)/A(i+1)+v3
A(i) = 2*A(i)+sin(C(j))
B(i-1) = B(i)*B(i+2) - A(i+1)*A(i-1)
A(i+1) = A(i)+v1*v2
B(i-1) = B(i)+v2*v3
v3 = v3+v2-v1
enddo
A(j) = A(i)*B(j)
enddo
The outer loop is a good candidate for binomial checkpointing.
Its iterations are definitely not independent,
so the II-LOOP directive is out of question.
Also, the number of iterations, although dynamic,
is known before the loop starts.The inner loop could also be applied binomial checkpointing, but this would be less profitable as it is a smaller piece of code. The three values provided after the BINOMIAL-CKP directive are
If you look at the produced reverse code, you will find that the adjoint code is in fact a driver, initialized with the parameters of the BINOMIAL-CKP directive, and that will trigger the correct sequence of actions to compute the adjoint of the iterative loop. These actions are:
|
Linear Solvers:

|
My original code solves a linear system and the differentiated code
for this part of the code is slow. My original code uses an iterative linear solver and the differentiated code produces derivatives whose accuracy gets poorer and poorer as the differentiated linear solver is used. |
|
Linear solvers are often sophisticated and do not lend themselves to
Automatic Differentiation efficiently. Even if you have access to the
source of the linear solver, it may be wiser not to use the AD-generated
code for this solver, and write a better one by hand. Moreover, when the linear solver is iterative, it often happens that the number of iterations required to fully converge the solution y of Ay = b is not enough to converge its derivative yd or yb. Since AD uses the same control as the original program, it stops iteration when the original program does. Therefore yd or yb may not have reached convergence when y has! Of course if you use a solver from a library and you don't have access to the source, then you are forced to write the differentiated solver by hand. You have no choice. It will just comfort you to learn that this is the best thing to do anyway! Just refer to the FAQ item about Black-box procedures to tell Tapenade about this library call (We would advise you to apply the dummy procedure method), and come back here for hints about how to write this hand-coded differentiated linear solver. Assume that the code to be differentiated has first built a matrix A and a right-hand side vector b, that may be both active. Size of A is n*n and size of b is n. Assume that some solver is called, e.g.
call SOLVE(A,y,b,n)
that computes the y
(of size n) such that:![]() y is also active. In tangent mode, we can differentiate the above equation by hand and we obtain: ![]() so that we find yd is: ![]() Therefore, since Tapenade probably built a tangent differentiated call looking like:
call SOLVE_D(A,Ad,y,yd,b,bd,n)
you can write your hand-coded SOLVE_D along the lines of:
subroutine SOLVE_D(A,Ad,y,yd,b,bd,n)
INTEGER n
REAL A(n,n), Ad(n,n)
REAL y(n), yd(n), b(n), bd(n)
INTEGER i,j
REAL RHSd(n)
call SOLVE(A,y,b,n)
DO i=1,n
RHSd(i) = bd(i)
DO j=1,n
RHSd(i) = RHSd(i) - Ad(i,j)*y(j)
ENDDO
ENDDO
call SOLVE(A,Yd,RHSd,n)
END
In reverse mode, it's no surprise things get a little harder! The inputs of the linear solver call are A and b, and the output is y. By definition of the adjoint variables Ab, bb, and yb, we have the dot products equality (dot product noted as (_|_)) for any Ad and bd : ![]() Beware that these dot products are element-wise: the dot product of Ad and Ab is the sum of all Ad(i,j)*Ab(i,j) for all i and j. Replacing yd by its above definition, we get: ![]() for any Ad and bd. Therefore: ![]() The first equation gives directly bb as: ![]() The second equation gives Ab but this is less obvious: we must go down to the indices level: ![]() and we get the value of each element of Ab. Finally, since Tapenade probably built a reverse differentiated call looking like:
call SOLVE_B(A,Ab,y,yb,b,bb,n)
you can write your hand-coded SOLVE_B along the lines of:
subroutine SOLVE_B(A,Ab,y,yb,b,bb,n)
INTEGER n
REAL A(n,n), Ab(n,n)
REAL y(n), yb(n), b(n), bb(n)
INTEGER i,j
REAL AT(n,n), incrbb(n)
DO i=1,n
DO j=1,n
AT(i,j) = A(j,i)
ENDDO
ENDDO
call SOLVE(AT, incrbb, yb, n)
DO j=1,n
bb(j) = bb(j) + incrbb(j)
ENDDO
call SOLVE(A,y,b,n)
DO i=1,n
DO j=1,n
Ab(i,j) = Ab(i,j) - y(j)*incrbb(i)
ENDDO
ENDDO
END
Two remarks:
|
Multiple sessions in parallel:

| The differentiated code contains parts that do not correspond to anything in my original code. |
| Maybe you have two web Tapenade sessions active at the same time. When using the Tapenade server on the web, there can be only one differentiation page opened at a time. Do not try to open two different web browser pages, and call one Tapenade in each, because your Tapenade session is unique: therefore the files you upload would all go into the same directory here, causing strange behavior. |
Approximative source-code correspondence:

| You claim that the graphical interface shows the correspondence between source and differentiated code. I can't see it! |
|
This source-code correspondence is implemented through html
links and anchors, so its behavior is approximate.
All it guarantees is that the target of the link is somewhere
visible on the page, but not necessarily in front of the source.
Moreover there are situations where source-code correspondence is lost, for example on declarations. So in this case, the best target we can show is the enclosing generated subroutine. |
Too many Warning and Error messages:

| Help! Tapenade sends me plenty of error messages ! |
|
Yes Tapenade sends plenty of error messages!
One may think there are too many of them.
This is because we believe these messages can be very helpful in
the case when you obtain wrong differentiated values.
Sometimes one of these messages is an indication of
a real problem for differentiation, so it can save you
a lot of time to look at them. However, maybe you should not try too hard to get rid of all these messages. After all many of them are purely warnings. Very often, Tapenade will produce a correct differentiated code, even if it has complained. We strongly advise you to look at the detailed discussion on messages which indicates, for each message, whether it is really important (or not), and why, especially from the AD point of view. Tapenade lets you define in the command line an error level threshold, below which errors are not shown any more. Finally, to summarize, please do not overlook Tapenade errors because they potentially indicate hard differentiation problems, and at the same time do not be too demanding on the source program: some warnings are there to stay! |
Includes and Comments:

|
What a mess! the nice include files of my original program have all been
expanded in the differentiated program ! What a mess! all my nice comments around declarations have been displaced or lost ! |
|
This still happens for some programs. Sorry about that. For a very long time, you can understand this has not been our topmost priority. After all, it was more urgent to make differentiated programs thar run, right? Also this was not so easy because the internal representation of Tapenade used to digest every declaration into symbol tables that did not remember where declarations came from. For the same reason, we had nowhere to keep comments attached to declarations. This has been improved for version 3. So hopefully, you should notice an improvement in that respect. Sometimes when the code uses the implicit feature of Fortran77, the include "calls" can't be placed back. Use implicit none to avoid that. Despite our efforts, it may happen that some comments are lost. This is a bug and we will be grateful if you signal it to us. |
Default sizes of basic data types:

| My system/compiler has a default size of 8 bytes for REAL, and Tapenade has a default size of 4 bytes. |
|
By default, Tapenade assumes that the actual sizes in memory of basic data types are
as follows: INTEGER: 4 bytes, REAL: 4 bytes, DOUBLE PRECISION: 8 bytes, COMPLEX: 8 bytes, DOUBLE COMPLEX: 16 bytes, LOGICAL: 4 byte, CHARACTER: 1 byte, These values are important to solve EQUIVALENCEs, or to match different declarations of the same COMMON block, or more importantly for the PUSH/POP mechanism of the reverse mode. Please check that your system/compiler uses the same sizes. In case of doubt, we provide a small tool to check this. Tapenade lets you declare new default sizes with the following options: -i2 -i4 -i8 -r4 -r8 other options might be created as necessary... |
Parameterized array sizes:

| My original file declares array A(N+1), and the code generated by Tapenade declares it as A(201) |
| This is true, and we understand it makes the new code less easy to maintain. When N is a constant, whose value is known statically, e.g. 200, Tapenade internally solves the N+1 into its value, here 201. This is on our list of things to improve. |
Some cases of "segmentation fault":

| My differentiated program crashes with a "segmentation fault". |
|
Yes. This may still happen. Sigh... Here are two (not so frequent) cases, that happen in the reverse mode:
|
Multi-directional modes (i.e. option -multi):

|
What do I do with the code generated with the option -multi? What is this DIFFSIZES.inc include file used in the differentiated code? What is this extra dimension in the differentiated variables? What is the relation between variables nd, nbdirs and nbdirsmax? |
|
The option -multi turns on multi-directional mode, which produces a code that will
compute the derivatives at a single point in the input space but along several
directions in this input space. This definition is for the tangent mode. Actually, there could be a -multi behavior in the adjoint mode too. This would compute the gradient for several weightings of the outputs. At present this multi-directional adjoint mode is not available in Tapenade, but this might be the case in the future. The way it works is by modifying the structure of the derivative variables. In standard, single-direction mode, a derivative variable retains the structure of its original variable (except for structures, in which non-active components disappear from the differentiated structure). In multi-directional mode, derivative variables receive one extra dimension, that corresponds to the multiple possible directions (or weightings). For instance a scalar variable
real*8 xx
will be differentiated if active into:
real*8 xxd(nbdirsmax)
and an array variable
real*8 aa(100,N)
will be differentiated if active into:
real*8 aad(nbdirsmax,100,N)
Notice that the extra dimension is added on the left, i.e. in the
deepest place in the FORTRAN dimensions order. This is useful
to isolate the array of derivatives of a given element
of aad.
Also notice that the (static) size of this new dimension is fixed to
nbdirsmax, which must be
at least equal to the maximum number of directions you plan
to explore at a time.The differentiated instructions are modified accordingly. A statement such as
a(i1, i2) = 2*b(i1, i2)
will be differentiated as
DO nd=1,nbdirs
ad(nd, i1, i2) = 2*bd(nd, i1, i2)
ENDDO
Notice that the loop on differentiation directions is
placed at the deepest level of the code's loop nesting.
Also these loops iterate nbdirs
times, thus allowing you to select at run time a number of directions
less or equal to nbdirsmax.
nd is just the index that
spans on the nbdirs directions.
When possible, Tapenade tries to merge these loops on
dimensions, to reduce loop overhead.
Notice finally that there is no such loop around
differentiated procedure calls, as these loops
will done inside the differentiated procedure.nbdirs is dynamic, so it is an extra parameter to be passed to the differentiated procedures. on the other hand, nbdirsmax is static: you must provide it at compile time. Tapenade strongly suggests that you define it in an include file named DIFFSIZES.inc. Tapenade adds the include command that includes this file. All you have to do is to create this file. In general, you need to do that only once. For instance, this file may contain
integer nbdirsmax
parameter (nbdirsmax = 50)
In Fortran90, a module DIFFSIZES
is used instead of an include file. You have to write it so that
it defines the constant nbdirsmax.
|
Getting the Jacobian using the Multi-directional mode:

|
How can I use the Multi-directional mode to compute a Jacobian? |
|
Some easy theory to begin with. Consider a procedure P(X,Y), that evaluates a math function F: X -> Y. Tangent mode AD of P produces a tangent code P_D(X,XD,Y,YD) that computes and returns:
If your goal is to obtain the Jacobian J, and knowing that the tangent code gives you J*XD for any XD, you see that the tangent code will give you any column of J by just providing a XD which is the corresponding vector of the Cartesian basis of the space of X's. In other words, if you provide XD full of 0.0 except for a 1.0 at rank i, then you will obtain a YD = J*XD that is the i-th column of J. If you do this repeatedly, you will get all the columns of J, and therefore J. So you need nothing more than the plain tangent mode of AD to obtain J. However, you see that this amounts to running P_D several times for different XD's but for the same X. Some identical computations are going to be duplicated. Multi-directional tangent mode just lets you factor out these identical computations: just ask Tapenade to produce the multi-directional tangent code of P, and then run this multi-directional tangent code with an XD which has now an extra dimension (at the deepest level) so that it contains all the vectors of the Cartesian basis of the input space (the space of X's). Technical details of the multi-directional mode are explained here. We also have in this FAQ a short discussion about Jacobians and Hessians by AD. Please take a look at it, especially in its second part devoted to the Jacobian: it contains further discussion about Jacobian sparsity and the multi directional adjoint/reverse mode. A final warning about the Jacobian built by multi-directional mode: In multi-directional tangent, all derivative variables receive naturally an extra dimension, which ranges over the different directions of differentiation, i.e. over the dimensions of the input space. For implementation reasons, this extra dimension must be the "deepest" dimension in the new differentiated variable. This is because we must be able to easily extract the derivatives of any element of any array. This has a consequence on the shape of the differentiated output. Suppose that the output Y is actually an array Y(1:10). The derivative result, which you may think of as the Jacobian, will have an extra dimension at the deepest location. For instance in Fortran, this will be YD(1:nbd,1:10) where nbd is the number of directions, which is (forgetting about sparsity) the dimension of the input space. On the other hand, the second dimension is linked to the dimension of the output space. You must take this into accout when giving this YD to a procedure that asks for the Jacobian: the conventions on dimensions must match and if they don't you may have to build the transpose of YD or modify the using procedure. |
Second Derivatives and Hessians:

|
Can Tapenade return second derivatives? Can Tapenade create a file to calculate Hessian also? |
|
Basically yes! However this requires a bit more interaction with
the end-user. Also the resulting differentiated programs could be
improved further if Tapenade had a special mode for second derivatives.
There is some ongoing research on these questions in our
research team. The idea to obtain second derivatives is to apply Automatic Differentiation twice. Starting from a procedure P in file p.f that computes y = f(x), a first run of Tapenade e.g. in tangent mode through the command line: $> tapenade -d -head P -vars "x" -outvars "y" p.freturns in file p_d.f a procedure P_D that computes yd = f'(x).xd. Now a new run of Tapenade on the resulting file e.g. in tangent mode again through the command line: $> tapenade -d -head P_D -vars "x" -outvars "yd" p_d.freturns in file p_d_d.f a procedure P_D_D that computes ydd = f''(x).xd.xd0 Specifically if you call P_D_D with inputs xd=1.0 and xd0=1.0 in addition to the current x, you obtain in output ydd the second derivative f''(x). TANGENT-ON-TANGENT APPROACH:In more realistic cases, the input x can be multi-dimensional. The output y can be multi-dimensional too, but this is not so much of a problem here. If you look for the second partial derivative d2y/dxidxj, you may apply the same two steps, differentiating P with respect to input xi first, then differentiating the resulting P_D with respect to input xj. If in reality xi or xj are indeed elements of a larger variable, e.g. elements of an array x, you must differentiate with respect to the larger variable. This is because Tapenade does not let you differentiate with respect to individual array elements. Then if you call the resulting procedure P_D_D with "correct" inputs xd and xd0, you obtain the second derivative(s) that you want in output ydd. Finally what are the "correct" values to initialize xd and xd0? In the special case of simple scalars, it is just 1.0. In the general case of array elements, set arrays xd and xd0 to 0.0, except for elements xd(i) and xd0(j) which must be set to 1.0.Going one step further, one may want several such derivatives d2y/dxidxj for a bunch of xi or xj. The natural answer is then to use tapenade multi-directional mode to compute al these derivatives in just one run. This works fine when multi-directional mode is called for only one of the two steps. In other words, what you get then is either a row or a column of the complete Hessian matrix. In the general case where one wants to compute the complete Hessian, one needs to call both differentiations in multi-directional mode. Doing this, you might encouter a couple of simple problems that you will need to fix by hand like we usually do:
TANGENT-ON-REVERSE APPROACH:Suppose the output y is scalar. Then you know that the reverse mode of AD can return you with the Jacobian, which is a single-row matrix, i.e. a gradient, at a very low cost. The natural extension of this is to differentiate the reverse-differentiated program, this time in multi-directional tangent mode. Hence the name "Tangent-on-Reverse". In theory, the cost of computing the gradient is a fixed small multiple (say 7*) of the cost of the original program P. Similarly, the cost of computing the tangent derivative is also a small multiple (say 4*) of the cost of P. If one uses the multi-directional tangent mode, for n simultaneous directions, the cost of the multi-directional tangent derivative is proportional to n (say 4*n). To summarize, the cost of computing the complete Hessian through the Tangent-on-Reverse approach should be roughly
4*n*7*P
whereas the cost of computing the complete Hessian
through the Tangent-on-Tangent approach should be roughly
4*n*4*n*P
Therefore Tangent-on-Reverse is appealing, although
in reality things are not so clear-cut.Tangent-on-Reverse raises a number of new problems, because of the PUSH and POP that are inserted by reverse differentiation. To obtain an correct tangent differentiation, one must tell the second-step Tapenade about the behavior of these PUSH and POP added by the first-step Tapenade. This could be improved if Tapenade made the two steps jointly, but this is still not the case... First thing is to declare the inputs and outputs of PUSH and POP. In addition to their argument, they have another hidden parameter, which is the stack. You must declare this in a special file, using the syntax for black-box routines described here. We provide such a file, named PUSHPOPGeneralLib, in the ADFirstAidKid. You may need to extend it if it doesn't cover a PUSH or POP function that your code uses. You will tell Tapenade to use this file PUSHPOPGeneralLib in the command line, using the command-line option ... -ext PUSHPOPGeneralLib ...The second thing is to link the final Tangent-on-Reverse differentiated code with PUSHPOPDiff.o obtained by compiling PUSHPOPDiff.f.
to the "tapenade-users" mailing list).
|
Declarations improperly differentiated:

| The array dimensions are lost in the differentiated declaration. |
|
Common containing arrays:
If an array has its bounds declared in the common statement, the differentiated declarations may lost these bounds. If it is the case, you should declare these bounds in the type declaration statement instead of the common statement. The pointer, target, and allocatable statements: allocatable :: a(:,:) pointer :: son target :: a, y(10)As described In "Fortran 95/2003 explained", "We believe that it is much clearer to specify these attributes on the type declaration statements, and therefore do not use these forms." In particular, if these statements use implicit typing, some declarations may be differentiated improperly. Until we fix these bugs, you should completly declare the arrays or allocatable arrays in the typing declaration. |
Errors on complicated statements:

|
I got the message (TC43) Could not find type of ..., please check |
| For differentiation purposes, Tapenade sometimes needs to split a long and complex expression with subroutine calls, introducing a a number of new intermediate variables. Usually this works fine, but in some situations, Tapenade cannot guess the type and size of the new intermediate variable. If Tapenade fails to find the correct type REAL of the temporary variable, puts INTEGER instead, this looses activity. We put the warning message (TC43) to signal the problem. The workaround is to do the splitting beforehand, on the source program, and to declare the intermediate variables with the correct type. |
Copied procedures in the differentiated code :

| Tapenade generates a large body of what looks like superfluous code that simply duplicates the code going in to Tapenade. |
|
There are nearly-copies of the original procedures in the differentiated code.
They appear in general with a new name obtained by appending "_C". This happens
in FORTRAN90 when a non-differentiated subroutine USE's a differentiated MODULE.
Suppose the original code is: MODULE M ... END MODULE M SUBROUTINE S USE M ... END SUBROUTINE SThis code is differentiated as: MODULE M_B ... END MODULE M_B SUBROUTINE S_B USE M_B ... END SUBROUTINE S_B SUBROUTINE S_CB USE M_B ... END SUBROUTINE S_CBSubroutine "S_CB" is a copy of subroutine "S", with a USE of the differentiated module "M_B" instead of the original module "M". So this does not create a clash for users who use the original code together with the differentiated code: we generate a different procedure for "S". So we chose to make a difference between
|
Building the context for calling the differentiated code :

|
I have differentiated my root procedure. How should I organize the program that calls it? I'm lost with these all these FOO and FOO_D and FOO_CD procedures and modules. How can I include them into my big application? My differentiated program does a segmentation fault because some differentiated globals are not allocated! |
|
You may use as a basis a calling context designed for the original, non-differentiated root procedure. Notice that even if you provided Tapenade with the complete calling context code as well, it will differentiate only the call graph below the root procedure. The calling context will not be modified nor adapted to the differentiated root. So even if there is a calling context available for the root, you will have to modify it by hand so that it can call the differentiated root. There are many possibile cases, so we'd rather use an example. We'll try to extract general rules later. Here's a small example source program. The differentiation root procedure is ROOT. The first column contains the code that is not in the call graph below ROOT.
We will differentiate in tangent mode, with head ROOT. We will not specify the name of the output file, so that every module or isolated procedure of the differentiated code will be put into a separate file. The differentiated call graph is returned in files root_d.f90, mma_d.f90, and mmb_d.f90.
The calling context of the original ROOT is not sufficient to call ROOT_D and must be adapted. Here this context consists of program MAIN, but there can be more modules and procedures in it. The new program MAIN_CD produced by Tapenade in file main_cd.f90 is a possible starting point, but it must be modified by hand. Or you can write it from scratch. For instance:
Places of interest are in blue. Some comments:
|
Tapenade and Windows :

| What should I do to use Tapenade on Windows? |
|
Before installing Tapenade, you must check that
an up-to-date Java Runtime Environment is installed. If there is none,
you can download and install the latest Java Runtime Environment
(Java SE 6 or JDK6) from the java.sun.com site.
Tapenade will not run with an older Java Runtime Environment. Then
|
Tapenade, Windows and cygwin problem :

| When parsing a fortran file with tapenade, I get these error messages : |
You have multiple copies of cygwin1.dll on your system. Search for cygwin1.dll using ... and delete all but the most recent version. The most recent version *should* reside in x:\cygwin\bin, where 'x' is the drive on which you have installed the cygwin distribution. System: Parsing errorIf it is the case, just remove the old cygwin1.dll that is in the directory tapenade/bin/windows. |
Tapenade with C programs on Windows, problem with cpp :

| Preprocessor error |
|
Before parsing a C program, Tapenade calls cpp to preprocess the C
program. On a unix machine, cpp is found in the standard PATH. On a
windows machine, you can specify the cpp command using the option
-cpp "\...\...\cpp_command".
If you have cygwin, you should find the correct path.
If you don't find cpp, and if your C program doesn't contain #define, #include, you can call tapenade -cpp "nocpp" program.c. If your program contains #include they will be ignore. |
Validation of the differentiated programs (OLD METHOD)

|
Help, the differentiated program crashes! How can I be sure that the differentiated program really computes what I want? How can I check the derivatives and find the place where the differentiated program goes wrong? |
|
This section is kept here only for the record.
It describes the old method that Tapenade provided before 2011.
We now have a simpler, more powerful method, described here.
In any case, to ease our validation mechanism, we strongly advise you to follow a few preparation steps, before you even start to differentiate anything. You will understand the reason for these steps if you go through the validation process that follows. CHECK THE TANGENT DERIVATIVES WITH RESPECT TO DIVIDED DIFFERENCES:In the following, words between double quotes and drawn in this color (such as "SHAPE") are indeed templates, that must be instantiated by actual strings depending on the context. Square brackets represent additional arguments, and the star represents repetition.In the example pieces of programs, actual pieces of code are written in this sort of dark red, short strings in double quotes are "patterns" as above, and "entiere lines between double quotes and drawn in the pattern color"represent a piece of code that you must write at that place. This is the standard way to test the tangent derivatives. Run your original subroutine P with input X, then with X+ *Xd, then compute:![]() You should get nearly the same result as when running the tangent differentiated subroutine P_d(X,Xd,Y) Differences should be only on a few last decimals. If this is not the case, here is a way to find where it goes wrong. Use the given library ddTest.f from the AD First-Aid kit. ddTest.f defines subroutines whose name is of the form
DD"TYPE""SHAPE"("(STRING) variable name",
"(FORTRAN TYPE) original variable",
"(SAME FORTRAN TYPE) differentiated variable"
[,"(INTEGER) array length"])
These procedures compare dynamically (at run-time) the derivatives obtained by divided differences (see below how they are built) with the derivatives obtained normally by the tangent differentiated program. If there is a difference, the procedure prints a message. This test is done each time one of these DD* procedures is encountered, on the variable passed as argument. Syntax details:
CALL DDREAL8("x", x, xd)To test a whole array, e.g. REAL T(10,0:2), and REAL means REAL*4 on your system CALL DDREAL4ARRAY("T", t, td, 30)You may test only one cell of an array, and then you don't need the dimension: CALL DDREAL4("T_3_2", t(3,2), td(3,2))You may even cheat a little with array sizes, to check only a fragment of it: CALL DDREAL4ARRAY("somepartofT", t(3,1), td(3,1), 5)Notice that these calls must be inserted in the code after the traced variable and its derivative have been computed, and before the traced variable or its derivative get overwritten later on. Of course, the above works only on a prepared program. Here is how to prepare it: We assume you have been differentiating a subroutine F. Then the file that uses F_D probably has the shape:
"set all inputs x1, x2, ... to their initial values"
"set the differentiation tangent direction in x1d, x2d,..."
CALL F_D(x1, x1d, x2, x2d,..., y1, y1d, y2, y2d,...)
"use the resulting derivatives y1d, y2d, ..."
Caution: By "inputs" (resp. "outputs") of F, we mean logical inputs
(resp. outputs), i.e. all values that will be used inside (resp. returned by)
F. This comprises
not only the arguments, but any common, global, etc... of any sort that
holds a value that is used inside (resp. returned by) F.
Since this verification mechanism needs to perform two
runs of F_D on the same inputs, you will need to store all these inputs
(we call this "taking a snapshot") before the first run of F_D, and restore them before
the second run. If you know your code very well and you are certain that some
inputs are never modified between the 1st and the 2nd run of F_D, then you
may omit them from the snapshot.
You should prepare the above call site so that it now looks like:
real*8 ddeps, epszero
integer ddphase, ddfile
common /DDTESTCOMMON/ ddphase, ddfile, ddeps, epszero
"set the inputs x1, x2, ... to their initial values"
"take a snapshot "S" of the initial values of x1, x2,..."
"set the differentiation tangent direction in variables x1d, x2d,..."
"take a snapshot "SD" of this initial direction x1d, x2d,..."
ddeps = 1.d-5
"set the inputs x1, x2, ... to a value "plus This program will encounter all the DD* calls that are present in F_D. On the first sweep (ddphase=1), it will just store the current value of the variable, which is the value "with " into a
temporary file epsvalues. If this file really grows too big, you might consider
modifying the present strategy, for example building two separate processes that
communicate through a UNIX pipe.
On the second sweep (ddphase=2), the value "with "
is retrieved, and combined with the current value to build the "divided differences" derivative.
This derivative is in turn compared with the AD analytic derivative.
if the divided difference is far from the AD derivative
(this is parametrized inside ddTest.f, where the percentage of difference is in
variable diff), then the word "DIFFERENCE" is printed on the
standard output close to the faulty derivatives.
Please note that nothing is printed when both derivatives (DD and AD) are zero.
Tapenade has an option that places automatically the calls to all necessary
DD* procedures, at the beginning and end of
user-specified procedures. This is of course an option of the tangent mode of AD and
its syntax is:$> tapenade -d -traceactivevars "units" ... The following does it on all units: $> tapenade -d -traceactivevars %all% ... Of course, you may add extra DD* trace calls by hand after that. Then playing with adding/moving calls to DD* procedures, you will hopefully close in to the place where the F_D file is incorrect! CHECK THE REVERSE DERIVATIVES USING THE DOT-PRODUCT TEST:In the following, words between double quotes and drawn in this color (such as "SHAPE") are indeed templates, that must be instantiated by actual strings depending on the context. Square brackets represent additional arguments, and the star represents repetition.In the example pieces of programs, actual pieces of code are written in this sort of dark red, short strings in double quotes are "patterns" as above, and "entiere lines between double quotes and drawn in the pattern color"represent a piece of code that you must write at that place. The dot-product test assumes that, if you call J the jacobian of F, and Jt its transpose, F_D(X,Xd) computes actually Yd = J.Xd and F_B(X,Yb) computes actually Xb = Jt.Yb Therefore, for a given Xd, F_D returns Yd = J.Xd, and if we set Yb=Yd F_B(X,Yb) then returns Xb = Jt.J.Xd We observe that if the program is correct, then the dot product (Xb | Xd) == (Jt.J.Xd | Xd) should be equal to (J.Xd | J.Xd) == (Yd | Yd) Tapenade will automatically place the necessary calls inside the differentiated code for you. Use the option -dptest provided in the differentiation command. Logically, you should first try to validate the complete differentiated program, whose root is here procedure F. Also, the dot-product test requires a preliminary run of a tangent differentiated code, so you should use Tapenade to generate both a tangent and a reverse differentiated code, both being aware of the dot-product test on F through the -dptest option. Finally, the -dptest option is incompatible with the -traceactivevars option used to validate the tangent mode, so make sure not to use it now. All in all, you should produce validation codes for F_D and F_B with commands: $> tapenade -d -head F -dptest "F" ... $> tapenade -b -head F -dptest "F" ... Look out for (AD14) messages such as: (AD14) Cannot instrument hidden active variable VVV . These tell you that the preparation of the code for the dot-product test could not be completed, because the test needs to maipulate variable VVV which is a global not visible by F. To solve this, you need to make this variable visible inside F, and differentiate again. Then, to run the dot-product test, you must also prepare your calling program as follows:
INTEGER dpunitcallcount,dptriggercount
COMMON /DPCALLCOUNT/dpunitcallcount,dptriggercount
"set the inputs x1, x2, ... to their initial values"
"take a snapshot "S" of the initial values of x1, x2,..."
"set the differentiation tangent direction in x1d, x2d,..."
dptriggercount = 1
CALL F_D(x1, x1d, x2, x2d,..., y1, y1d, y2, y2d,...)
"reset the inputs x1, x2, ... to their initial values, using "S""
"assign the y1d, y2d, ... into y1b, y2b,..."
CALL F_B(x1, x1b, x2, x2b,..., y1, y1b, y2, y2b,...)
CALL DPPRINTSUMMARY()
Be careful to check that the call patterns of the F_D and F_B subroutines match exactly their definition, which you will find in the differentiated files. In fact, using the -dptest option might change the number of arguments of these differentiated subroutines, so it is important to check that definitions and call match. Compile the test code and link it with the two libraries specific to the dot-product test, dptest.f and dpStack.c. Since this uses the reverse mode, it should also be linked with the usual adBuffer.f and adStack.c. All these files are provided in the AD First-Aid kit. Running the test code will print on the standard output the values of the dot-products for each fragment between the milestone points placed in the code, printing the products (Yd | Yd) when going forward, and the products (Xd | Xb) when going backward. There is a summary printed at the end of the process by the call to DPPRINTSUMMARY() in the test site, which should make it easier to read. If there is no bug, corresponding forward and backward dot-products should return the same value, and the validation process is complete. If this is not the case, assuming that you have validated the tangent mode as indicated above, this shows there is a problem with the reverse mode of the fragments for which the dot-products differ. Then the question is: where does the problem occur? To find this out, we propose that you use a divide and conquer method, by moving or adding extra milestones for the dot-product test. For example the -dptest "F" has set only the minimal two milestones, at the entry and at the exit of F, plus one "somewhere in the middle". These milestones appear in the summary with default names: EntryIntof, Inf, and ExitFromf respectively. Choose one (or more...) points in (the control flow of) subroutine F_D. Find the EXACTLY corresponding point in the reverse sweep of subroutine F_B. Then move the piece of code that defines the Inf milestone of F_D (it is an in-then-endif statement controlled by IF (DPFWDTESTCOUNT('f')) THEN) to the chosen new point in F_D. Alternatively you may duplicate this code (give it a new name e.g 'PointAInf') to place an additional milestone. Do the same for F_B. Don't call Tapenade again at this step, because it would overwrite your hand-modified F_D and F_B codes. Compile and run the test again as explained above, from this "Compile-and-Run" step. If the faulty zone eventually gets reduced to a handful of simple statements, then you should be able to find the bug. Otherwise the faulty zone may be reduced to a single call to a procedure called, say, G. You must now restart the divide and conquer process this time on G. Use Tapenade to place the necessary dot-product calls, with the -dptest option. $> tapenade -d -head F -dptest "G" ... $> tapenade -b -head F -dptest "G" ...Look out for (AD14) messages again! Also, the bug may appear not on the first run-time call to G, but on some later call, for instance the 12th. In this case, replace
dptriggercount = 1
in the calling toplevel by
dptriggercount = 12
then resume from the "Compile-and-Run" step. We advise you not to create new milestones by hand, except by copy-paste as explained above, because this is rather delicate. The order of PUSH and POP's matters, and it is extremely important that no active variable goes through a milestone without being caught by one PUSH/POP. This dot-product validation process is much more fragile than the divided differences validation of the tangent derivatives. Lastly, when all goes well, don't forget to remove the validation options from your differentiation command lines! |
All Warning or Error messages :

| What do all these Warning and Error messages mean? |
|
TAPENADE issues a number of messages resulting from the preliminary analyses done before actual differentiation.
Although the temptation is strong,
these messages should not be ignored right away. Especially when AD is
concerned, these messages can be the indication that the program runs into
one limitation of the AD technology. So even if the original program compiles
and runs correctly with your compiler, these messages warn you that
differntiation may produce a faulty program. Some messages are requests for help: when TAPENADE needs some help from the user, e.g. because it needs and does not know the size of an array, it issues a message that asks you to give this size after differentiation is done. Otherwise the resulting program will not run. Here is the list of all Warning and Error messages of Tapenade, grouped by category, and pointing to a discussion on their meaning and importance. This list may not be up to date, unfortunately...
What can you do? Do not use files as temporary storage. Use arrays instead. You may also use TAPENADE "Black-box" mechanism to disconnect differentiation on the calling subroutines, and differentiate them by hand, with an ad-hoc mechanism to propagate derivatives.
This reminds you that a variable that was active has been overwritten by an I-O operation Why should you care? The derivative is of course reset to zero. Maybe this is not what you expected? What can you do? Check that it is OK. Otherwise try to put all I-O operations outside the part of the program you are actually differentiating.
These messages all indicate that the checkpointing mechanism is not certain that it can do a correct job. These messages point at (a collection) of procedure calls in the program, e.g. all calls to Func, or one particular call, or procedure (i.e. subroutine or function) Proc1 calling another procedure (i.e. subroutine or function) Proc2. In reverse mode, the standard strategy of Tapenade is to checkpoint all procedure calls (cf a quick description of checkpointing, or Tapenade User's guide for something more technical). Checkpointing a call to Proc2 inside Proc1 means that the reverse differentiated subroutine of Proc1 is going to call Proc2 twice, or more precisely the original Proc2 once and then the differentiated Proc2. Roughly speking, these two calls must be made with the same inputs, so that the 2nd call is an exact duplication of the 1st call. To do this, a minimal "environment" needs to be stored just before the 1st call to Proc2 and restored just before the 2nd call. We know how to save and restore most variables, e.g. the formal parameters of Proc2, but also the globals known by Proc1 and Proc2. On the other hand, we don't know how to save and restore hidden SAVE variables, or private global variables of a module, or a COMMON which is not declared at the calling subroutine level, or the position of the read/write pointer inside an opened file which is being read/written, or the very open/close status of a file, and several other things... These messages just signal such (possibly) nasty situations. Why should you care? Checkpointing cannot restore the environment before the second call to Proc2. Therefore the differentiated program might be wrong. What can you do? Either make absolutely sure that this is not a problem in the present case, because you know that the second call to Proc2 will behave just like the 1st one, at least as far as differentiation is concerned. You need to know the code for that because errors will be hard to track down. For instance, one favorable situation is when the (AD10) message corresponds only to prints of messages on the screen. The messages will appear twice, but derivatives should be correct. Or modify your code so that the problem disappears, for instance insert the COMMON declaration at the level of the calling subroutine. or transform the SAVE variables into elements of a COMMON that everyone can see, or take the file I/O or dynamic memory allocation or deallocation outside the code to differentiate, Or (more high-tech!) tell Tapenade not to checkpoint the calls that raise the problem using the "nocheckpoint" directive.
In some cases, for example in reverse-mode AD, the differentiated function must save some variables, to restore them later. If one such variable is an array, and the size of this array is dynamic, TAPENADE must know this size to actually do the save. Why should you care? If you don't give this size, the differentiated program will not compile. What can you do? Define the value of the PARAMETERS that TAPENADE requests, in a special include file. You may also explicitly give this size in the original file, so that the message does not show up!
You must provide the differentiated program with a new function, differentiated from Func with respect to the input and output parameters of Func specified in the arguments. Probably Func is an external function, for which TAPENADE has no source, and therefore it couldn't differentiate it. Why should you care? If you don't give this new function, the differentiated program will probably not compile. What can you do? Define this new function. You have the choice of the method. Maybe you know explicitly the derivative mathematical function, which can be implemented efficiently. Maybe you have the source and you may use AD again, with TAPENADE or with another tool. Maybe you just want to run a local "divided differences" method, knowing that this might slightly degrade the precision of the derivatives. In any case, make sure that you provide a differentiated function in the correct mode (tangent, reverse...), and which adheres to the conventions of TAPENADE about the name of the function and the order of its parameters. |
... To be continued ...