You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1387 lines
48 KiB
1387 lines
48 KiB
//===-- 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 <cmath>
|
|
#include <ctgmath>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
#include <complex>
|
|
#include <random>
|
|
#include <iostream>
|
|
#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<duals::dual<T>>`). It
|
|
can also be used to differentiate parts of complex functions as
|
|
`std::complex<duals::dual<T>>`. 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 <duals/dual>
|
|
|
|
using namespace duals::literals;
|
|
|
|
template <class T> T f(T x) { return pow(x,pow(x,x)); }
|
|
template <class T> T df(T x) { return pow(x,-1. + x + pow(x,x)) * (1. + x*log(x) + x*pow(log(x),2.)); }
|
|
template <class T> 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
|
|
<duals/dual>`, for extended [Eigen](http://eigen.tuxfamily.org)
|
|
support, instead include the header \ref duals/dual_eigen "#include <duals/dual_eigen>".
|
|
|
|
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<T>`. For example,
|
|
you can assign (or add/sub/mul/div) `duals::dual<float>` and
|
|
`duals::dual<double>`, but you can not assign
|
|
`duals::dual<duals::dual<float>>` to `duals::dual<float>`.
|
|
|
|
Here is a synopsis of the class:
|
|
|
|
```
|
|
namespace duals {
|
|
|
|
template<class T> class dual {
|
|
|
|
typedef T value_type;
|
|
|
|
dual(const & re = T(), const & du = T());
|
|
dual(const dual &);
|
|
template<class X> dual(const dual<X> &);
|
|
|
|
T rpart() const;
|
|
T dpart() const;
|
|
|
|
void rpart(T);
|
|
void dpart(T);
|
|
|
|
dual<T> operator-() const;
|
|
dual<T> operator+() const;
|
|
|
|
dual<T> & operator= (const T &);
|
|
dual<T> & operator+=(const T &);
|
|
dual<T> & operator-=(const T &);
|
|
dual<T> & operator*=(const T &);
|
|
dual<T> & operator/=(const T &);
|
|
|
|
dual<T> & operator=(const dual<T> &);
|
|
template<class X> dual<T> & operator= (const dual<X> &);
|
|
template<class X> dual<T> & operator+=(const dual<X> &);
|
|
template<class X> dual<T> & operator-=(const dual<X> &);
|
|
template<class X> dual<T> & operator*=(const dual<X> &);
|
|
template<class X> dual<T> & operator/=(const dual<X> &);
|
|
|
|
// 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<X> &b) const;
|
|
bool operator >(const dual<X> &b) const;
|
|
bool operator <=(const dual<X> &b) const;
|
|
bool operator >=(const dual<X> &b) const;
|
|
|
|
};
|
|
|
|
// Non-member functions:
|
|
|
|
T rpart(dual<T>) // Real part
|
|
T dpart(dual<T>) // Dual part
|
|
dual<T> dconj(dual<T>) // Dual-conjugate
|
|
|
|
dual<T> random(dual<T> low = {0,0}, dual<T> high = {1,0})
|
|
|
|
dual<T> exp(dual<T>)
|
|
dual<T> log(dual<T>)
|
|
dual<T> log10(dual<T>)
|
|
dual<T> pow(dual<T>, U)
|
|
dual<T> pow(U, dual<T>)
|
|
dual<T> pow(dual<T>, dual<T>)
|
|
dual<T> sqrt(dual<T>)
|
|
dual<T> cbrt(dual<T>)
|
|
dual<T> sin(dual<T>)
|
|
dual<T> cos(dual<T>)
|
|
dual<T> tan(dual<T>)
|
|
dual<T> asin(dual<T>)
|
|
dual<T> acos(dual<T>)
|
|
dual<T> atan(dual<T>)
|
|
|
|
// TODO:
|
|
dual<T> sinh(dual<T>)
|
|
dual<T> cosh(dual<T>)
|
|
dual<T> tanh(dual<T>)
|
|
dual<T> asinh(dual<T>)
|
|
dual<T> acosh(dual<T>)
|
|
dual<T> atanh(dual<T>)
|
|
|
|
// Non-differentiable operations on the real part.
|
|
T frexp(duals::dual<T> arg, int* exp );
|
|
duals::dual<T> ldexp(duals::dual<T> arg, int exp );
|
|
T trunc(duals::dual<T> d);
|
|
T floor(duals::dual<T> d);
|
|
T ceil(duals::dual<T> d);
|
|
T round(duals::dual<T> d);
|
|
int fpclassify(duals::dual<T> d);
|
|
bool isfinite(duals::dual<T> d);
|
|
bool isnormal(duals::dual<T> d);
|
|
bool isinf(duals::dual<T> d);
|
|
bool isnan(duals::dual<T> d);
|
|
|
|
// Stream IO
|
|
template<T> operator>>(basic_istream<charT, traits> &, dual<T> &);
|
|
template<T> operator<<(basic_ostream<charT, traits> &, const dual<T> &);
|
|
|
|
}
|
|
```
|
|
|
|
Some useful typedefs:
|
|
|
|
```
|
|
typedef dual<float> dualf;
|
|
typedef dual<double> duald;
|
|
typedef dual<long double> dualld;
|
|
typedef dual<dualf> hyperdualf;
|
|
typedef dual<duald> hyperduald;
|
|
typedef dual<dualld> hyperdualld;
|
|
typedef std::complex<dualf> cdualf;
|
|
typedef std::complex<duald> cduald;
|
|
typedef std::complex<dualld> 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 <duals/dual>
|
|
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 T> class dual;
|
|
#endif
|
|
|
|
/// Check if T is dual<>, match non-duals.
|
|
template <class T> struct is_dual : std::false_type {};
|
|
|
|
#ifndef PARSED_BY_DOXYGEN
|
|
|
|
/// Check if T is dual<>, match dual<>.
|
|
template <class T> struct is_dual<dual<T> > : std::true_type {};
|
|
|
|
#endif
|
|
|
|
/// Check if T is std::complex<>, match non- std::complex<>.
|
|
template <class T> struct is_complex : std::false_type {};
|
|
|
|
#ifndef PARSED_BY_DOXYGEN
|
|
|
|
/// Check if T is std::complex<>, match std::complex<>.
|
|
template <class T> struct is_complex<std::complex<T> > : std::true_type {};
|
|
|
|
#endif
|
|
|
|
/// Dual_traits helper class.
|
|
template <class T> struct dual_traits
|
|
{
|
|
/// Depth of T - for T=scalar this is 0. for dual_traits<double> 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 <class T> struct dual_traits<dual<T>>
|
|
{
|
|
/// 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<T>::depth + 1 };
|
|
|
|
/// The real storage type.
|
|
typedef typename dual_traits<T>::real_type real_type;
|
|
};
|
|
|
|
template <class T> struct dual_traits<std::complex<dual<T>>>
|
|
{
|
|
/// complex<dual<T>> have the same 'depth' as their dual.
|
|
enum { depth = dual_traits<T>::depth };
|
|
|
|
/// The real storage type.
|
|
typedef typename dual_traits<T>::real_type real_type;
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
template<class T>
|
|
struct Void { typedef void type; };
|
|
template<class T, class U = void>
|
|
struct has_member_type : std::false_type {};
|
|
template<class T>
|
|
struct has_member_type<T, typename Void<typename T::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 <class T, class U, class V = void> struct promote : std::common_type<T,U> {};
|
|
|
|
/// Can types A and B be promoted to a common type?
|
|
template<class A, class B> using can_promote = detail::has_member_type<promote<A,B>>;
|
|
|
|
template <class T, class U>
|
|
struct promote<dual<T>, dual<U>,
|
|
typename std::enable_if<(can_promote<T,U>::value
|
|
&& (int)dual_traits<T>::depth == (int)dual_traits<U>::depth)>::type>
|
|
{
|
|
typedef dual<typename promote<U,T>::type> type;
|
|
};
|
|
template <class T, class U>
|
|
struct promote<dual<T>, U,
|
|
typename std::enable_if<(can_promote<T,U>::value
|
|
&& (int)dual_traits<T>::depth >= (int)dual_traits<U>::depth
|
|
&& !is_complex<U>::value)>::type>
|
|
{
|
|
typedef dual<typename promote<U,T>::type> type;
|
|
};
|
|
template <class T, class U>
|
|
struct promote<U, dual<T>,
|
|
typename std::enable_if<(can_promote<T,U>::value
|
|
&& (int)dual_traits<T>::depth >= (int)dual_traits<U>::depth
|
|
&& !is_complex<U>::value)>::type>
|
|
{
|
|
typedef dual<typename promote<U,T>::type> type;
|
|
};
|
|
// /////////////////////////////////////////////////
|
|
template <class T, class U>
|
|
struct promote<std::complex<T>, std::complex<U>,
|
|
typename std::enable_if<(can_promote<T,U>::value
|
|
&& (is_dual<T>::value || is_dual<U>::value))>::type>
|
|
{
|
|
typedef std::complex<typename promote<T,U>::type> type;
|
|
};
|
|
template <class T, class U>
|
|
struct promote<std::complex<T>, U,
|
|
typename std::enable_if<(can_promote<T,U>::value
|
|
&& (is_dual<T>::value || is_dual<U>::value)
|
|
&& !is_complex<U>::value)>::type>
|
|
{
|
|
typedef std::complex<typename promote<T,U>::type> type;
|
|
};
|
|
template <class T, class U>
|
|
struct promote<U, std::complex<T>,
|
|
typename std::enable_if<(can_promote<T,U>::value
|
|
&& (is_dual<T>::value || is_dual<U>::value)
|
|
&& !is_complex<U>::value)>::type>
|
|
{
|
|
typedef std::complex<typename promote<T,U>::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 <class T>
|
|
struct is_arithmetic<duals::dual<T>> : is_arithmetic<T> {};
|
|
|
|
#endif // CPPDUALS_ENABLE_IS_ARITHMETIC
|
|
|
|
/// Duals are compound types.
|
|
template <class T>
|
|
struct is_compound<duals::dual<T>> : true_type {};
|
|
|
|
// Modification of std::numeric_limits<> per
|
|
// C++03 17.4.3.1/1, and C++11 18.3.2.3/1.
|
|
template <class T>
|
|
struct numeric_limits<duals::dual<T>> : numeric_limits<T> {
|
|
static constexpr bool is_specialized = true;
|
|
static constexpr duals::dual<T> min NOMACRO () { return numeric_limits<T>::min NOMACRO (); }
|
|
static constexpr duals::dual<T> lowest() { return numeric_limits<T>::lowest(); }
|
|
static constexpr duals::dual<T> max NOMACRO () { return numeric_limits<T>::max NOMACRO (); }
|
|
static constexpr duals::dual<T> epsilon() { return numeric_limits<T>::epsilon(); }
|
|
static constexpr duals::dual<T> round_error() { return numeric_limits<T>::round_error(); }
|
|
static constexpr duals::dual<T> infinity() { return numeric_limits<T>::infinity(); }
|
|
static constexpr duals::dual<T> quiet_NaN() { return numeric_limits<T>::quiet_NaN(); }
|
|
static constexpr duals::dual<T> signaling_NaN() { return numeric_limits<T>::signaling_NaN(); }
|
|
static constexpr duals::dual<T> denorm_min() { return numeric_limits<T>::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<X>::depth == \
|
|
(int)duals::dual_traits<T>::depth, int>::type = 0, \
|
|
typename std::enable_if<can_promote<T,X>::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<T>::depth == \
|
|
(int)duals::dual_traits<U>::depth, int>::type = 0, \
|
|
typename std::enable_if<can_promote<T,U>::value,int>::type = 0, \
|
|
typename common_t = dual<typename duals::promote<T,U>::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<T>::depth >= \
|
|
(int)duals::dual_traits<U>::depth), int>::type = 0, \
|
|
typename std::enable_if<can_promote<dual<T>,U>::value,int>::type = 0, \
|
|
typename common_t = typename duals::promote<dual<T>, U>::type
|
|
|
|
// T is wrapped in complex<dual<>>
|
|
#define CPPDUALS_ENABLE_LEQ_DEPTH_AND_CX_COMMON_T(T,U) \
|
|
typename std::enable_if<((int)duals::dual_traits<T>::depth >= \
|
|
(int)duals::dual_traits<U>::depth), int>::type = 0, \
|
|
typename std::enable_if<can_promote<std::complex<dual<T>>,U>::value,int>::type = 0, \
|
|
typename common_t = typename duals::promote<std::complex<dual<T> >,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 T>
|
|
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<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X),
|
|
CPPDUALS_ENABLE_IF(!is_complex<X>::value)>
|
|
dual(const dual<X> & x)
|
|
: _real(x.rpart()), _dual(x.dpart()) {}
|
|
|
|
/// Cast to a complex<dual<>> with real part equal to *this.
|
|
operator std::complex<dual<T>>() { return std::complex<dual<T>>(*this); }
|
|
|
|
/// Explicit cast to an arithmetic type retains the rpart()
|
|
template <class X,
|
|
CPPDUALS_ENABLE_IF(std::is_arithmetic<X>::value && !is_dual<X>::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<T> operator-() const { return dual<T>(-_real, -_dual); }
|
|
|
|
/// Unary nothing
|
|
dual<T> operator+() const { return *this; }
|
|
|
|
/// Assignment of `value_type` assigns the real part and zeros the dual part.
|
|
dual<T> & operator= (const T & x) { _real = x; _dual = value_type(); return *this; }
|
|
|
|
/// Add a relatively-scalar to this dual.
|
|
dual<T> & operator+=(const T & x) { _real += x; return *this; }
|
|
|
|
/// Subtract a relatively-scalar from this dual.
|
|
dual<T> & operator-=(const T & x) { _real -= x; return *this; }
|
|
|
|
/// Multiply a relatively-scalar with this dual.
|
|
dual<T> & operator*=(const T & x) { _real *= x; _dual *= x; return *this; }
|
|
|
|
/// Divide this dual by relatively-scalar.
|
|
dual<T> & operator/=(const T & x) { _real /= x; _dual /= x; return *this; }
|
|
|
|
//dua & operator=(const dual & x) { _real = x.rpart(); _dual = x.dpart(); }
|
|
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
|
|
dual<T> & operator= (const dual<X> & x) { _real = x.rpart(); _dual = x.dpart(); return *this; }
|
|
|
|
/// Add a dual of the same depth to this dual.
|
|
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
|
|
dual<T> & operator+=(const dual<X> & x) { _real += x.rpart(); _dual += x.dpart(); return *this; }
|
|
|
|
/// Subtract a dual of the same depth from this dual.
|
|
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
|
|
dual<T> & operator-=(const dual<X> & x) { _real -= x.rpart(); _dual -= x.dpart(); return *this; }
|
|
|
|
/// Multiply this dual with a dual of same of lower depth.
|
|
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
|
|
dual<T> & operator*=(const dual<X> & 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<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
|
|
dual<T> & operator/=(const dual<X> & 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<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
|
|
bool operator ==(const dual<X> &b) const { return _real == b.rpart(); }
|
|
|
|
/// Compare this dual with another dual, comparing real parts for inequality.
|
|
template<class X, CPPDUALS_ONLY_SAME_DEPTH_AS_T(T,X)>
|
|
bool operator !=(const dual<X> &b) const { return _real != b.rpart(); }
|
|
|
|
/// Compare real part against real part of b
|
|
template<class X, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,X)>
|
|
bool operator <(const dual<X> &b) const { return _real < b.rpart(); }
|
|
|
|
/// Compare real part against real part of b
|
|
template<class X, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,X)>
|
|
bool operator >(const dual<X> &b) const { return _real > b.rpart(); }
|
|
|
|
/// Compare real part against real part of b
|
|
template<class X, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,X)>
|
|
bool operator <=(const dual<X> &b) const { return _real <= b.rpart(); }
|
|
|
|
/// Compare real part against real part of b
|
|
template<class X, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,X)>
|
|
bool operator >=(const dual<X> &b) const { return _real >= b.rpart(); }
|
|
#endif
|
|
};
|
|
|
|
/// Get the dual's real part.
|
|
template <class T> T rpart(const dual<T> & x) { return x.rpart(); }
|
|
|
|
/// Get the dual's dual part.
|
|
template <class T> T dpart(const dual<T> & x) { return x.dpart(); }
|
|
|
|
/// R-part of complex<dual<>> is non-dual complex<> (not to be confused with real())
|
|
template <class T> std::complex<T> rpart(const std::complex<dual<T>> & x)
|
|
{ return std::complex<T>(x.real().rpart(), x.imag().rpart()); }
|
|
|
|
/// Dual part of complex<dual<>> is complex<>
|
|
template <class T> std::complex<T> dpart(const std::complex<dual<T>> & x)
|
|
{ return std::complex<T>(x.real().dpart(), x.imag().dpart()); }
|
|
|
|
/// Get a non-dual's real part.
|
|
template <class T,
|
|
CPPDUALS_ENABLE_IF((std::is_arithmetic<T>::value &&
|
|
!std::is_compound<T>::value) || is_complex<T>::value)>
|
|
T rpart(const T & x) { return x; }
|
|
|
|
/// Get a non-dual's dual part := zero.
|
|
template <class T,
|
|
CPPDUALS_ENABLE_IF((std::is_arithmetic<T>::value &&
|
|
!std::is_compound<T>::value) || is_complex<T>::value)>
|
|
T dpart(const T & ) { return T(0); }
|
|
|
|
#ifndef PARSED_BY_DOXYGEN
|
|
|
|
/// Dual +-*/ ops with another entity
|
|
#define CPPDUALS_BINARY_OP(op) \
|
|
template<class T, class U, CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U)> \
|
|
common_t \
|
|
operator op(const dual<T> & z, const dual<U> & w) { \
|
|
common_t x(z); \
|
|
return x op##= w; \
|
|
} \
|
|
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U), \
|
|
CPPDUALS_ENABLE_IF(!std::is_same<U,std::complex<dual<T>>>::value)> \
|
|
common_t \
|
|
operator op(const dual<T> & z, const U & w) { \
|
|
common_t x(z); \
|
|
return x op##= w; \
|
|
} \
|
|
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U), \
|
|
CPPDUALS_ENABLE_IF(!std::is_same<U,std::complex<dual<T>>>::value)> \
|
|
common_t \
|
|
operator op(const U & z, const dual<T> & w) { \
|
|
common_t x(z); \
|
|
return x op##= w; \
|
|
} \
|
|
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_CX_COMMON_T(T,U), \
|
|
CPPDUALS_ENABLE_IF(!std::is_same<U,std::complex<dual<T>>>::value)> \
|
|
common_t \
|
|
operator op(const std::complex<dual<T>> & z, const U & w) { \
|
|
common_t x(z); \
|
|
return x op##= w; \
|
|
} \
|
|
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_CX_COMMON_T(T,U), \
|
|
CPPDUALS_ENABLE_IF(!std::is_same<U,std::complex<dual<T>>>::value)> \
|
|
common_t \
|
|
operator op(const U & z, const std::complex<dual<T>> & 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<class T, class U, CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U)> \
|
|
bool \
|
|
operator op(const dual<T> & a, const dual<U> & b) { \
|
|
return a.rpart() op b.rpart(); \
|
|
} \
|
|
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U), \
|
|
CPPDUALS_ENABLE_IF(!is_complex<U>::value)> \
|
|
bool \
|
|
operator op(const U & a, const dual<T> & b) { \
|
|
return a op b.rpart(); \
|
|
} \
|
|
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U), \
|
|
CPPDUALS_ENABLE_IF(!is_complex<U>::value)> \
|
|
bool \
|
|
operator op(const dual<T> & 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<class T,
|
|
typename dist = std::uniform_real_distribution<T>,
|
|
CPPDUALS_ENABLE_IF(!is_complex<T>::value && !is_dual<T>::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<class T,
|
|
typename dist = std::uniform_real_distribution<T>,
|
|
CPPDUALS_ENABLE_IF(!is_complex<T>::value && !is_dual<T>::value)>
|
|
T random2(T a = T(0), T b = T(1)) { return random<T,dist>(a,b); }
|
|
|
|
// Helper class for testing - also random value in dual part.
|
|
template <class DT,
|
|
CPPDUALS_ENABLE_IF(is_dual<DT>::value)>
|
|
DT random2(DT a = DT(0,0), DT b = DT(1,1)) {
|
|
using randos::random;
|
|
return DT(a.rpart() + random2<typename DT::value_type>() * (b.rpart() - a.rpart()),
|
|
a.dpart() + random2<typename DT::value_type>() * (b.dpart() - a.dpart()));
|
|
}
|
|
|
|
// Helper class for testing - also random value in dual part of the complex.
|
|
template<class CT,
|
|
CPPDUALS_ENABLE_IF(is_complex<CT>::value)>
|
|
CT random2(CT a = CT(0,0), CT b = CT(1,1)) {
|
|
return CT(a.real() + random2<typename CT::value_type>() * (b.real() - a.real()),
|
|
a.imag() + random2<typename CT::value_type>() * (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 <class DT,
|
|
typename dist = std::uniform_real_distribution<typename DT::value_type>,
|
|
CPPDUALS_ENABLE_IF(is_dual<DT>::value)>
|
|
DT random(DT a = DT(0,0), DT b = DT(1,0)) {
|
|
using randos::random;
|
|
return DT(random<typename DT::value_type,dist>(a.rpart(),b.rpart()),
|
|
random<typename DT::value_type,dist>(a.dpart(),b.dpart()));
|
|
}
|
|
|
|
/// Complex Conjugate of a dual is just the dual.
|
|
template<class T> dual<T> conj(const dual<T> & 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<T>.
|
|
template<class T, CPPDUALS_ENABLE_IF(!is_dual<T>::value &&
|
|
!is_complex<T>::value &&
|
|
std::is_arithmetic<T>::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<class T> dual<T> dconj(const dual<T> & x) {
|
|
return dual<T>(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<class T> std::complex<T> dconj(const std::complex<T> & x) {
|
|
return std::complex<T>(dconj(x.real()), dconj(x.imag()));
|
|
}
|
|
|
|
/// Conjugate a thing that's not dual and not complex.
|
|
template<class T, CPPDUALS_ENABLE_IF(!is_dual<T>::value &&
|
|
!is_complex<T>::value &&
|
|
std::is_arithmetic<T>::value)>
|
|
T dconj(const T & x) { return x; }
|
|
|
|
/// Exponential e^x
|
|
template<class T> dual<T> exp(const dual<T> & x) {
|
|
using std::exp;
|
|
T v = exp(x.rpart());
|
|
return dual<T>(v,
|
|
v * x.dpart());
|
|
}
|
|
|
|
/// Natural log ln(x)
|
|
template<class T> dual<T> log(const dual<T> & x) {
|
|
using std::log;
|
|
T v = log(x.rpart());
|
|
if (x.dpart() == T(0))
|
|
return v;
|
|
else
|
|
return dual<T>(v, x.dpart() / x.rpart());
|
|
}
|
|
|
|
template<class T> dual<T> log10(const dual<T> & x) {
|
|
using std::log;
|
|
return log(x) / log(static_cast<T>(10));
|
|
}
|
|
|
|
template<class T> dual<T> log2(const dual<T> & x) {
|
|
using std::log;
|
|
return log(x) / log(static_cast<T>(2));
|
|
}
|
|
|
|
template<class T, class U, CPPDUALS_ENABLE_SAME_DEPTH_AND_COMMON_T(T,U)>
|
|
common_t
|
|
pow(const dual<T> & x, const dual<U> & 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<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U)>
|
|
common_t
|
|
pow(const dual<T> & x, const U & y) {
|
|
using std::pow;
|
|
return common_t(pow(x.rpart(), y),
|
|
x.dpart() * y * pow(x.rpart(), y - U(1)));
|
|
}
|
|
|
|
template<class T, class U, CPPDUALS_ENABLE_LEQ_DEPTH_AND_COMMON_T(T,U)>
|
|
common_t
|
|
pow(const U & x, const dual<T> & y) {
|
|
return pow(common_t(x), y);
|
|
}
|
|
|
|
namespace utils {
|
|
template <typename T> int sgn(T val) {
|
|
return (T(0) < val) - (val < T(0));
|
|
}
|
|
}
|
|
|
|
template<class T> dual<T> abs(const dual<T> & x) {
|
|
using std::abs;
|
|
return dual<T>(abs(x.rpart()),
|
|
x.dpart() * utils::sgn(x.rpart()));
|
|
}
|
|
|
|
template<class T> dual<T> fabs(const dual<T> & x) {
|
|
using std::fabs;
|
|
return dual<T>(fabs(x.rpart()),
|
|
x.dpart() * utils::sgn(x.rpart()));
|
|
}
|
|
|
|
#if 0
|
|
template<class T> dual<T> abs2(const dual<T> & x) {
|
|
using std::abs;
|
|
return dual<T>(x.rpart() * x.rpart(),
|
|
xxx x.dpart() * utils::sgn(x.rpart()));
|
|
}
|
|
#endif
|
|
|
|
template<class T> duals::dual<T> copysign(const duals::dual<T> & x, const duals::dual<T> & y) {
|
|
using std::copysign;
|
|
T r = copysign(x.rpart(), y.rpart());
|
|
return duals::dual<T>(r, r == x.rpart() ? x.dpart() : -x.dpart());
|
|
}
|
|
|
|
template<class T> duals::dual<T> hypot(const duals::dual<T> & x, const duals::dual<T> & y) {
|
|
return sqrt(x*x + y*y);
|
|
}
|
|
|
|
template<class T> duals::dual<T> scalbn(const duals::dual<T> & arg, int ex) {
|
|
return arg * std::pow((T)2, ex);
|
|
}
|
|
|
|
template<class T> duals::dual<T> (fmax)(const duals::dual<T> & x, const duals::dual<T> & y) {
|
|
return x.rpart() > y.rpart() ? x : y;
|
|
}
|
|
|
|
template<class T> duals::dual<T> (fmin)(const duals::dual<T> & x, const duals::dual<T> & y) {
|
|
return x.rpart() <= y.rpart() ? x : y;
|
|
}
|
|
|
|
template<class T> duals::dual<T> logb(const duals::dual<T> & x) {
|
|
return duals::log2(x);
|
|
}
|
|
|
|
template<class T> int (fpclassify)(const duals::dual<T> & d) { using std::fpclassify; return (fpclassify)(d.rpart()); }
|
|
template<class T> bool (isfinite)(const duals::dual<T> & d) { using std::isfinite; return (isfinite)(d.rpart()); }
|
|
template<class T> bool (isnormal)(const duals::dual<T> & d) { using std::isnormal; return (isnormal)(d.rpart()); }
|
|
template<class T> bool (isinf)(const duals::dual<T> & d) { using std::isinf; return (isinf)(d.rpart()); }
|
|
template<class T> bool (isnan)(const duals::dual<T> & d) { using std::isnan; return (isnan)(d.rpart()); }
|
|
template<class T> bool (signbit)(const duals::dual<T> & d) { using std::signbit; return (signbit)(d.rpart()); }
|
|
|
|
template<class T> dual<T> sqrt(const dual<T> & x) {
|
|
using std::sqrt;
|
|
T v = sqrt(x.rpart());
|
|
if (x.dpart() == T(0))
|
|
return v;
|
|
else
|
|
return dual<T>(v, x.dpart() / (T(2) * v) );
|
|
}
|
|
|
|
template<class T> dual<T> cbrt(const dual<T> & x) {
|
|
using std::cbrt;
|
|
T v = cbrt(x.rpart());
|
|
if (x.dpart() == T(0))
|
|
return v;
|
|
else
|
|
return dual<T>(v, x.dpart() / (T(3) * v * v) );
|
|
}
|
|
|
|
template<class T> dual<T> sin(const dual<T> & x) {
|
|
using std::sin;
|
|
using std::cos;
|
|
return dual<T>(sin(x.rpart()),
|
|
x.dpart() * cos(x.rpart()));
|
|
}
|
|
|
|
template<class T> dual<T> cos(const dual<T> & x) {
|
|
using std::cos;
|
|
using std::sin;
|
|
return dual<T>(cos(x.rpart()),
|
|
-sin(x.rpart()) * x.dpart());
|
|
}
|
|
|
|
template<class T> dual<T> tan(const dual<T> & x) {
|
|
using std::tan;
|
|
T v = tan(x.rpart());
|
|
return dual<T>(v, x.dpart() * (v*v + 1));
|
|
}
|
|
|
|
template<class T> dual<T> asin(const dual<T> & x) {
|
|
using std::asin;
|
|
using std::sqrt;
|
|
T v = asin(x.rpart());
|
|
if (x.dpart() == T(0))
|
|
return v;
|
|
else
|
|
return dual<T>(v, x.dpart() / sqrt(1 - x.rpart()*x.rpart()));
|
|
}
|
|
|
|
template<class T> dual<T> acos(const dual<T> & x) {
|
|
using std::acos;
|
|
using std::sqrt;
|
|
T v = acos(x.rpart());
|
|
if (x.dpart() == T(0))
|
|
return v;
|
|
else
|
|
return dual<T>(v, -x.dpart() / sqrt(1 - x.rpart()*x.rpart()));
|
|
}
|
|
|
|
template<class T> dual<T> atan(const dual<T> & x) {
|
|
using std::atan;
|
|
T v = atan(x.rpart());
|
|
if (x.dpart() == T(0))
|
|
return v;
|
|
else
|
|
return dual<T>(v, x.dpart() / (1 + x.rpart()*x.rpart()));
|
|
}
|
|
|
|
template<class T> dual<T> atan2(const dual<T> & x, const dual<T> & y) {
|
|
using std::atan2;
|
|
T v = atan2(x.rpart(), y.rpart());
|
|
if (x.dpart() == T(0))
|
|
return v;
|
|
else
|
|
return dual<T>(v, x.dpart() / (1 + x.rpart()*x.rpart()));
|
|
}
|
|
|
|
// TODO
|
|
template<class T> dual<T> sinh(const dual<T> & x);
|
|
template<class T> dual<T> cosh(const dual<T> & x);
|
|
template<class T> dual<T> tanh(const dual<T> & x);
|
|
template<class T> dual<T> asinh(const dual<T> & x);
|
|
template<class T> dual<T> acosh(const dual<T> & x);
|
|
template<class T> dual<T> atanh(const dual<T> & x);
|
|
template<class T> dual<T> log1p(const dual<T> & x);
|
|
template<class T> dual<T> expm1(const dual<T> & x);
|
|
|
|
/// The error function. Make sure to `#include <math.h>` before
|
|
/// `#include <duals/dual>` to use this function.
|
|
template<class T> dual<T> erf(const dual<T> & x) {
|
|
using std::erf;
|
|
using std::sqrt;
|
|
using std::pow;
|
|
using std::exp;
|
|
return dual<T>(erf(x.rpart()),
|
|
x.dpart() * T(2)/sqrt(T(MY_PI)) * exp(-pow(x.rpart(),T(2))));
|
|
}
|
|
|
|
/// Error function complement (1 - erf()).
|
|
template<class T> dual<T> erfc(const dual<T> & x) {
|
|
using std::erfc;
|
|
using std::sqrt;
|
|
using std::pow;
|
|
using std::exp;
|
|
return dual<T>(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<class T> dual<T> tgamma(const dual<T> & 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<T>::digits / 3)));
|
|
T w((tgamma(x.rpart()+h) - tgamma(x.rpart()-h))/(2*h));
|
|
errno = errno_saved;
|
|
return dual<T>(v, x.dpart() * w);
|
|
}
|
|
}
|
|
|
|
/// Log of absolute value of gamma function. Approximation of the dual part.
|
|
template<class T> dual<T> lgamma(const dual<T> & 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<T>::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<T>(v, x.dpart() * w);
|
|
}
|
|
}
|
|
|
|
/// Putto operator
|
|
template<class T, class _CharT, class _Traits>
|
|
std::basic_ostream<_CharT, _Traits> &
|
|
operator<<(std::basic_ostream<_CharT, _Traits> & os, const dual<T> & 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<typename std::decay<T>::type,float>::value ? "f" :
|
|
std::is_same<typename std::decay<T>::type,double>::value ? "" :
|
|
std::is_same<typename std::decay<T>::type,long double>::value ? "l" : "")
|
|
<< ")";
|
|
return os << s.str();
|
|
}
|
|
|
|
/// Stream reader
|
|
template<class T, class CharT, class Traits>
|
|
std::basic_istream<CharT, Traits> &
|
|
operator>>(std::basic_istream<CharT, Traits> & is, dual<T> & 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<typename std::decay<T>::type,float>::value) ||
|
|
(c == 'l' && !std::is_same<typename std::decay<T>::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<T>(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<T>(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<T>(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<float> operator""_ef(long double du)
|
|
{
|
|
return { 0.0f, static_cast<float>(du) };
|
|
}
|
|
constexpr dual<float> operator""_ef(unsigned long long du)
|
|
{
|
|
return { 0.0f, static_cast<float>(du) };
|
|
}
|
|
constexpr dual<double> operator""_e(long double du)
|
|
{
|
|
return { 0.0, static_cast<double>(du) };
|
|
}
|
|
constexpr dual<double> operator""_e(unsigned long long du)
|
|
{
|
|
return { 0.0, static_cast<double>(du) };
|
|
}
|
|
constexpr dual<long double> operator""_el(long double du)
|
|
{
|
|
return { 0.0l, du };
|
|
}
|
|
constexpr dual<long double> operator""_el(unsigned long long du)
|
|
{
|
|
return { 0.0l, static_cast<long double>(du) };
|
|
}
|
|
}
|
|
#endif
|
|
|
|
typedef dual<float> dualf;
|
|
typedef dual<double> duald;
|
|
typedef dual<long double> dualld;
|
|
typedef dual<dualf> hyperdualf;
|
|
typedef dual<duald> hyperduald;
|
|
typedef dual<dualld> hyperdualld;
|
|
typedef std::complex<dualf> cdualf;
|
|
typedef std::complex<duald> cduald;
|
|
typedef std::complex<dualld> cdualld;
|
|
|
|
} // namespace duals
|
|
|
|
#ifdef CPPDUALS_LIBFMT
|
|
#include <fmt/format.h>
|
|
/// 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<double> 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 <typename T, typename Char>
|
|
struct fmt::formatter<duals::dual<T>,Char> : public fmt::formatter<T,Char>
|
|
{
|
|
typedef fmt::formatter<T,Char> base;
|
|
enum style { expr, star, pair } style_ = expr;
|
|
internal::dynamic_format_specs<Char> specs_;
|
|
FMT_CONSTEXPR auto parse(format_parse_context & ctx) -> decltype(ctx.begin()) {
|
|
using handler_type = internal::dynamic_specs_handler<format_parse_context>;
|
|
auto type = internal::type_constant<T, Char>::value;
|
|
internal::specs_checker<handler_type> 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 <typename FormatCtx>
|
|
auto format(const duals::dual<T> & 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<typename std::decay<T>::type,float>::value) format_to(ctx.out(), "f");
|
|
if (std::is_same<typename std::decay<T>::type,long double>::value) format_to(ctx.out(), "l");
|
|
}
|
|
return format_to(ctx.out(), ")");
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef CPPDUALS_LIBFMT_COMPLEX
|
|
#ifndef CPPDUALS_LIBFMT
|
|
#include <fmt/format.h>
|
|
#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<double> using the specs {:g}
|
|
/// | {:$g}, {:*g}, or {:,g}, respectively. (this implementation is a
|
|
/// bit hacky - glad for cleanups).
|
|
///
|
|
template <typename T, typename Char>
|
|
struct fmt::formatter<std::complex<T>,Char> : public fmt::formatter<T,Char>
|
|
{
|
|
typedef fmt::formatter<T,Char> base;
|
|
enum style { expr, star, pair } style_ = expr;
|
|
internal::dynamic_format_specs<Char> specs_;
|
|
FMT_CONSTEXPR auto parse(format_parse_context & ctx) -> decltype(ctx.begin()) {
|
|
using handler_type = internal::dynamic_specs_handler<format_parse_context>;
|
|
auto type = internal::type_constant<T, Char>::value;
|
|
internal::specs_checker<handler_type> 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 <typename FormatCtx>
|
|
auto format(const std::complex<T> & 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<typename std::decay<T>::type,float>::value) format_to(ctx.out(), "f");
|
|
if (std::is_same<typename std::decay<T>::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<T> & arg, int* exp ) { return (frexp)(arg.rpart(), exp); } \
|
|
inline duals::dual<T> (ldexp)(const duals::dual<T> & arg, int exp ) { return arg * std::pow((T)2,exp); } \
|
|
inline T (trunc)(const duals::dual<T> & d) { return (trunc)(d.rpart()); } \
|
|
inline T (floor)(const duals::dual<T> & d) { return (floor)(d.rpart()); } \
|
|
inline T (ceil)(const duals::dual<T> & d) { return (ceil)(d.rpart()); } \
|
|
inline T (round)(const duals::dual<T> & d) { return (round)(d.rpart()); } \
|
|
inline int (fpclassify)(const duals::dual<T> & d) { return (fpclassify)(d.rpart()); } \
|
|
inline bool (isfinite)(const duals::dual<T> & d) { return (isfinite)(d.rpart()); } \
|
|
inline bool (isnormal)(const duals::dual<T> & d) { return (isnormal)(d.rpart()); } \
|
|
inline bool (isinf)(const duals::dual<T> & d) { return (isinf)(d.rpart()); } \
|
|
inline bool (isnan)(const duals::dual<T> & 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
|