#ifndef FILE_AUTODIFFDIFF #define FILE_AUTODIFFDIFF /**************************************************************************/ /* File: autodiffdiff.hpp */ /* Author: Joachim Schoeberl */ /* Date: 13. June. 05 */ /**************************************************************************/ namespace ngcore { using ngcore::IfPos; // Automatic second differentiation datatype /** Datatype for automatic differentiation. Contains function value, D first derivatives, and D*D second derivatives. Algebraic operations are overloaded by using product-rule etc. etc. **/ template class AutoDiffDiff { SCAL val; SCAL dval[D?D:1]; SCAL ddval[D?D*D:1]; public: typedef AutoDiffDiff TELEM; /// elements are undefined AutoDiffDiff () throw() { ; } /// copy constructor AutoDiffDiff (const AutoDiffDiff & ad2) throw() { val = ad2.val; for (int i = 0; i < D; i++) dval[i] = ad2.dval[i]; for (int i = 0; i < D*D; i++) ddval[i] = ad2.ddval[i]; } /// initial object with constant value AutoDiffDiff (SCAL aval) throw() { val = aval; for (int i = 0; i < D; i++) dval[i] = 0; for (int i = 0; i < D*D; i++) ddval[i] = 0; } /// initial object with value and derivative AutoDiffDiff (const AutoDiff & ad2) throw() { val = ad2.Value(); for (int i = 0; i < D; i++) dval[i] = ad2.DValue(i); for (int i = 0; i < D*D; i++) ddval[i] = 0; } /// init object with (val, e_diffindex) AutoDiffDiff (SCAL aval, int diffindex) throw() { val = aval; for (int i = 0; i < D; i++) dval[i] = 0; for (int i = 0; i < D*D; i++) ddval[i] = 0; dval[diffindex] = 1; } NETGEN_INLINE AutoDiffDiff (SCAL aval, const SCAL * grad) { val = aval; LoadGradient (grad); for (int i = 0; i < D*D; i++) ddval[i] = 0; } NETGEN_INLINE AutoDiffDiff (SCAL aval, const SCAL * grad, const SCAL * hesse) { val = aval; LoadGradient (grad); LoadHessian (hesse); } /// assign constant value AutoDiffDiff & operator= (SCAL aval) throw() { val = aval; for (int i = 0; i < D; i++) dval[i] = 0; for (int i = 0; i < D*D; i++) ddval[i] = 0; return *this; } NETGEN_INLINE void StoreGradient (SCAL * p) const { for (int i = 0; i < D; i++) p[i] = dval[i]; } NETGEN_INLINE void LoadGradient (const SCAL * p) { for (int i = 0; i < D; i++) dval[i] = p[i]; } NETGEN_INLINE void StoreHessian (SCAL * p) const { for (int i = 0; i < D*D; i++) p[i] = ddval[i]; } NETGEN_INLINE void LoadHessian (const SCAL * p) { for (int i = 0; i < D*D; i++) ddval[i] = p[i]; } /// returns value SCAL Value() const throw() { return val; } /// returns partial derivative SCAL DValue (int i) const throw() { return dval[i]; } AutoDiff DValueAD (int i) const { AutoDiff r(dval[i]); for (int j = 0; j < D; j++) r.DValue(j) = ddval[i*D+j]; return r; } /// returns partial derivative SCAL DDValue (int i) const throw() { return ddval[i]; } /// returns partial derivative SCAL DDValue (int i, int j) const throw() { return ddval[i*D+j]; } /// access value SCAL & Value() throw() { return val; } /// accesses partial derivative SCAL & DValue (int i) throw() { return dval[i]; } /// accesses partial derivative SCAL & DDValue (int i) throw() { return ddval[i]; } /// accesses partial derivative SCAL & DDValue (int i, int j) throw() { return ddval[i*D+j]; } explicit operator AutoDiff () const { return AutoDiff (val, &dval[0]); } /// add autodiffdiff object AutoDiffDiff & operator+= (const AutoDiffDiff & y) throw() { val += y.val; for (int i = 0; i < D; i++) dval[i] += y.dval[i]; for (int i = 0; i < D*D; i++) ddval[i] += y.ddval[i]; return *this; } /// subtract autodiffdiff object AutoDiffDiff & operator-= (const AutoDiffDiff & y) throw() { val -= y.val; for (int i = 0; i < D; i++) dval[i] -= y.dval[i]; for (int i = 0; i < D*D; i++) ddval[i] -= y.ddval[i]; return *this; } /// multiply with autodiffdiff object AutoDiffDiff & operator*= (const AutoDiffDiff & y) throw() { for (int i = 0; i < D*D; i++) ddval[i] = val * y.ddval[i] + y.val * ddval[i]; for (int i = 0; i < D; i++) for (int j = 0; j < D; j++) ddval[i*D+j] += dval[i] * y.dval[j] + dval[j] * y.dval[i]; for (int i = 0; i < D; i++) { dval[i] *= y.val; dval[i] += val * y.dval[i]; } val *= y.val; return *this; } /// multiply with scalar AutoDiffDiff & operator*= (const SCAL & y) throw() { for ( int i = 0; i < D*D; i++ ) ddval[i] *= y; for (int i = 0; i < D; i++) dval[i] *= y; val *= y; return *this; } /// divide by scalar AutoDiffDiff & operator/= (const SCAL & y) throw() { SCAL iy = 1.0 / y; for ( int i = 0; i < D*D; i++ ) ddval[i] *= iy; for (int i = 0; i < D; i++) dval[i] *= iy; val *= iy; return *this; } /// same value bool operator== (SCAL val2) throw() { return val == val2; } /// different values bool operator!= (SCAL val2) throw() { return val != val2; } /// less bool operator< (SCAL val2) throw() { return val < val2; } /// greater bool operator> (SCAL val2) throw() { return val > val2; } }; //@{ AutoDiff helper functions. /// Prints AudoDiffDiff template inline ostream & operator<< (ostream & ost, const AutoDiffDiff & x) { ost << x.Value() << ", D = "; for (int i = 0; i < D; i++) ost << x.DValue(i) << " "; ost << ", DD = "; for (int i = 0; i < D*D; i++) ost << x.DDValue(i) << " "; return ost; } /// template inline AutoDiffDiff operator+ (const AutoDiffDiff & x, const AutoDiffDiff & y) throw() { AutoDiffDiff res; res.Value () = x.Value()+y.Value(); for (int i = 0; i < D; i++) res.DValue(i) = x.DValue(i) + y.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = x.DDValue(i) + y.DDValue(i); return res; } /// template inline AutoDiffDiff operator- (const AutoDiffDiff & x, const AutoDiffDiff & y) throw() { AutoDiffDiff res; res.Value() = x.Value()-y.Value(); for (int i = 0; i < D; i++) res.DValue(i) = x.DValue(i) - y.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = x.DDValue(i) - y.DDValue(i); return res; } /// template::value, int>::type = 0> inline AutoDiffDiff operator+ (SCAL2 x, const AutoDiffDiff & y) throw() { AutoDiffDiff res; res.Value() = x+y.Value(); for (int i = 0; i < D; i++) res.DValue(i) = y.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = y.DDValue(i); return res; } /// template::value, int>::type = 0> inline AutoDiffDiff operator+ (const AutoDiffDiff & y, SCAL2 x) throw() { AutoDiffDiff res; res.Value() = x+y.Value(); for (int i = 0; i < D; i++) res.DValue(i) = y.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = y.DDValue(i); return res; } /// template inline AutoDiffDiff operator- (const AutoDiffDiff & x) throw() { AutoDiffDiff res; res.Value() = -x.Value(); for (int i = 0; i < D; i++) res.DValue(i) = -x.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = -x.DDValue(i); return res; } /// template::value, int>::type = 0> inline AutoDiffDiff operator- (const AutoDiffDiff & x, SCAL2 y) throw() { AutoDiffDiff res; res.Value() = x.Value()-y; for (int i = 0; i < D; i++) res.DValue(i) = x.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = x.DDValue(i); return res; } /// template::value, int>::type = 0> inline AutoDiffDiff operator- (SCAL2 x, const AutoDiffDiff & y) throw() { AutoDiffDiff res; res.Value() = x-y.Value(); for (int i = 0; i < D; i++) res.DValue(i) = -y.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = -y.DDValue(i); return res; } /// template::value, int>::type = 0> inline AutoDiffDiff operator* (SCAL2 x, const AutoDiffDiff & y) throw() { AutoDiffDiff res; res.Value() = x*y.Value(); for (int i = 0; i < D; i++) res.DValue(i) = x*y.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = x*y.DDValue(i); return res; } /// template::value, int>::type = 0> inline AutoDiffDiff operator* (const AutoDiffDiff & y, SCAL2 x) throw() { AutoDiffDiff res; res.Value() = x*y.Value(); for (int i = 0; i < D; i++) res.DValue(i) = x*y.DValue(i); for (int i = 0; i < D*D; i++) res.DDValue(i) = x*y.DDValue(i); return res; } /// template inline AutoDiffDiff operator* (const AutoDiffDiff & x, const AutoDiffDiff & y) throw() { AutoDiffDiff res; SCAL hx = x.Value(); SCAL hy = y.Value(); res.Value() = hx*hy; for (int i = 0; i < D; i++) res.DValue(i) = hx*y.DValue(i) + hy*x.DValue(i); for (int i = 0; i < D; i++) for (int j = 0; j < D; j++) res.DDValue(i,j) = hx * y.DDValue(i,j) + hy * x.DDValue(i,j) + x.DValue(i) * y.DValue(j) + x.DValue(j) * y.DValue(i); return res; } template inline AutoDiffDiff Inv (const AutoDiffDiff & x) { AutoDiffDiff res(1.0 / x.Value()); for (int i = 0; i < D; i++) res.DValue(i) = -x.DValue(i) / (x.Value() * x.Value()); SCAL fac1 = 2/(x.Value()*x.Value()*x.Value()); SCAL fac2 = 1/sqr(x.Value()); for (int i = 0; i < D; i++) for (int j = 0; j < D; j++) res.DDValue(i,j) = fac1*x.DValue(i)*x.DValue(j) - fac2*x.DDValue(i,j); return res; } template inline AutoDiffDiff operator/ (const AutoDiffDiff & x, const AutoDiffDiff & y) { return x * Inv (y); } template::value, int>::type = 0> inline AutoDiffDiff operator/ (const AutoDiffDiff & x, SCAL2 y) { return (1/y) * x; } template::value, int>::type = 0> inline AutoDiffDiff operator/ (SCAL2 x, const AutoDiffDiff & y) { return x * Inv(y); } template inline AutoDiffDiff sqrt (const AutoDiffDiff & x) { AutoDiffDiff res; res.Value() = sqrt(x.Value()); for (int j = 0; j < D; j++) res.DValue(j) = IfZero(x.DValue(j),SCAL{0.},0.5 / res.Value() * x.DValue(j)); for (int i = 0; i < D; i++) for (int j = 0; j < D; j++) res.DDValue(i,j) = IfZero(x.DDValue(i,j)+x.DValue(i) * x.DValue(j),SCAL{0.},0.5/res.Value() * x.DDValue(i,j) - 0.25 / (x.Value()*res.Value()) * x.DValue(i) * x.DValue(j)); return res; } // df(u)/dx = exp(x) * du/dx // d^2 f(u) / dx^2 = exp(x) * (du/dx)^2 + exp(x) * d^2u /dx^2 template NETGEN_INLINE AutoDiffDiff exp (AutoDiffDiff x) { AutoDiffDiff res; res.Value() = exp(x.Value()); for (int k = 0; k < D; k++) res.DValue(k) = x.DValue(k) * res.Value(); for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = (x.DValue(k) * x.DValue(l)+x.DDValue(k,l)) * res.Value(); return res; } using std::pow; template NETGEN_INLINE AutoDiffDiff pow (AutoDiffDiff x, AutoDiffDiff y ) { return exp(log(x)*y); } template NETGEN_INLINE AutoDiffDiff log (AutoDiffDiff x) { AutoDiffDiff res; res.Value() = log(x.Value()); SCAL xinv = 1.0/x.Value(); for (int k = 0; k < D; k++) res.DValue(k) = x.DValue(k) * xinv; for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = -xinv*xinv*x.DValue(k) * x.DValue(l) + xinv * x.DDValue(k,l); return res; } template NETGEN_INLINE AutoDiffDiff sin (AutoDiffDiff x) { AutoDiffDiff res; SCAL s = sin(x.Value()); SCAL c = cos(x.Value()); res.Value() = s; for (int k = 0; k < D; k++) res.DValue(k) = x.DValue(k) * c; for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = -s * x.DValue(k) * x.DValue(l) + c * x.DDValue(k,l); return res; } template NETGEN_INLINE AutoDiffDiff cos (AutoDiffDiff x) { AutoDiffDiff res; SCAL s = sin(x.Value()); SCAL c = cos(x.Value()); res.Value() = c; for (int k = 0; k < D; k++) res.DValue(k) = -s * x.DValue(k); for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = -c * x.DValue(k) * x.DValue(l) - s * x.DDValue(k,l); return res; } template NETGEN_INLINE AutoDiffDiff tan (AutoDiffDiff x) { return sin(x) / cos(x); } template NETGEN_INLINE AutoDiffDiff atan (AutoDiffDiff x) { AutoDiffDiff res; SCAL a = atan(x.Value()); res.Value() = a; for (int k = 0; k < D; k++) res.DValue(k) = x.DValue(k)/(1+x.Value()*x.Value()) ; for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = -2*x.Value()/((1+x.Value()*x.Value())*(1+x.Value()*x.Value())) * x.DValue(k) * x.DValue(l) + x.DDValue(k,l)/(1+x.Value()*x.Value()); return res; } template NETGEN_INLINE AutoDiffDiff atan2 (AutoDiffDiff x,AutoDiffDiff y) { AutoDiffDiff res; SCAL a = atan2(x.Value(), y.Value()); res.Value() = a; for (int k = 0; k < D; k++) res.DValue(k) = (x.Value()*y.DValue(k)-y.Value()*x.DValue(k))/(y.Value()*y.Value()+x.Value()*x.Value()); for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = (x.DValue(k)*y.DValue(l)+x.Value()*y.DDValue(l,k) - y.DValue(k)*x.DValue(l) - y.Value()*x.DDValue(l,k))/(y.Value()*y.Value()+x.Value()*x.Value()) - 2 * (x.Value()*y.DValue(k)-y.Value()*x.DValue(k)) * (x.Value()*x.DValue(k) + y.Value()*y.DValue(k))/( (y.Value()*y.Value()+x.Value()*x.Value()) * (y.Value()*y.Value()+x.Value()*x.Value()) ); return res; } using std::acos; template NETGEN_INLINE AutoDiffDiff acos (AutoDiffDiff x) { AutoDiffDiff res; SCAL a = acos(x.Value()); res.Value() = a; auto omaa = 1-x.Value()*x.Value(); auto s = sqrt(omaa); SCAL da = -1 / s; SCAL dda = -x.Value() / (s*omaa); for (int k = 0; k < D; k++) res.DValue(k) = x.DValue(k)*da; for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = dda * x.DValue(k) * x.DValue(l) + da * x.DDValue(k,l); return res; } using std::acos; template NETGEN_INLINE AutoDiffDiff asin (AutoDiffDiff x) { AutoDiffDiff res; SCAL a = asin(x.Value()); res.Value() = a; auto omaa = 1-x.Value()*x.Value(); auto s = sqrt(omaa); SCAL da = 1 / s; SCAL dda = x.Value() / (s*omaa); for (int k = 0; k < D; k++) res.DValue(k) = x.DValue(k)*da; for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = dda * x.DValue(k) * x.DValue(l) + da * x.DDValue(k,l); return res; } template NETGEN_INLINE AutoDiffDiff sinh (AutoDiffDiff x) { AutoDiffDiff res; SCAL sh = sinh(x.Value()); SCAL ch = cosh(x.Value()); res.Value() = sh; for (int k = 0; k < D; k++) res.DValue(k) = x.DValue(k) * ch; for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = sh * x.DValue(k) * x.DValue(l) + ch * x.DDValue(k,l); return res; } template NETGEN_INLINE AutoDiffDiff cosh (AutoDiffDiff x) { AutoDiffDiff res; SCAL sh = sinh(x.Value()); SCAL ch = cosh(x.Value()); res.Value() = ch; for (int k = 0; k < D; k++) res.DValue(k) = sh * x.DValue(k); for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = ch * x.DValue(k) * x.DValue(l) + sh * x.DDValue(k,l); return res; } template NETGEN_INLINE AutoDiffDiff erf (AutoDiffDiff x) { AutoDiffDiff res; SCAL derf = 2. / sqrt(M_PI) * exp(- x.Value() * x.Value()); res.Value() = erf(x.Value()); for (int k = 0; k < D; k++) res.DValue(k) = - derf * x.DValue(k); for (int k = 0; k < D; k++) for (int l = 0; l < D; l++) res.DDValue(k,l) = derf * (x.DDValue(k, l) - 2 * x.Value() * x.DValue(k) * x.DValue(l)); return res; } using std::floor; template NETGEN_INLINE AutoDiffDiff floor (const AutoDiffDiff & x) { return floor(x.Value()); } using std::ceil; template NETGEN_INLINE AutoDiffDiff ceil (const AutoDiffDiff & x) { return ceil(x.Value()); } template auto IfPos (AutoDiffDiff a, TB b, TC c) -> decltype(IfPos (a.Value(), b, c)) { return IfPos (a.Value(), b, c); } template NETGEN_INLINE AutoDiffDiff IfPos (SCAL /* SIMD */ a, AutoDiffDiff b, AutoDiffDiff c) { AutoDiffDiff res; res.Value() = IfPos (a, b.Value(), c.Value()); for (int j = 0; j < D; j++) { res.DValue(j) = IfPos (a, b.DValue(j), c.DValue(j)); res.DDValue(j) = IfPos (a, b.DDValue(j), c.DDValue(j)); } return res; } template NETGEN_INLINE AutoDiffDiff IfPos (SCAL /* SIMD */ a, AutoDiffDiff b, TC c) { return IfPos (a, b, AutoDiffDiff (c)); } //@} } namespace ngbla { template struct is_scalar_type; template struct is_scalar_type> { static constexpr bool value = true; }; // not meaningful for AutoDiff, since this is // not (complex) differentiable anyway template inline auto L2Norm2 (const ngcore::AutoDiffDiff & x) { return x*x; } } #endif