Goal: Evaluate only parts of CoDiPack tapes.
Prerequisite: Tutorial 2 - Reverse mode AD
Function:
template<typename Real>
Real func(Real a, Real b) {
Real x = a + b;
Real y = a - b;
Real w = sqrt(x * x + y * y);
return cos(w);
}
Full code:
#include <iostream>
#include <codi.hpp>
template<typename Real>
Real func(Real a, Real b) {
Real x = a + b;
Real y = a - b;
Real w = sqrt(x * x + y * y);
return cos(w);
}
using Position = typename Tape::Position;
Real funcInner(Real x, Real y) {
return sqrt(x * x + y * y);
}
void positionalExample() {
Real a = 10.0;
Real b = 4.0;
tape.setActive();
tape.registerInput(a);
tape.registerInput(b);
Real u1 = a + b;
Real u2 = a - b;
Position begin = tape.getPosition();
Real w = funcInner(u1, u2);
tape.evaluate(tape.getPosition(), begin);
tape.resetTo(begin);
Real y = cos(w);
tape.registerOutput(y);
tape.setPassive();
tape.evaluate();
std::cout << "Positional example:" << std::endl;
std::cout <<
"Gradient of dy/da: " << a.
getGradient() << std::endl;
std::cout << "Gradient of dy/db: " << b.getGradient() << std::endl;
tape.reset();
}
void validation() {
Real a = 10.0;
Real b = 4.0;
tape.setActive();
tape.registerInput(a);
tape.registerInput(b);
Real y = func(a, b);
tape.registerOutput(y);
tape.setPassive();
tape.evaluate();
std::cout << "Validation:" << std::endl;
std::cout <<
"Gradient of dy/da: " << a.
getGradient() << std::endl;
std::cout <<
"Gradient of dy/db: " << b.
getGradient() << std::endl;
tape.reset();
}
int main(int nargs, char** args) {
positionalExample();
std::cout << std::endl;
validation();
}
In this example, some part of the function func()
can be replaced by another function funcInner
. The tape recording and evaluation for func() can either be done in the usual way or by using funcInner through storing its position during the recording process and manually providing its derivatives (u1_d, u2_d).
In general, positional evaluation can have multiple use cases:
- Record independent parts of the application and access them on a need basis
- Directly evaluate recorded functions to compute the Jacobians and store them (See also Example 15 - Preaccumulation of code parts)
- etc.
For a partly evaluated/reset tape the following should always be kept in mind:
- Is the adjoint vector cleared properly
- Are all active variables still valid after the reset.