Goal: Write a tape to storage in a binary, text, graphical or math format.
Prerequisite: Tutorial 2 - Reverse mode AD
Function:
template<typename Real>
Real func(
const std::vector<Real>& x) {
return x[0] * x[1] * x[0];
}
Generate Tape
template<typename Real>
void generateTape(std::string const& fileName) {
std::vector<Identifier> x_id;
std::vector<Identifier> y_id;
std::vector<Real> x = {4.0, 3.0};
tape.setActive();
tape.registerInput(x[0]);
tape.registerInput(x[1]);
tape.registerOutput(y);
tape.setPassive();
tape.writeTape(codi::createWriter<Real>(fileName + "_text.txt", x_id, y_id, codi::FileType::Text));
tape.writeTape(codi::createWriter<Real>(fileName + "_graph.dot", x_id, y_id, codi::FileType::Graph));
tape.evaluate();
std::cout << fileName << ":" << std::endl;
std::cout << "df/dx[0](4.0) = " << x[0].getGradient() << std::endl;
std::cout << "df/dx[1](3.0) = " << x[1].getGradient() << std::endl << std::endl;
tape.reset();
}
Full code:
#include <codi.hpp>
#include <iostream>
template<typename Real>
Real func(
const std::vector<Real>& x) {
return x[0] * x[1] * x[0];
}
template<typename Real>
void generateTape(std::string const& fileName) {
std::vector<Identifier> x_id;
std::vector<Identifier> y_id;
std::vector<Real> x = {4.0, 3.0};
tape.setActive();
tape.registerInput(x[0]);
tape.registerInput(x[1]);
tape.registerOutput(y);
tape.setPassive();
tape.writeTape(codi::createWriter<Real>(fileName + "_text.txt", x_id, y_id, codi::FileType::Text));
tape.writeTape(codi::createWriter<Real>(fileName + "_graph.dot", x_id, y_id, codi::FileType::Graph));
tape.evaluate();
std::cout << fileName << ":" << std::endl;
std::cout << "df/dx[0](4.0) = " << x[0].getGradient() << std::endl;
std::cout << "df/dx[1](3.0) = " << x[1].getGradient() << std::endl << std::endl;
tape.reset();
}
int main(int nargs, char** args) {
generateTape<codi::RealReverse>("jacobian_linear");
generateTape<codi::RealReversePrimalIndex>("primal_reuse");
return 0;
}
The tape writers are used to store a tape. The writers can create a binary or text file which can later be used in a new context to restore a tape. The writers can also create a graphical (for both Jacobian and primal value tapes) or a math representation of the tape (only for primal value tapes). These formats are useful for visualization and debugging.