//===-- duals/dual - Dual number class --------------------------*- C++ -*-===// // // Part of the cppduals project. // https://tesch1.gitlab.io/cppduals // // (c)2019 Michael Tesch. tesch1@gmail.com // // See https://gitlab.com/tesch1/cppduals/blob/master/LICENSE.txt for // license information. // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef CPPDUALS_DUAL #define CPPDUALS_DUAL #ifndef PARSED_BY_DOXYGEN #include #include #include #include #include #include #include #endif #if !defined(CPPDUALS_IGNORE_COMPILER_VERSION) && !defined(_WIN32) #if __cplusplus < 201103L #error CPPDUALS needs at least a C++11 compliant compiler #endif #endif /// Configure whether system has POSIX extern int signgam; #ifndef CPPDUALS_HAVE_SIGNGAM #ifndef _WIN32 #ifndef __CYGWIN__ #define CPPDUALS_HAVE_SIGNGAM 1 #endif #endif #endif namespace duals { /** \file dual \brief Dual number class. \mainpage cppduals \ref duals/dual is a single-header [Dual number](https://en.wikipedia.org/wiki/Dual_number) C++ template library that implements numbers of the type \f$(a + b \cdot \epsilon)\f$ where \f$ \epsilon \ne 0\f$, and \f$\epsilon^2 = 0\f$. `duals::dual<>` can be used for "automatic" differentiation, it can also recursively nest with itself for higher orders of differentiation (ie for the second derivative use `duals::dual>`). It can also be used to differentiate parts of complex functions as `std::complex>`. This file can be used stand-alone for dual number support, but for Eigen vectorization support rather the file \ref duals/dual_eigen should be included. ``` #include using namespace duals::literals; template T f(T x) { return pow(x,pow(x,x)); } template T df(T x) { return pow(x,-1. + x + pow(x,x)) * (1. + x*log(x) + x*pow(log(x),2.)); } template T ddf(T x) { return (pow(x,pow(x,x)) * pow(pow(x,x - 1.) + pow(x,x)*log(x)*(log(x) + 1.), 2.) + pow(x,pow(x,x)) * (pow(x,x - 1.) * log(x) + pow(x,x - 1.) * (log(x) + 1.) + pow(x,x - 1.) * ((x - 1.)/x + log(x)) + pow(x,x) * log(x) * pow(log(x) + 1., 2.) )); } int main() { std::cout << " f(2.) = " << f(2.) << "\n"; std::cout << " df(2.) = " << df(2.) << "\n"; std::cout << "ddf(2.) = " << ddf(2.) << "\n"; std::cout << " f(2+1_e) = " << f(2+1_e) << "\n"; std::cout << " f(2+1_e).dpart() = " << f(2+1_e).dpart() << "\n"; duals::hyperduald x(2+1_e,1+0_e); std::cout << " f((2+1_e) + (1+0_e)_e).dpart().dpart() = " << f(x).dpart().dpart() << "\n"; } ``` Produces (notice the derivative in the dual-part): ``` f(2.) = 16 df(2.) = 107.11 ddf(2.) = 958.755 f(2+1_e) = (16+107.11_e) f(2+1_e).dpart() = 107.11 f((2+1_e) + (1+0_e)_e).dpart().dpart() = 958.755 ``` How this works can be seen by inspecting the infinite [Taylor series](https://en.wikipedia.org/wiki/Taylor_series) expansion of a function \f$ f(x) \f$ at \f$ a \f$ with \f$ x = a + b\epsilon \f$. The series truncates itself due to the property \f$ \epsilon^2 = 0 \f$, leaving the function's derivative at \f$ a \f$ in the "dual part" of the result (when \f$ b = 1 \f$): \f[ \begin{split} f(a + b \epsilon) &= f(a) + f'(a)(b \epsilon) + \frac{f''(a)}{2!}(b \epsilon)^2 + \frac{f'''(a)}{3!}(b \epsilon)^3 + \ldots \\ \ &= f(a) + f'(a)(b \epsilon) \end{split} \f] The class is contained in a single c++11 header `#include `, for extended [Eigen](http://eigen.tuxfamily.org) support, instead include the header \ref duals/dual_eigen "#include ". Type X in the templates below can be any value which can be assigned to value_type. Type X also indicates a limitation to dual numbers of the same depth but (possibly) different value_type as `duals::dual`. For example, you can assign (or add/sub/mul/div) `duals::dual` and `duals::dual`, but you can not assign `duals::dual>` to `duals::dual`. Here is a synopsis of the class: ``` namespace duals { template class dual { typedef T value_type; dual(const & re = T(), const & du = T()); dual(const dual &); template dual(const dual &); T rpart() const; T dpart() const; void rpart(T); void dpart(T); dual operator-() const; dual operator+() const; dual & operator= (const T &); dual & operator+=(const T &); dual & operator-=(const T &); dual & operator*=(const T &); dual & operator/=(const T &); dual & operator=(const dual &); template dual & operator= (const dual &); template dual & operator+=(const dual &); template dual & operator-=(const dual &); template dual & operator*=(const dual &); template dual & operator/=(const dual &); // The comparison operators are not strictly well-defined, // they are implemented as comparison of the real part. bool operator ==(const X &b) const; bool operator !=(const X &b) const; bool operator <(const X &b) const; bool operator >(const X &b) const; bool operator <=(const X &b) const; bool operator >=(const X &b) const; bool operator <(const dual &b) const; bool operator >(const dual &b) const; bool operator <=(const dual &b) const; bool operator >=(const dual &b) const; }; // Non-member functions: T rpart(dual) // Real part T dpart(dual) // Dual part dual dconj(dual) // Dual-conjugate dual random(dual low = {0,0}, dual high = {1,0}) dual exp(dual) dual log(dual) dual log10(dual) dual pow(dual, U) dual pow(U, dual) dual pow(dual, dual) dual sqrt(dual) dual cbrt(dual) dual sin(dual) dual cos(dual) dual tan(dual) dual asin(dual) dual acos(dual) dual atan(dual) // TODO: dual sinh(dual) dual cosh(dual) dual tanh(dual) dual asinh(dual) dual acosh(dual) dual atanh(dual) // Non-differentiable operations on the real part. T frexp(duals::dual arg, int* exp ); duals::dual ldexp(duals::dual arg, int exp ); T trunc(duals::dual d); T floor(duals::dual d); T ceil(duals::dual d); T round(duals::dual d); int fpclassify(duals::dual d); bool isfinite(duals::dual d); bool isnormal(duals::dual d); bool isinf(duals::dual d); bool isnan(duals::dual d); // Stream IO template operator>>(basic_istream &, dual &); template operator<<(basic_ostream &, const dual &); } ``` Some useful typedefs: ``` typedef dual dualf; typedef dual duald; typedef dual dualld; typedef dual hyperdualf; typedef dual hyperduald; typedef dual hyperdualld; typedef std::complex cdualf; typedef std::complex cduald; typedef std::complex cdualld; ``` There are also literals for dual parts defined in the inline namespace duals::literals. They are available with `using namespace duals;` or `using namespace duals::literals`. ``` using namespace duals::literals; dualf x = 3 + 4_ef; duald y = 3 + 4_e; dualld z = 3 + 4_el; ``` And in case you dislike iostreams, there are some formatters for the [`{fmt}`](https://github.com/fmtlib/fmt) formatting library. These are disabled by default, but can be enabled by `#define CPPDUALS_LIBFMT` for the `dual<>` formatter, and/or `#define CPPDUALS_LIBFMT_COMPLEX` for the std::complex<> formatter. There are three custom formatting flags that control how the dual numbers are printed, these must come before the normal `{fmt}` formatting spec: '$', '*', ','. For me these are about 3x faster than iostreams. ``` #define CPPDUALS_LIBFMT #define CPPDUALS_LIBFMT_COMPLEX #inlcude using namespace duals::literals; ... string s = fmt::format("{:}", 1 + 2_e); // s = "(1.0+2.0_e)" string s = fmt::format("{:g}", 1 + 2_e); // s = "(1+2_e)" string s = fmt::format("{:*}", 1 + 2_e); // s = "(1.0+2.0*e)" string s = fmt::format("{:,}", 1 + 2_e); // s = "(1.0,2.0)" string s = fmt::format("{:*,g}", complexd(1,2_e)); // s = "((1)+(0,2)*i)" ``` The "archaic Greek epsilon" logo is from [Wikimedia commons](https://commons.wikimedia.org/wiki/File:Greek_Epsilon_archaic.svg) Some casual reading material: - [ON QUATERNIONS, William Rowan Hamilton, Proceedings of the Royal Irish Academy, 3 (1847), pp. 1–16.](https://www.maths.tcd.ie/pub/HistMath/People/Hamilton/Quatern2/) - [Basic Space-Time Transformations Expressed by Means of Two-Component Number Systems](https://doi.org/10.12693%2Faphyspola.86.291) */ #ifndef PARSED_BY_DOXYGEN template class dual; #endif /// Check if T is dual<>, match non-duals. template struct is_dual : std::false_type {}; #ifndef PARSED_BY_DOXYGEN /// Check if T is dual<>, match dual<>. template struct is_dual > : std::true_type {}; #endif /// Check if T is std::complex<>, match non- std::complex<>. template struct is_complex : std::false_type {}; #ifndef PARSED_BY_DOXYGEN /// Check if T is std::complex<>, match std::complex<>. template struct is_complex > : std::true_type {}; #endif /// Dual_traits helper class. template struct dual_traits { /// Depth of T - for T=scalar this is 0. for dual_traits it /// is 1. enum { depth = 0 }; // -Wenum-compare /// The real storage type. typedef T real_type; }; #ifndef PARSED_BY_DOXYGEN /// dual_traits for dual<> types template struct dual_traits> { /// Depth to which this dual<> type is nested. One (1) is a /// first-level dual, whereas non-duals have a depth of 0. enum { depth = dual_traits::depth + 1 }; /// The real storage type. typedef typename dual_traits::real_type real_type; }; template struct dual_traits>> { /// complex> have the same 'depth' as their dual. enum { depth = dual_traits::depth }; /// The real storage type. typedef typename dual_traits::real_type real_type; }; namespace detail { template struct Void { typedef void type; }; template struct has_member_type : std::false_type {}; template struct has_member_type::type > : std::true_type { struct wrap { typedef typename T::type type; typedef typename T::type ReturnType; }; }; } // namespace detail /// Promote two types - default according to common_type template struct promote : std::common_type {}; /// Can types A and B be promoted to a common type? template using can_promote = detail::has_member_type>; template struct promote, dual, typename std::enable_if<(can_promote::value && (int)dual_traits::depth == (int)dual_traits::depth)>::type> { typedef dual::type> type; }; template struct promote, U, typename std::enable_if<(can_promote::value && (int)dual_traits::depth >= (int)dual_traits::depth && !is_complex::value)>::type> { typedef dual::type> type; }; template struct promote, typename std::enable_if<(can_promote::value && (int)dual_traits::depth >= (int)dual_traits::depth && !is_complex::value)>::type> { typedef dual::type> type; }; // ///////////////////////////////////////////////// template struct promote, std::complex, typename std::enable_if<(can_promote::value && (is_dual::value || is_dual::value))>::type> { typedef std::complex::type> type; }; template struct promote, U, typename std::enable_if<(can_promote::value && (is_dual::value || is_dual::value) && !is_complex::value)>::type> { typedef std::complex::type> type; }; template struct promote, typename std::enable_if<(can_promote::value && (is_dual::value || is_dual::value) && !is_complex::value)>::type> { typedef std::complex::type> type; }; // ///////////////////////////////////////////////// #endif // PARSED_BY_DOXYGEN } // namespace duals #ifndef PARSED_BY_DOXYGEN #define NOMACRO // thwart macroification #ifdef EIGEN_PI #define MY_PI EIGEN_PI #else #define MY_PI M_PI #endif #endif // PARSED_BY_DOXYGEN #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD #else namespace std { #endif #ifdef CPPDUALS_ENABLE_STD_IS_ARITHMETIC /// Duals are as arithmetic as their value_type is arithmetic. template struct is_arithmetic> : is_arithmetic {}; #endif // CPPDUALS_ENABLE_IS_ARITHMETIC /// Duals are compound types. template struct is_compound> : true_type {}; // Modification of std::numeric_limits<> per // C++03 17.4.3.1/1, and C++11 18.3.2.3/1. template struct numeric_limits> : numeric_limits { static constexpr bool is_specialized = true; static constexpr duals::dual min NOMACRO () { return numeric_limits::min NOMACRO (); } static constexpr duals::dual lowest() { return numeric_limits::lowest(); } static constexpr duals::dual max NOMACRO () { return numeric_limits::max NOMACRO (); } static constexpr duals::dual epsilon() { return numeric_limits::epsilon(); } static constexpr duals::dual round_error() { return numeric_limits::round_error(); } static constexpr duals::dual infinity() { return numeric_limits::infinity(); } static constexpr duals::dual quiet_NaN() { return numeric_limits::quiet_NaN(); } static constexpr duals::dual signaling_NaN() { return numeric_limits::signaling_NaN(); } static constexpr duals::dual denorm_min() { return numeric_limits::denorm_min(); } }; #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD #else } // namespace std #endif namespace duals { #ifndef PARSED_BY_DOXYGEN // T and X are wrapped in a dual<> #define CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X) \ typename std::enable_if<(int)duals::dual_traits::depth == \ (int)duals::dual_traits::depth, int>::type = 0, \ typename std::enable_if::value,int>::type = 0 // Both T and U are wrapped in a dual<> #define CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U) \ typename std::enable_if<(int)duals::dual_traits::depth == \ (int)duals::dual_traits::depth, int>::type = 0, \ typename std::enable_if::value,int>::type = 0, \ typename common_t = dual::type> // T is wrapped in a dual<>, U may or may not be. #define CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U) \ typename std::enable_if<((int)duals::dual_traits::depth >= \ (int)duals::dual_traits::depth), int>::type = 0, \ typename std::enable_if,U>::value,int>::type = 0, \ typename common_t = typename duals::promote, U>::type // T is wrapped in complex> #define CPPDUALS_ENABLE_LEQ_DEPTH_AND_CX_COMMON_T(T,U) \ typename std::enable_if<((int)duals::dual_traits::depth >= \ (int)duals::dual_traits::depth), int>::type = 0, \ typename std::enable_if>,U>::value,int>::type = 0, \ typename common_t = typename duals::promote >,U>::type #define CPPDUALS_ENABLE_IF(...) typename std::enable_if< (__VA_ARGS__) , int>::type = 0 #endif /*! \page user Background TODO: Add text here... */ /// Abstract dual number class. Can nest with other dual numbers and /// complex numbers. template class dual { public: /// Class type of rpart() and dpart(). This type can be nested /// dual<> or std::complex<>. typedef T value_type; private: /// The real part. value_type _real; /// The dual part. value_type _dual; public: /// Construct dual from optional real and dual parts. constexpr dual(const value_type re = value_type(), const value_type du = value_type()) : _real(re), _dual(du) {} /// Copy construct from a dual of equal depth. #pragma warning (disable : 4244) /* floats are not used in HICUM */ template::value)> dual(const dual & x) : _real(x.rpart()), _dual(x.dpart()) {} /// Cast to a complex> with real part equal to *this. operator std::complex>() { return std::complex>(*this); } /// Explicit cast to an arithmetic type retains the rpart() template ::value && !is_dual::value)> explicit operator X() const { return X(_real); } /// Get the real part. T rpart() const { return _real; } /// Get the dual part. T dpart() const { return _dual; } /// Set the real part. void rpart(value_type re) { _real = re; } /// Get the dual part. void dpart(value_type du) { _dual = du; } /// Unary negation dual operator-() const { return dual(-_real, -_dual); } /// Unary nothing dual operator+() const { return *this; } /// Assignment of `value_type` assigns the real part and zeros the dual part. dual & operator= (const T & x) { _real = x; _dual = value_type(); return *this; } /// Add a relatively-scalar to this dual. dual & operator+=(const T & x) { _real += x; return *this; } /// Subtract a relatively-scalar from this dual. dual & operator-=(const T & x) { _real -= x; return *this; } /// Multiply a relatively-scalar with this dual. dual & operator*=(const T & x) { _real *= x; _dual *= x; return *this; } /// Divide this dual by relatively-scalar. dual & operator/=(const T & x) { _real /= x; _dual /= x; return *this; } //dua & operator=(const dual & x) { _real = x.rpart(); _dual = x.dpart(); } template dual & operator= (const dual & x) { _real = x.rpart(); _dual = x.dpart(); return *this; } /// Add a dual of the same depth to this dual. template dual & operator+=(const dual & x) { _real += x.rpart(); _dual += x.dpart(); return *this; } /// Subtract a dual of the same depth from this dual. template dual & operator-=(const dual & x) { _real -= x.rpart(); _dual -= x.dpart(); return *this; } /// Multiply this dual with a dual of same of lower depth. template dual & operator*=(const dual & x) { _dual = _real * x.dpart() + _dual * x.rpart(); _real = _real * x.rpart(); return *this; } /// Divide this dual by another dual of the same or lower depth. template dual & operator/=(const dual & x) { _dual = (_dual * x.rpart() - _real * x.dpart()) / (x.rpart() * x.rpart()); _real = _real / x.rpart(); return *this; } //The following comparison operators are not strictly well-defined, //they are implemented as comparison of the real parts. #if 0 /// Compare this dual with another dual, comparing real parts for equality. template bool operator ==(const dual &b) const { return _real == b.rpart(); } /// Compare this dual with another dual, comparing real parts for inequality. template bool operator !=(const dual &b) const { return _real != b.rpart(); } /// Compare real part against real part of b template bool operator <(const dual &b) const { return _real < b.rpart(); } /// Compare real part against real part of b template bool operator >(const dual &b) const { return _real > b.rpart(); } /// Compare real part against real part of b template bool operator <=(const dual &b) const { return _real <= b.rpart(); } /// Compare real part against real part of b template bool operator >=(const dual &b) const { return _real >= b.rpart(); } #endif }; /// Get the dual's real part. template T rpart(const dual & x) { return x.rpart(); } /// Get the dual's dual part. template T dpart(const dual & x) { return x.dpart(); } /// R-part of complex> is non-dual complex<> (not to be confused with real()) template std::complex rpart(const std::complex> & x) { return std::complex(x.real().rpart(), x.imag().rpart()); } /// Dual part of complex> is complex<> template std::complex dpart(const std::complex> & x) { return std::complex(x.real().dpart(), x.imag().dpart()); } /// Get a non-dual's real part. template ::value && !std::is_compound::value) || is_complex::value)> T rpart(const T & x) { return x; } /// Get a non-dual's dual part := zero. template ::value && !std::is_compound::value) || is_complex::value)> T dpart(const T & ) { return T(0); } #ifndef PARSED_BY_DOXYGEN /// Dual +-*/ ops with another entity #define CPPDUALS_BINARY_OP(op) \ template \ common_t \ operator op(const dual & z, const dual & w) { \ common_t x(z); \ return x op##= w; \ } \ template>>::value)> \ common_t \ operator op(const dual & z, const U & w) { \ common_t x(z); \ return x op##= w; \ } \ template>>::value)> \ common_t \ operator op(const U & z, const dual & w) { \ common_t x(z); \ return x op##= w; \ } \ template>>::value)> \ common_t \ operator op(const std::complex> & z, const U & w) { \ common_t x(z); \ return x op##= w; \ } \ template>>::value)> \ common_t \ operator op(const U & z, const std::complex> & w) { \ common_t x(z); \ return x op##= w; \ } CPPDUALS_BINARY_OP(+) CPPDUALS_BINARY_OP(-) CPPDUALS_BINARY_OP(*) CPPDUALS_BINARY_OP(/) /// Dual compared to a non-complex lower rank thing #define CPPDUALS_LHS_COMPARISON(op) \ template \ bool \ operator op(const dual & a, const dual & b) { \ return a.rpart() op b.rpart(); \ } \ template::value)> \ bool \ operator op(const U & a, const dual & b) { \ return a op b.rpart(); \ } \ template::value)> \ bool \ operator op(const dual & a, const U & b) { \ return a.rpart() op b; \ } CPPDUALS_LHS_COMPARISON(<) CPPDUALS_LHS_COMPARISON(>) CPPDUALS_LHS_COMPARISON(<=) CPPDUALS_LHS_COMPARISON(>=) CPPDUALS_LHS_COMPARISON(==) CPPDUALS_LHS_COMPARISON(!=) #endif // PARSED_BY_DOXYGEN namespace randos { // Random real value between a and b. template, CPPDUALS_ENABLE_IF(!is_complex::value && !is_dual::value)> T random(T a = T(0), T b = T(1)) { static std::default_random_engine generator; dist distribution(a, b); return distribution(generator); } template, CPPDUALS_ENABLE_IF(!is_complex::value && !is_dual::value)> T random2(T a = T(0), T b = T(1)) { return random(a,b); } // Helper class for testing - also random value in dual part. template ::value)> DT random2(DT a = DT(0,0), DT b = DT(1,1)) { using randos::random; return DT(a.rpart() + random2() * (b.rpart() - a.rpart()), a.dpart() + random2() * (b.dpart() - a.dpart())); } // Helper class for testing - also random value in dual part of the complex. template::value)> CT random2(CT a = CT(0,0), CT b = CT(1,1)) { return CT(a.real() + random2() * (b.real() - a.real()), a.imag() + random2() * (b.imag() - a.imag())); } } // randos /// Random real and dual parts, used by Eigen's Random(), by default // the returned value has zero dual part and is in the range [0+0_e, // 1+0_e]. template , CPPDUALS_ENABLE_IF(is_dual
::value)> DT random(DT a = DT(0,0), DT b = DT(1,0)) { using randos::random; return DT(random(a.rpart(),b.rpart()), random(a.dpart(),b.dpart())); } /// Complex Conjugate of a dual is just the dual. template dual conj(const dual & x) { return x; } /// Conjugate a thing that's not dual and not complex -- it has no /// complex value so just return it. This is different from /// std::conj() which promotes the T to a std::complex. template::value && !is_complex::value && std::is_arithmetic::value)> T conj(const T & x) { return x; } /// Dual Conjugate, such that x*dconj(x) = rpart(x)^2. Different /// function name from complex conjugate conj(). template dual dconj(const dual & x) { return dual(x.rpart(), - x.dpart()); } /// Dual Conjugate of a complex, such that x*dconj(x) = rpart(x)^2. /// Different function name from complex conjugate conj(). template std::complex dconj(const std::complex & x) { return std::complex(dconj(x.real()), dconj(x.imag())); } /// Conjugate a thing that's not dual and not complex. template::value && !is_complex::value && std::is_arithmetic::value)> T dconj(const T & x) { return x; } /// Exponential e^x template dual exp(const dual & x) { using std::exp; T v = exp(x.rpart()); return dual(v, v * x.dpart()); } /// Natural log ln(x) template dual log(const dual & x) { using std::log; T v = log(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / x.rpart()); } template dual log10(const dual & x) { using std::log; return log(x) / log(static_cast(10)); } template dual log2(const dual & x) { using std::log; return log(x) / log(static_cast(2)); } template common_t pow(const dual & x, const dual & y) { using std::pow; using std::log; T v = pow(x.rpart(), y.rpart()); return common_t(v, x.dpart() * y.rpart() * pow(x.rpart(), y.rpart() - T(1)) + y.dpart() * log(x.rpart()) * v); } template common_t pow(const dual & x, const U & y) { using std::pow; return common_t(pow(x.rpart(), y), x.dpart() * y * pow(x.rpart(), y - U(1))); } template common_t pow(const U & x, const dual & y) { return pow(common_t(x), y); } namespace utils { template int sgn(T val) { return (T(0) < val) - (val < T(0)); } } template dual abs(const dual & x) { using std::abs; return dual(abs(x.rpart()), x.dpart() * utils::sgn(x.rpart())); } template dual fabs(const dual & x) { using std::fabs; return dual(fabs(x.rpart()), x.dpart() * utils::sgn(x.rpart())); } #if 0 template dual abs2(const dual & x) { using std::abs; return dual(x.rpart() * x.rpart(), xxx x.dpart() * utils::sgn(x.rpart())); } #endif template duals::dual copysign(const duals::dual & x, const duals::dual & y) { using std::copysign; T r = copysign(x.rpart(), y.rpart()); return duals::dual(r, r == x.rpart() ? x.dpart() : -x.dpart()); } template duals::dual hypot(const duals::dual & x, const duals::dual & y) { return sqrt(x*x + y*y); } template duals::dual scalbn(const duals::dual & arg, int ex) { return arg * std::pow((T)2, ex); } template duals::dual (fmax)(const duals::dual & x, const duals::dual & y) { return x.rpart() > y.rpart() ? x : y; } template duals::dual (fmin)(const duals::dual & x, const duals::dual & y) { return x.rpart() <= y.rpart() ? x : y; } template duals::dual logb(const duals::dual & x) { return duals::log2(x); } template int (fpclassify)(const duals::dual & d) { using std::fpclassify; return (fpclassify)(d.rpart()); } template bool (isfinite)(const duals::dual & d) { using std::isfinite; return (isfinite)(d.rpart()); } template bool (isnormal)(const duals::dual & d) { using std::isnormal; return (isnormal)(d.rpart()); } template bool (isinf)(const duals::dual & d) { using std::isinf; return (isinf)(d.rpart()); } template bool (isnan)(const duals::dual & d) { using std::isnan; return (isnan)(d.rpart()); } template bool (signbit)(const duals::dual & d) { using std::signbit; return (signbit)(d.rpart()); } template dual sqrt(const dual & x) { using std::sqrt; T v = sqrt(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / (T(2) * v) ); } template dual cbrt(const dual & x) { using std::cbrt; T v = cbrt(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / (T(3) * v * v) ); } template dual sin(const dual & x) { using std::sin; using std::cos; return dual(sin(x.rpart()), x.dpart() * cos(x.rpart())); } template dual cos(const dual & x) { using std::cos; using std::sin; return dual(cos(x.rpart()), -sin(x.rpart()) * x.dpart()); } template dual tan(const dual & x) { using std::tan; T v = tan(x.rpart()); return dual(v, x.dpart() * (v*v + 1)); } template dual asin(const dual & x) { using std::asin; using std::sqrt; T v = asin(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / sqrt(1 - x.rpart()*x.rpart())); } template dual acos(const dual & x) { using std::acos; using std::sqrt; T v = acos(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, -x.dpart() / sqrt(1 - x.rpart()*x.rpart())); } template dual atan(const dual & x) { using std::atan; T v = atan(x.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / (1 + x.rpart()*x.rpart())); } template dual atan2(const dual & x, const dual & y) { using std::atan2; T v = atan2(x.rpart(), y.rpart()); if (x.dpart() == T(0)) return v; else return dual(v, x.dpart() / (1 + x.rpart()*x.rpart())); } // TODO template dual sinh(const dual & x); template dual cosh(const dual & x); template dual tanh(const dual & x); template dual asinh(const dual & x); template dual acosh(const dual & x); template dual atanh(const dual & x); template dual log1p(const dual & x); template dual expm1(const dual & x); /// The error function. Make sure to `#include ` before /// `#include ` to use this function. template dual erf(const dual & x) { using std::erf; using std::sqrt; using std::pow; using std::exp; return dual(erf(x.rpart()), x.dpart() * T(2)/sqrt(T(MY_PI)) * exp(-pow(x.rpart(),T(2)))); } /// Error function complement (1 - erf()). template dual erfc(const dual & x) { using std::erfc; using std::sqrt; using std::pow; using std::exp; return dual(erfc(x.rpart()), x.dpart() * -T(2)/sqrt(T(MY_PI)) * exp(-pow(x.rpart(),T(2)))); } /// Gamma function. Approximation of the dual part. // TODO specialize for integers template dual tgamma(const dual & x) { using std::tgamma; T v = tgamma(x.rpart()); if (x.dpart() == T(0)) return v; else { int errno_saved = errno; T h(T(1) / (1ull << (std::numeric_limits::digits / 3))); T w((tgamma(x.rpart()+h) - tgamma(x.rpart()-h))/(2*h)); errno = errno_saved; return dual(v, x.dpart() * w); } } /// Log of absolute value of gamma function. Approximation of the dual part. template dual lgamma(const dual & x) { using std::lgamma; T v = lgamma(x.rpart()); if (x.dpart() == T(0)) return v; else { #if CPPDUALS_HAVE_SIGNGAM int signgam_saved = signgam; #endif int errno_saved = errno; T h(T(1) / (1ull << (std::numeric_limits::digits / 3))); T w((lgamma(x.rpart()+h) - lgamma(x.rpart()-h))/(2*h)); #if CPPDUALS_HAVE_SIGNGAM signgam = signgam_saved; #endif errno = errno_saved; return dual(v, x.dpart() * w); } } /// Putto operator template std::basic_ostream<_CharT, _Traits> & operator<<(std::basic_ostream<_CharT, _Traits> & os, const dual & x) { std::basic_ostringstream<_CharT, _Traits> s; s.flags(os.flags()); s.imbue(os.getloc()); s.precision(os.precision()); s << '(' << x.rpart() << (x.dpart() < 0 ? "" : "+") << x.dpart() << "_e" << (std::is_same::type,float>::value ? "f" : std::is_same::type,double>::value ? "" : std::is_same::type,long double>::value ? "l" : "") << ")"; return os << s.str(); } /// Stream reader template std::basic_istream & operator>>(std::basic_istream & is, dual & x) { if (is.good()) { ws(is); if (is.peek() == CharT('(')) { is.get(); T r; is >> r; if (!is.fail()) { CharT c = is.peek(); if (c != CharT('_')) { ws(is); c = is.peek(); } if (c == CharT('+') || c == CharT('-') || c == CharT('_')) { if (c == CharT('+')) is.get(); T d; if (c == CharT('_')) { d = r; r = 0; } else is >> d; if (!is.fail()) { ws(is); c = is.peek(); if (c == CharT('_')) { is.get(); c = is.peek(); if (c == CharT('e')) { is.get(); c = is.peek(); if ((c == 'f' && !std::is_same::type,float>::value) || (c == 'l' && !std::is_same::type,long double>::value)) is.setstate(std::ios_base::failbit); else { if (c == 'f' || c == 'l') is.get(); ws(is); c = is.peek(); if (c == ')') { is.get(); x = dual(r, d); } else is.setstate(std::ios_base::failbit); } } else is.setstate(std::ios_base::failbit); } else is.setstate(std::ios_base::failbit); } else is.setstate(std::ios_base::failbit); } else if (c == CharT(')')) { is.get(); x = dual(r, T(0)); } else is.setstate(std::ios_base::failbit); } else is.setstate(std::ios_base::failbit); } else { T r; is >> r; if (!is.fail()) x = dual(r, T(0)); else is.setstate(std::ios_base::failbit); } } else is.setstate(std::ios_base::failbit); return is; } #if __cpp_user_defined_literals >= 200809 /// Dual number literals in namespace duals::literals inline namespace literals { using duals::dual; constexpr dual operator""_ef(long double du) { return { 0.0f, static_cast(du) }; } constexpr dual operator""_ef(unsigned long long du) { return { 0.0f, static_cast(du) }; } constexpr dual operator""_e(long double du) { return { 0.0, static_cast(du) }; } constexpr dual operator""_e(unsigned long long du) { return { 0.0, static_cast(du) }; } constexpr dual operator""_el(long double du) { return { 0.0l, du }; } constexpr dual operator""_el(unsigned long long du) { return { 0.0l, static_cast(du) }; } } #endif typedef dual dualf; typedef dual duald; typedef dual dualld; typedef dual hyperdualf; typedef dual hyperduald; typedef dual hyperdualld; typedef std::complex cdualf; typedef std::complex cduald; typedef std::complex cdualld; } // namespace duals #ifdef CPPDUALS_LIBFMT #include /// duals::dual<> Formatter for libfmt https://github.com/fmtlib/fmt /// /// Formats a dual number (r,d) as (r+d_e), offering the same /// formatting options as the underlying type - with the addition of /// three optional format options, only one of which may appear /// directly after the ':' in the format spec: '$', '*', and ',". The /// '*' flag changes the separating _ to a *, producing (r+d*e), where /// r and d are the formatted value_type values. The ',' flag simply /// prints the real and dual parts separated by a comma. As a /// concrete exmple, this formatter can produce either (3+5.4_e) or /// (3+5.4*e) or (3,5.4) for a dual using the specs {:g}, /// {:*g}, or {:,g}, respectively. When the '*' is NOT specified, the /// output should be compatible with the input operator>> and the dual /// literals below. (this implementation is a bit hacky - glad for /// cleanups). template struct fmt::formatter,Char> : public fmt::formatter { typedef fmt::formatter base; enum style { expr, star, pair } style_ = expr; internal::dynamic_format_specs specs_; FMT_CONSTEXPR auto parse(format_parse_context & ctx) -> decltype(ctx.begin()) { using handler_type = internal::dynamic_specs_handler; auto type = internal::type_constant::value; internal::specs_checker handler(handler_type(specs_, ctx), type); auto it = ctx.begin(); switch (*it) { case '$': style_ = style::expr; ctx.advance_to(++it); break; case '*': style_ = style::star; ctx.advance_to(++it); break; case ',': style_ = style::pair; ctx.advance_to(++it); break; default: break; } parse_format_specs(ctx.begin(), ctx.end(), handler); return base::parse(ctx); } template auto format(const duals::dual & x, FormatCtx & ctx) -> decltype(ctx.out()) { format_to(ctx.out(), "("); if (style_ == style::pair) { base::format(x.rpart(), ctx); format_to(ctx.out(), ","); base::format(x.dpart(), ctx); return format_to(ctx.out(), ")"); } if (x.rpart() || !x.dpart()) base::format(x.rpart(), ctx); if (x.dpart()) { if (x.rpart() && x.dpart() >= 0 && specs_.sign != sign::plus) format_to(ctx.out(), "+"); base::format(x.dpart(), ctx); if (style_ == style::star) format_to(ctx.out(), "*e"); else format_to(ctx.out(), "_e"); if (std::is_same::type,float>::value) format_to(ctx.out(), "f"); if (std::is_same::type,long double>::value) format_to(ctx.out(), "l"); } return format_to(ctx.out(), ")"); } }; #endif #ifdef CPPDUALS_LIBFMT_COMPLEX #ifndef CPPDUALS_LIBFMT #include #endif /// std::complex<> Formatter for libfmt https://github.com/fmtlib/fmt /// /// libfmt does not provide a formatter for std::complex<>, although /// one is proposed for c++20. Anyway, at the expense of a k or two, /// you can define CPPDUALS_LIBFMT_COMPLEX and get this one. /// /// The standard iostreams formatting of complex numbers is (a,b), /// where a and b are the real and imaginary parts. This formats a /// complex number (a+bi) as (a+bi), offering the same formatting /// options as the underlying type - with the addition of three /// optional format options, only one of which may appear directly /// after the ':' in the format spec (before any fill or align): '$' /// (the default if no flag is specified), '*', and ',". The '*' flag /// adds a * before the 'i', producing (a+b*i), where a and b are the /// formatted value_type values. The ',' flag simply prints the real /// and complex parts separated by a comma (same as iostreams' format). /// As a concrete exmple, this formatter can produce either (3+5.4i) /// or (3+5.4*i) or (3,5.4) for a complex using the specs {:g} /// | {:$g}, {:*g}, or {:,g}, respectively. (this implementation is a /// bit hacky - glad for cleanups). /// template struct fmt::formatter,Char> : public fmt::formatter { typedef fmt::formatter base; enum style { expr, star, pair } style_ = expr; internal::dynamic_format_specs specs_; FMT_CONSTEXPR auto parse(format_parse_context & ctx) -> decltype(ctx.begin()) { using handler_type = internal::dynamic_specs_handler; auto type = internal::type_constant::value; internal::specs_checker handler(handler_type(specs_, ctx), type); auto it = ctx.begin(); switch (*it) { case '$': style_ = style::expr; ctx.advance_to(++it); break; case '*': style_ = style::star; ctx.advance_to(++it); break; case ',': style_ = style::pair; ctx.advance_to(++it); break; default: break; } parse_format_specs(ctx.begin(), ctx.end(), handler); //todo: fixup alignment return base::parse(ctx); } template auto format(const std::complex & x, FormatCtx & ctx) -> decltype(ctx.out()) { format_to(ctx.out(), "("); if (style_ == style::pair) { base::format(x.real(), ctx); format_to(ctx.out(), ","); base::format(x.imag(), ctx); return format_to(ctx.out(), ")"); } if (x.real() || !x.imag()) base::format(x.real(), ctx); if (x.imag()) { if (x.real() && x.imag() >= 0 && specs_.sign != sign::plus) format_to(ctx.out(), "+"); base::format(x.imag(), ctx); if (style_ == style::star) format_to(ctx.out(), "*i"); else format_to(ctx.out(), "i"); if (std::is_same::type,float>::value) format_to(ctx.out(), "f"); if (std::is_same::type,long double>::value) format_to(ctx.out(), "l"); } return format_to(ctx.out(), ")"); } }; #endif #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD #else namespace std { #endif #ifndef PARSED_BY_DOXYGEN #define make_math(T) \ inline T (frexp)(const duals::dual & arg, int* exp ) { return (frexp)(arg.rpart(), exp); } \ inline duals::dual (ldexp)(const duals::dual & arg, int exp ) { return arg * std::pow((T)2,exp); } \ inline T (trunc)(const duals::dual & d) { return (trunc)(d.rpart()); } \ inline T (floor)(const duals::dual & d) { return (floor)(d.rpart()); } \ inline T (ceil)(const duals::dual & d) { return (ceil)(d.rpart()); } \ inline T (round)(const duals::dual & d) { return (round)(d.rpart()); } \ inline int (fpclassify)(const duals::dual & d) { return (fpclassify)(d.rpart()); } \ inline bool (isfinite)(const duals::dual & d) { return (isfinite)(d.rpart()); } \ inline bool (isnormal)(const duals::dual & d) { return (isnormal)(d.rpart()); } \ inline bool (isinf)(const duals::dual & d) { return (isinf)(d.rpart()); } \ inline bool (isnan)(const duals::dual & d) { return (isnan)(d.rpart()); } \ make_math(float) make_math(double) make_math(long double) #undef make_math #endif // PARSED_BY_DOXYGEN #ifdef _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD #else } // namespace std #endif #endif // CPPDUALS_DUAL