Added Function object for computing the grad and macros to create them.

This commit is contained in:
Jérôme 2019-03-26 23:25:01 +01:00
parent 592f6976ad
commit 5c57aa0afd
6 changed files with 2787 additions and 60 deletions

3
.gitignore vendored
View file

@ -1,6 +1,9 @@
# executables # executables
run run
# documentation
html/
# catch2 generated files # catch2 generated files
*.gcda *.gcda
*.gcno *.gcno

View file

@ -207,22 +207,23 @@ class Dual
VectorT b; /// Infinitesimal parts VectorT b; /// Infinitesimal parts
}; };
//*
template<typename A, typename B> template<typename A, typename B>
Dual<B> operator+(A const& v, Dual<B> const& x) { Dual<B> operator+(A const& v, Dual<B> const& x) {
return (Dual<B>(v) + x); return (Dual<B>(static_cast<B>(v)) + x);
} }
template<typename A, typename B> template<typename A, typename B>
Dual<B> operator-(A const& v, Dual<B> const& x) { Dual<B> operator-(A const& v, Dual<B> const& x) {
return (Dual<B>(v) - x); return (Dual<B>(static_cast<B>(v)) - x);
} }
template<typename A, typename B> template<typename A, typename B>
Dual<B> operator*(A const& v, Dual<B> const& x) { Dual<B> operator*(A const& v, Dual<B> const& x) {
return (Dual<B>(v) * x); return (Dual<B>(static_cast<B>(v)) * x);
} }
template<typename A, typename B> template<typename A, typename B>
Dual<B> operator/(A const& v, Dual<B> const& x) { Dual<B> operator/(A const& v, Dual<B> const& x) {
return (Dual<B>(v) / x); return (Dual<B>(static_cast<B>(v)) / x);
} }//*/
// Basic mathematical functions for Scalar numbers // Basic mathematical functions for Scalar numbers
@ -464,35 +465,82 @@ template<typename Scalar> std::ostream & operator<<(std::ostream & s, const Dual
return (s << x.a); return (s << x.a);
} }
/// Function object to evaluate the derivative of a function anywhere without explicitely using the Dual numbers.
//*
template<typename Func, typename Scalar>
struct GradFunc
{
Func f;
GradFunc(Func f_, Scalar) : f(f_) {}
// Function that returns the 1st derivative of the function f at x.
Scalar operator()(Scalar const& x)
{
// differentiate using the Dual number and return the .b component.
Dual<Scalar> X(x);
X.diff(0,1);
Dual<Scalar> Y = f(X);
return Y.d(0);
}
/// Function that returns both the function value and the gradient of f at x. Use this preferably over separate calls to f and to gradf.
void get_f_grad(Scalar const& x, Scalar & fx, Scalar & gradfx)
{
// differentiate using the Dual number and return the .b component.
Dual<Scalar> X(x);
X.diff(0,1);
Dual<Scalar> Y = f(X);
fx = Y.x();
gradfx = Y.d(0);
}
};//*/
//*
/// Macro to create a function object that returns the gradient of the function at X.
/// Designed to work with functions, lambdas, etc.
#define CREATE_GRAD_FUNCTION_OBJECT(Func, GradFuncName) \
struct GradFuncName { \
template<typename Scalar> \
Scalar operator()(Scalar const& x) { \
Dual<Scalar> X(x); \
X.diff(0,1); \
Dual<Scalar> Y = Func<Dual<Scalar>>(X); \
return Y.d(0); \
} \
template<typename Scalar> \
void get_f_grad(Scalar const& x, Scalar & fx, Scalar & gradfx) { \
Dual<Scalar> X(x); \
X.diff(0,1); \
Dual<Scalar> Y = Func<Dual<Scalar>>(X); \
fx = Y.x(); \
gradfx = Y.d(0); \
} \
}
//*/
//*
/// Macro to create a function object that returns the gradient of the function at X.
/// Designed to work with function objects.
#define CREATE_GRAD_FUNCTION_OBJECT_FUNCTOR(Func, GradFuncName) \
struct GradFuncName { \
template<typename Scalar> \
Scalar operator()(Scalar const& x) { \
Dual<Scalar> X(x); \
X.diff(0,1); \
Func f; \
Dual<Scalar> Y = f(X); \
return Y.d(0); \
} \
template<typename Scalar> \
void get_f_grad(Scalar const& x, Scalar & fx, Scalar & gradfx) { \
Dual<Scalar> X(x); \
X.diff(0,1); \
Func f; \
Dual<Scalar> Y = f(X); \
fx = Y.x(); \
gradfx = Y.d(0); \
} \
}
//*/
#endif #endif

2482
Doxyfile Executable file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
CXX = clang++ CXX = clang++
INCLUDE = -I../Catch2-master/single_include INCLUDE = -I../Catch2-master/single_include
CFLAGS = -Wall -std=c++11 -O0 -fprofile-arcs -ftest-coverage $(INCLUDE) CFLAGS = -Wall -std=c++17 -O0 -fprofile-arcs -ftest-coverage $(INCLUDE)
LDFLAGS = -lgcov LDFLAGS = -lgcov
LDFLAGS_ALL = $(LDFLAGS) LDFLAGS_ALL = $(LDFLAGS)
OUTPUT_NAME = test_AutomaticDifferentiation OUTPUT_NAME = test_AutomaticDifferentiation

102
examples/grad_functor.cpp Executable file
View file

@ -0,0 +1,102 @@
#include "../AutomaticDifferentiation.hpp"
#include <iostream>
#include <iomanip>
using std::cout;
using std::endl;
using std::setw;
#define PRINT_VAR(x) std::cout << #x << "\t= " << std::setprecision(16) << (x) << std::endl
#define PRINT_DUAL(x) std::cout << #x << "\t= " << std::fixed << std::setprecision(4) << std::setw(10) << (x).a << ", " << std::setw(10) << (x).b << std::endl
template<typename T>
T f(const T & x)
{
// return T(1.) + x + x*x + T(1.)/x + log(x);
return 1 + x + x*x + 1/x + log(x);
}
template<typename T>
T df(const T & x)
{
return 2*x + 1 + 1.0/x - 1/pow(x, 2);
}
template<typename T>
T ddf(const T & x)
{
return 2 - 1/pow(x, 2) + 2/pow(x, 3);
}
struct fFunctor
{
template<typename Scalar>
Scalar operator()(Scalar const& x) {return f(x);}
};
CREATE_GRAD_FUNCTION_OBJECT(f, GradF);
CREATE_GRAD_FUNCTION_OBJECT_FUNCTOR(fFunctor, GradF_funct);
CREATE_GRAD_FUNCTION_OBJECT_FUNCTOR(GradF_funct, Grad2F_funct);
int main()
{
cout.precision(16);
double xdbl = 1.5;
{
cout << "Analytical\n";
cout << "f(x) = " << f(xdbl) << endl;
cout << "df(x)/dx = " << df(xdbl) << endl;
cout << "d²f(x)/dx² = " << ddf(xdbl) << endl;
}
// 1st derivative forward
{
using Fd = Dual<double>;
Fd x = xdbl;
x.diff(0);
Fd y = f(x);
cout << "\nForward\n";
cout << "f(x) = " << y.a << endl;
cout << "df(x)/dx = " << y.d(0) << endl;
}
// first derivative using the gradient functor
{
GradFunc gradf(f<Dual<double>>, xdbl);
double fx, dfdx;
gradf.get_f_grad(xdbl, fx, dfdx);
cout << "\nForward using gradient function object\n";
cout << "f(x) = " << fx << endl;
cout << "df(x)/dx = " << dfdx << endl;
cout << "df(x)/dx = " << gradf(xdbl) << endl;
}
// first derivative using the gradient functor created through the macro
{
// GradF<double> gradf;
GradF gradf;
// GradFunc gradf(f<Dual<double>>, xdbl);
double fx, dfdx;
gradf.get_f_grad(xdbl, fx, dfdx);
cout << "\nForward using gradient function object created using the macro\n";
cout << "f(x) = " << fx << endl;
cout << "df(x)/dx = " << dfdx << endl;
cout << "df(x)/dx = " << gradf(xdbl) << endl;
{
GradF_funct gradf_funct;
Grad2F_funct grad2f_funct;
PRINT_VAR(gradf(1.5f));
PRINT_VAR(gradf_funct(1.5));
// PRINT_VAR(grad2f_funct(1.5));// for this to work, the operator+-*/(A, Dual<B>) must not be defined (conflict with operator from valarray)
// third order does not work anyway (can't convert a double to Dual<Dual<Dual<double>>>...)
}
}
return 0;
}

View file

@ -0,0 +1,92 @@
#include "../AutomaticDifferentiation.hpp"
#include <iostream>
#include <iomanip>
#include <Eigen/Dense>
using std::cout;
using std::endl;
using std::setw;
#define PRINT_VAR(x) std::cout << #x << "\t= " << std::setprecision(16) << (x) << std::endl
#define PRINT_DUAL(x) std::cout << #x << "\t= " << std::fixed << std::setprecision(4) << std::setw(10) << (x).a << ", " << std::setw(10) << (x).b << std::endl
template<typename T>
T f(const T & x)
{
return 1 + x + x*x + 1/x + log(x);
}
template<typename T>
T df(const T & x)
{
return 2*x + 1 + 1.0/x - 1/pow(x, 2);
}
template<typename T>
T ddf(const T & x)
{
return 2 - 1/pow(x, 2) + 2/pow(x, 3);
}
CREATE_GRAD_FUNCTION_OBJECT(f, GradF);
int main()
{
cout.precision(16);
double xdbl = 1.5;
{
cout << "Analytical\n";
cout << "f(x) = " << f(xdbl) << endl;
cout << "df(x)/dx = " << df(xdbl) << endl;
cout << "d²f(x)/dx² = " << ddf(xdbl) << endl;
}
// 1st derivative forward
{
using Fd = Dual<double>;
Fd x = xdbl;
x.diff(0);
Fd y = f(x);
cout << "\nForward\n";
cout << "f(x) = " << y.a << endl;
cout << "df(x)/dx = " << y.d(0) << endl;
}
// first derivative using the gradient functor
{
GradFunc gradf(f<Dual<double>>, xdbl);
double fx, dfdx;
gradf.get_f_grad(xdbl, fx, dfdx);
cout << "\nForward using gradient function object\n";
cout << "f(x) = " << fx << endl;
cout << "df(x)/dx = " << dfdx << endl;
cout << "df(x)/dx = " << gradf(xdbl) << endl;
}
// using a vector type
{
// using vtype = std::valarray<double>;
using vtype = Eigen::Array3d;
GradF gradf;
// vtype x(3, xdbl);
vtype x(xdbl/2, xdbl, xdbl*2);
vtype fx, dfx;
gradf.get_f_grad(x, fx, dfx);
// Dual<vtype> X(x);
// X.diff(0,1);
// Dual<vtype> Fx = f(X);
// fx = Fx.x();
// dfx = Fx.d(0);
PRINT_VAR(x);
PRINT_VAR(fx);
PRINT_VAR(dfx);
// also, using a single dual<vector_type> in a R^n -> R function to get the full gradient does not work : no operator[] in Dual, and adding it does not seem to work either...
}
return 0;
}