/** * @file Dual.hpp * * This is a dual number type for calculating automatic derivatives. * * Based roughly on the methods described in: * Automatic Differentiation, C++ Templates and Photogrammetry, by Dan Piponi * and * Ceres Solver's excellent Jet.h * * @author Julian Kent */ #pragma once #include "math.hpp" namespace matrix { template class Vector; template struct Dual { static constexpr size_t WIDTH = N; Scalar value {}; Vector derivative; Dual() = default; explicit Dual(Scalar v, size_t inputDimension = 65535) { value = v; if (inputDimension < N) { derivative(inputDimension) = Scalar(1); } } explicit Dual(Scalar v, const Vector& d) : value(v), derivative(d) {} Dual& operator=(const Scalar& a) { derivative.setZero(); value = a; return *this; } Dual& operator +=(const Dual& a) { return (*this = *this + a); } Dual& operator *=(const Dual& a) { return (*this = *this * a); } Dual& operator -=(const Dual& a) { return (*this = *this - a); } Dual& operator /=(const Dual& a) { return (*this = *this / a); } Dual& operator +=(Scalar a) { return (*this = *this + a); } Dual& operator -=(Scalar a) { return (*this = *this - a); } Dual& operator *=(Scalar a) { return (*this = *this * a); } Dual& operator /=(Scalar a) { return (*this = *this / a); } }; // operators template Dual operator+(const Dual& a) { return a; } template Dual operator-(const Dual& a) { return Dual(-a.value, -a.derivative); } template Dual operator+(const Dual& a, const Dual& b) { return Dual(a.value + b.value, a.derivative + b.derivative); } template Dual operator-(const Dual& a, const Dual& b) { return a + (-b); } template Dual operator+(const Dual& a, Scalar b) { return Dual(a.value + b, a.derivative); } template Dual operator-(const Dual& a, Scalar b) { return a + (-b); } template Dual operator+(Scalar a, const Dual& b) { return Dual(a + b.value, b.derivative); } template Dual operator-(Scalar a, const Dual& b) { return a + (-b); } template Dual operator*(const Dual& a, const Dual& b) { return Dual(a.value * b.value, a.value * b.derivative + b.value * a.derivative); } template Dual operator*(const Dual& a, Scalar b) { return Dual(a.value * b, a.derivative * b); } template Dual operator*(Scalar a, const Dual& b) { return b * a; } template Dual operator/(const Dual& a, const Dual& b) { const Scalar inv_b_real = Scalar(1) / b.value; return Dual(a.value * inv_b_real, a.derivative * inv_b_real - a.value * b.derivative * inv_b_real * inv_b_real); } template Dual operator/(const Dual& a, Scalar b) { return a * (Scalar(1) / b); } template Dual operator/(Scalar a, const Dual& b) { const Scalar inv_b_real = Scalar(1) / b.value; return Dual(a * inv_b_real, (-inv_b_real * a * inv_b_real) * b.derivative); } // basic math // sqrt template Dual sqrt(const Dual& a) { Scalar real = sqrt(a.value); return Dual(real, a.derivative * (Scalar(1) / (Scalar(2) * real))); } // abs template Dual abs(const Dual& a) { return a.value >= Scalar(0) ? a : -a; } // ceil template Dual ceil(const Dual& a) { return Dual(ceil(a.value)); } // floor template Dual floor(const Dual& a) { return Dual(floor(a.value)); } // fmod template Dual fmod(const Dual& a, Scalar mod) { return Dual(a.value - Scalar(size_t(a.value / mod)) * mod, a.derivative); } // max template Dual max(const Dual& a, const Dual& b) { return a.value >= b.value ? a : b; } // min template Dual min(const Dual& a, const Dual& b) { return a.value < b.value ? a : b; } // isnan template bool IsNan(Scalar a) { return isnan(a); } template bool IsNan(const Dual& a) { return IsNan(a.value); } // isfinite template bool IsFinite(Scalar a) { return isfinite(a); } template bool IsFinite(const Dual& a) { return IsFinite(a.value); } // isinf template bool IsInf(Scalar a) { return isinf(a); } template bool IsInf(const Dual& a) { return IsInf(a.value); } // trig // sin template Dual sin(const Dual& a) { return Dual(sin(a.value), cos(a.value) * a.derivative); } // cos template Dual cos(const Dual& a) { return Dual(cos(a.value), -sin(a.value) * a.derivative); } // tan template Dual tan(const Dual& a) { Scalar real = tan(a.value); return Dual(real, (Scalar(1) + real * real) * a.derivative); } // asin template Dual asin(const Dual& a) { Scalar asin_d = Scalar(1) / sqrt(Scalar(1) - a.value * a.value); return Dual(asin(a.value), asin_d * a.derivative); } // acos template Dual acos(const Dual& a) { Scalar acos_d = -Scalar(1) / sqrt(Scalar(1) - a.value * a.value); return Dual(acos(a.value), acos_d * a.derivative); } // atan template Dual atan(const Dual& a) { Scalar atan_d = Scalar(1) / (Scalar(1) + a.value * a.value); return Dual(atan(a.value), atan_d * a.derivative); } // atan2 template Dual atan2(const Dual& a, const Dual& b) { // derivative is equal to that of atan(a/b), so substitute a/b into atan and simplify Scalar atan_d = Scalar(1) / (a.value * a.value + b.value * b.value); return Dual(atan2(a.value, b.value), (a.derivative * b.value - a.value * b.derivative) * atan_d); } // retrieve the derivative elements of a vector of Duals into a matrix template Matrix collectDerivatives(const Matrix, M, 1>& input) { Matrix jac; for (size_t i = 0; i < M; i++) { jac.row(i) = input(i, 0).derivative; } return jac; } // retrieve the real (non-derivative) elements of a matrix of Duals into an equally sized matrix template Matrix collectReals(const Matrix, M, N>& input) { Matrix r; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { r(i,j) = input(i,j).value; } } return r; } #if defined(SUPPORT_STDIOSTREAM) template std::ostream& operator<<(std::ostream& os, const matrix::Dual& dual) { os << "["; os << std::setw(10) << dual.value << ";"; for (size_t j = 0; j < N; ++j) { os << "\t"; os << std::setw(10) << static_cast(dual.derivative(j)); } os << "]"; return os; } #endif // defined(SUPPORT_STDIOSTREAM) }