#ifndef DEF_AUTOMATIC_DIFFERENTIATION #define DEF_AUTOMATIC_DIFFERENTIATION #include #include #include /// Implementation of dual numbers for automatic differentiation. /// This implementation uses vectors for b so that function gradients can be computed in one function call. /// Set the index of every variable with the ::d(int i) function and call the function to be computed : f(x+DualVector::d(0), y+DualVector::d(1), z+DualVector::d(2), ...) /// reference : http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.89.7749&rep=rep1&type=pdf template class DualVector { public: using VectorT = Eigen::Matrix; DualVector(const Scalar & _a, const VectorT & _b = VectorT::Zero()) : a(_a), b(_b) {} DualVector(const Scalar & _a, const Scalar & _b) : a(_a) { b.fill(_b); } static DualVector d(int i = 0) { assert(i >= 0); assert(i < N); VectorT _d = VectorT::Zero(); _d[i] = Scalar(1.); return DualVector(Scalar(0.), _d); } DualVector & operator+=(const DualVector & x) { a += x.a; b += x.b; return *this; } DualVector & operator-=(const DualVector & x) { a -= x.a; b -= x.b; return *this; } DualVector & operator*=(const DualVector & x) { b = a*x.b + b*x.a; a *= x.a; return *this; } DualVector & operator/=(const DualVector & x) { b = (x.a*b - a*x.b)/(x.a*x.a); a /= x.a; return *this; } DualVector operator+(const DualVector & x) const { DualVector res(*this); return (res += x); } DualVector operator+(void) const // +x { return (*this); } DualVector operator-(const DualVector & x) const { DualVector res(*this); return (res -= x); } DualVector operator-(void) const // -x { return DualVector(-a, -b); } DualVector operator*(const DualVector & x) const { DualVector res(*this); return (res *= x); } DualVector operator/(const DualVector & x) const { DualVector res(*this); return (res /= x); } Scalar a; /// Real part VectorT b; /// Infinitesimal parts }; // Basic mathematical functions for Scalar numbers // Trigonometric functions template Scalar sec(const Scalar & x) { return Scalar(1.)/cos(x); } template Scalar cot(const Scalar & x) { return cos(x)/sin(x); } template Scalar csc(const Scalar & x) { return Scalar(1.)/sin(x); } // Inverse trigonometric functions template Scalar asec(const Scalar & x) { return acos(Scalar(1.)/x); } template Scalar acot(const Scalar & x) { return atan(Scalar(1.)/x); } template Scalar acsc(const Scalar & x) { return asin(Scalar(1.)/x); } // Hyperbolic trigonometric functions template Scalar sech(const Scalar & x) { return Scalar(1.)/cosh(x); } template Scalar coth(const Scalar & x) { return cosh(x)/sinh(x); } template Scalar csch(const Scalar & x) { return Scalar(1.)/sinh(x); } // Inverse hyperbolic trigonometric functions template Scalar asech(const Scalar & x) { return log((Scalar(1.) + sqrt(Scalar(1.) - x*x))/x); } template Scalar acoth(const Scalar & x) { return Scalar(0.5)*log((x + Scalar(1.))/(x - Scalar(1.))); } template Scalar acsch(const Scalar & x) { return (x >= Scalar(0.)) ? log((Scalar(1.) + sqrt(Scalar(1.) + x*x))/x) : log((Scalar(1.) - sqrt(Scalar(1.) + x*x))/x); } template Scalar exp10(const Scalar & x) { return exp(x*log(Scalar(10.))); } template Scalar sign(const Scalar & x) { return (x >= Scalar(0.)) ? ((x > Scalar(0.)) ? Scalar(1.) : Scalar(0.)) : Scalar(-1.); } template Scalar heaviside(const Scalar & x) { return Scalar(x >= Scalar(0.)); } // Basic mathematical functions for DualVector numbers // f(a + b*d) = f(a) + b*f'(a)*d // Trigonometric functions template DualVector cos(const DualVector & x) { return DualVector(cos(x.a), -x.b*sin(x.a)); } template DualVector sin(const DualVector & x) { return DualVector(sin(x.a), x.b*cos(x.a)); } template DualVector tan(const DualVector & x) { return DualVector(tan(x.a), x.b*sec(x.a)*sec(x.a)); } template DualVector sec(const DualVector & x) { return DualVector(sec(x.a), x.b*sec(x.a)*tan(x.a)); } template DualVector cot(const DualVector & x) { return DualVector(cot(x.a), x.b*(-csc(x.a)*csc(x.a))); } template DualVector csc(const DualVector & x) { return DualVector(csc(x.a), x.b*(-cot(x.a)*csc(x.a))); } // Inverse trigonometric functions template DualVector acos(const DualVector & x) { return DualVector(acos(x.a), x.b*(-Scalar(1.)/sqrt(Scalar(1.)-x.a*x.a))); } template DualVector asin(const DualVector & x) { return DualVector(asin(x.a), x.b*(Scalar(1.)/sqrt(Scalar(1.)-x.a*x.a))); } template DualVector atan(const DualVector & x) { return DualVector(atan(x.a), x.b*(Scalar(1.)/(x.a*x.a + Scalar(1.)))); } template DualVector asec(const DualVector & x) { return DualVector(asec(x.a), x.b*(Scalar(1.)/(sqrt(Scalar(1.)-Scalar(1.)/(x.a*x.a))*(x.a*x.a)))); } template DualVector acot(const DualVector & x) { return DualVector(acot(x.a), x.b*(-Scalar(1.)/((x.a*x.a)+Scalar(1.)))); } template DualVector acsc(const DualVector & x) { return DualVector(acsc(x.a), x.b*(-Scalar(1.)/(sqrt(Scalar(1.)-Scalar(1.)/(x.a*x.a))*(x.a*x.a)))); } // Hyperbolic trigonometric functions template DualVector cosh(const DualVector & x) { return DualVector(cosh(x.a), x.b*sinh(x.a)); } template DualVector sinh(const DualVector & x) { return DualVector(sinh(x.a), x.b*cosh(x.a)); } template DualVector tanh(const DualVector & x) { return DualVector(tanh(x.a), x.b*sech(x.a)*sech(x.a)); } template DualVector sech(const DualVector & x) { return DualVector(sech(x.a), x.b*(-sech(x.a)*tanh(x.a))); } template DualVector coth(const DualVector & x) { return DualVector(coth(x.a), x.b*(-csch(x.a)*csch(x.a))); } template DualVector csch(const DualVector & x) { return DualVector(csch(x.a), x.b*(-coth(x.a)*csch(x.a))); } // Inverse hyperbolic trigonometric functions template DualVector acosh(const DualVector & x) { return DualVector(acosh(x.a), x.b*(Scalar(1.)/sqrt((x.a*x.a)-Scalar(1.)))); } template DualVector asinh(const DualVector & x) { return DualVector(asinh(x.a), x.b*(Scalar(1.)/sqrt((x.a*x.a)+Scalar(1.)))); } template DualVector atanh(const DualVector & x) { return DualVector(atanh(x.a), x.b*(Scalar(1.)/(Scalar(1.)-(x.a*x.a)))); } template DualVector asech(const DualVector & x) { return DualVector(asech(x.a), x.b*(Scalar(-1.)/(sqrt(Scalar(1.)/(x.a*x.a)-Scalar(1.))*(x.a*x.a)))); } template DualVector acoth(const DualVector & x) { return DualVector(acoth(x.a), x.b*(-Scalar(1.)/((x.a*x.a)-Scalar(1.)))); } template DualVector acsch(const DualVector & x) { return DualVector(acsch(x.a), x.b*(-Scalar(1.)/(sqrt(Scalar(1.)/(x.a*x.a)+Scalar(1.))*(x.a*x.a)))); } // Exponential functions template DualVector exp(const DualVector & x) { return DualVector(exp(x.a), x.b*exp(x.a)); } template DualVector log(const DualVector & x) { return DualVector(log(x.a), x.b/x.a); } template DualVector exp10(const DualVector & x) { return DualVector(exp10(x.a), x.b*(log(Scalar(10.))*exp10(x.a))); } template DualVector log10(const DualVector & x) { return DualVector(log10(x.a), x.b/(log(Scalar(10.))*x.a)); } template DualVector exp2(const DualVector & x) { return DualVector(exp2(x.a), x.b*(log(Scalar(2.))*exp2(x.a))); } template DualVector log2(const DualVector & x) { return DualVector(log2(x.a), x.b/(log(Scalar(2.))*x.a)); } template DualVector pow(const DualVector & x, const DualVector & n) { return exp(n*log(x)); } // Other functions template DualVector sqrt(const DualVector & x) { return DualVector(sqrt(x.a), x.b/(Scalar(2.)*sqrt(x.a))); } template DualVector sign(const DualVector & x) { return DualVector(sign(x.a), DualVector::VectorT::Zero()); } template DualVector abs(const DualVector & x) { return DualVector(fabs(x.a), x.b*sign(x.a)); } template DualVector heaviside(const DualVector & x) { return DualVector(heaviside(x.a), DualVector::VectorT::Zero()); } template DualVector floor(const DualVector & x) { return DualVector(floor(x.a), DualVector::VectorT::Zero()); } template DualVector ceil(const DualVector & x) { return DualVector(ceil(x.a), DualVector::VectorT::Zero()); } template DualVector round(const DualVector & x) { return DualVector(round(x.a), DualVector::VectorT::Zero()); } template std::ostream & operator<<(std::ostream & s, const DualVector & x) { return (s << x.a); } #endif