emilib
dual.hpp
1 // By Emil Ernerfeldt 2017
2 // LICENSE:
3 // This software is dual-licensed to the public domain and under the following
4 // license: you are granted a perpetual, irrevocable license to copy, modify,
5 // publish, and distribute this file as you see fit.
6 
7 /*
8  A library for dual numbers.
9 
10  Dual numbers can be used to get numerical stable differentiation with minimal effort:
11  auto result = f(Dual<float>(x, 1));
12  result.real == f(x)
13  result.eps == d f(x) / dx
14 
15  If you want to use dual numbers in Eigen matrices then:
16  #define DUALS_EGIEN 1
17 */
18 #pragma once
19 
20 #include <cmath>
21 #include <iosfwd>
22 #include <limits>
23 #include <utility>
24 
25 namespace emilib {
26 
27 template<typename T>
28 class Dual
29 {
30 public:
31  inline constexpr Dual() : real(T()) , eps(T()) { }
32  inline constexpr explicit Dual(const T& real_) : real(real_) , eps(T()) { }
33  inline constexpr Dual(const T& real_, const T& eps_) : real(real_) , eps(eps_) { }
34 
35  T real;
36  T eps;
37 };
38 
39 // ----------------------------------------------------------------------------
40 
41 template<typename T>
42 inline constexpr Dual<T> operator+(const Dual<T>& left, const Dual<T>& right)
43 {
44  return {left.real + right.real, left.eps + right.eps};
45 }
46 
47 template<typename T>
48 inline constexpr Dual<T> operator+(const Dual<T>& left, const T& right)
49 {
50  return {left.real + right, left.eps};
51 }
52 
53 template<typename T>
54 inline constexpr Dual<T> operator+(const T& left, const Dual<T>& right)
55 {
56  return {left + right.real, right.eps};
57 }
58 
59 template<typename T>
60 inline constexpr void operator+=(Dual<T>& left, const Dual<T>& right)
61 {
62  left = left + right;
63 }
64 
65 template<typename T>
66 inline constexpr void operator+=(Dual<T>& left, const T& right)
67 {
68  left.real += right;
69 }
70 
71 // ----------------------------------------------------------------------------
72 
73 template<typename T>
74 inline constexpr Dual<T> operator-(const Dual<T>& left, const Dual<T>& right)
75 {
76  return {left.real - right.real, left.eps - right.eps};
77 }
78 
79 template<typename T>
80 inline constexpr Dual<T> operator-(const Dual<T>& left, const T& right)
81 {
82  return {left.real - right, left.eps};
83 }
84 
85 template<typename T>
86 inline constexpr Dual<T> operator-(const T& left, const Dual<T>& right)
87 {
88  return {left - right.real, right.eps};
89 }
90 
91 template<typename T>
92 inline constexpr void operator-=(Dual<T>& left, const Dual<T>& right)
93 {
94  left = left - right;
95 }
96 
97 template<typename T>
98 inline constexpr void operator-=(Dual<T>& left, const T& right)
99 {
100  left.real -= right;
101 }
102 
103 // ----------------------------------------------------------------------------
104 
105 template<typename T>
106 inline constexpr Dual<T> operator*(const T& left, const Dual<T>& right)
107 {
108  return {left * right.real, left * right.eps};
109 }
110 
111 template<typename T>
112 inline constexpr Dual<T> operator*(const Dual<T>& left, const T& right)
113 {
114  return {left.real * right, left.eps * right};
115 }
116 
117 template<typename T>
118 inline constexpr void operator*=(Dual<T>& left, const T& right)
119 {
120  left = left * right;
121 }
122 
123 template<typename T>
124 inline constexpr Dual<T> operator*(const Dual<T>& left, const Dual<T>& right)
125 {
126  return {left.real * right.real, left.real * right.eps + right.real * left.eps};
127 }
128 
129 template<typename T>
130 inline constexpr void operator*=(Dual<T>& left, const Dual<T>& right)
131 {
132  left = left * right;
133 }
134 
135 // ----------------------------------------------------------------------------
136 
137 template<typename T>
138 inline constexpr Dual<T> operator/(const Dual<T>& left, const T& right)
139 {
140  return {left.real / right, left.eps / right};
141 }
142 
143 template<typename T>
144 inline constexpr void operator/=(Dual<T>& left, const T& right)
145 {
146  left = left / right;
147 }
148 
149 template<typename T>
150 inline constexpr Dual<T> operator/(const Dual<T>& left, const Dual<T>& right)
151 {
152  if (right.real == T()) {
153  if (right.eps == T()) {
154  // Anything divided by zero:
155  return {std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()};
156  } else if (left.real == T()) {
157  // eps divided by eps:
158  return Dual<T>{left.eps / right.eps, std::numeric_limits<T>::quiet_NaN()};
159  // return Dual<T>{left.eps / right.eps, left.eps / right.eps}; // Assumes the eps is repeated as a hyper-eps
160  // return Dual<T>(left.eps - right.eps); // e.g. for x/x this makes sense for filling in a singularity.
161  // return Dual<T>{std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()}; // We shouldn't rely on dividing by 0 + eps
162  } else {
163  // real divided by eps:
164  const T sign = left.real * right.eps;
165  const T signed_inf = sign * std::numeric_limits<T>::infinity();
166  return Dual<T>(signed_inf, signed_inf);
167  }
168  } else {
169  // real divided by real:
170  return Dual<T>{
171  left.real / right.real,
172  (left.eps * right.real - left.real * right.eps) / (right.real * right.real)
173  };
174  }
175 }
176 
177 template<typename T>
178 inline constexpr void operator/=(Dual<T>& left, const Dual<T>& right)
179 {
180  left = left / right;
181 }
182 
183 // ----------------------------------------------------------------------------
184 
185 template<typename T>
186 inline constexpr Dual<T> operator+(const Dual<T>& operand)
187 {
188  return {+operand.real, +operand.eps};
189 }
190 
191 template<typename T>
192 inline constexpr Dual<T> operator-(const Dual<T>& operand)
193 {
194  return {-operand.real, -operand.eps};
195 }
196 
197 // ----------------------------------------------------------------------------
198 
199 template<typename T>
200 inline constexpr bool operator==(const Dual<T>& left, const Dual<T>& right)
201 {
202  return left.real == right.real && left.eps == right.eps;
203 }
204 
205 template<typename T>
206 inline constexpr bool operator!=(const Dual<T>& left, const Dual<T>& right)
207 {
208  return left.real != right.real || left.eps != right.eps;
209 }
210 
211 template<typename T>
212 inline constexpr bool operator<(const Dual<T>& left, const Dual<T>& right)
213 {
214  return std::make_pair(left.real, left.eps) < std::make_pair(right.real, right.eps);
215 }
216 
217 template<typename T>
218 inline constexpr bool operator<=(const Dual<T>& left, const Dual<T>& right)
219 {
220  return std::make_pair(left.real, left.eps) <= std::make_pair(right.real, right.eps);
221 }
222 
223 template<typename T>
224 inline constexpr bool operator>=(const Dual<T>& left, const Dual<T>& right)
225 {
226  return std::make_pair(left.real, left.eps) >= std::make_pair(right.real, right.eps);
227 }
228 
229 template<typename T>
230 inline constexpr bool operator>(const Dual<T>& left, const Dual<T>& right)
231 {
232  return std::make_pair(left.real, left.eps) > std::make_pair(right.real, right.eps);
233 }
234 
235 // ----------------------------------------------------------------------------
236 
237 template<typename T>
238 std::ostream& operator<<(std::ostream& os, const Dual<T>& x)
239 {
240  if (x.eps == T()) { return os << x.real; }
241  else if (x.real == T()) { return os << x.eps << "ε"; }
242  else { return os << x.real << (x.eps < 0 ? '-' : '+') << std::abs(x.eps) << "ε"; }
243 }
244 
245 } // namespace emilib
246 
247 // ----------------------------------------------------------------------------
248 
249 namespace std {
250 
251 template<typename T>
252 inline constexpr emilib::Dual<T> abs(const emilib::Dual<T>& x)
253 {
254  if (x.real < 0) { return { -x.real, -x.eps }; }
255  if (x.real > 0) { return { x.real, x.eps }; }
256  return {0, abs(x.eps)};
257 }
258 
259 template<typename T>
260 emilib::Dual<T> sin(const emilib::Dual<T>& x)
261 {
262  return {sin(x.real), x.eps * cos(x.real)};
263 }
264 
265 template<typename T>
266 emilib::Dual<T> cos(const emilib::Dual<T>& x)
267 {
268  return {cos(x.real), -x.eps * sin(x.real)};
269 }
270 
271 template <typename T>
272 emilib::Dual<T> log(const emilib::Dual<T>& x)
273 {
274  return {log(x.real), x.eps / x.real};
275 }
276 
277 template <typename T>
278 emilib::Dual<T> log10(const emilib::Dual<T>& x)
279 {
280  return {log10(x.real), x.eps / (log(T(10)) * x.real)};
281 }
282 
283 template <typename T>
284 emilib::Dual<T> sqrt(const emilib::Dual<T>& x)
285 {
286  return {
287  sqrt(x.real),
288  x.eps / (T(2) * sqrt(x.real))
289  };
290 }
291 
292 template <typename T, typename S>
293 emilib::Dual<T> pow(const emilib::Dual<T>& left, const S& right)
294 {
295  return {
296  pow(left.real, right),
297  left.eps * T(right) * pow(left.real, right - S(1))
298  };
299 }
300 
301 template <typename T>
302 emilib::Dual<T> pow(const T& left, const emilib::Dual<T>& right)
303 {
304  return {
305  pow(left, right.real),
306  right.eps * log(left) * pow(left, right.real)
307  };
308 }
309 
310 template <typename T>
311 emilib::Dual<T> pow(const emilib::Dual<T>& left, const emilib::Dual<T>& right)
312 {
313  return {
314  pow(left.real, right.real),
315  left.eps * right.real * pow(left.real, right.real - T(1)) +
316  right.eps * log(left.real) * pow(left.real, right.real)
317  };
318 }
319 
320 template <typename T>
321 bool isfinite(const emilib::Dual<T>& left)
322 {
323  return isfinite(left.real);
324 }
325 
326 template <typename T>
327 T ceil(const emilib::Dual<T>& x)
328 {
329  return ceil(x.real);
330 }
331 
332 } // namespace std
333 
334 // ----------------------------------------------------------------------------
335 
336 // Define DUALS_EGIEN to have you Dual:s work inside of Eigen matrices.
337 #if DUALS_EGIEN
338  #include <Eigen/Geometry>
339 
340  namespace Eigen {
341 
342  template <typename T>
343  struct NumTraits<emilib::Dual<T>> : GenericNumTraits<emilib::Dual<T>>
344  {
345  using Real = T;
346  using Literal = typename NumTraits<T>::Literal;
347  enum {
348  IsComplex = 1,
349  RequireInitialization = NumTraits<Real>::RequireInitialization,
350  ReadCost = 2 * NumTraits<Real>::ReadCost,
351  AddCost = 2 * NumTraits<Real>::AddCost,
352  MulCost = 3 * NumTraits<Real>::MulCost + NumTraits<Real>::AddCost
353  };
354 
355  EIGEN_DEVICE_FUNC static inline Real epsilon() { return NumTraits<Real>::epsilon(); }
356  EIGEN_DEVICE_FUNC static inline Real dummy_precision() { return NumTraits<Real>::dummy_precision(); }
357  EIGEN_DEVICE_FUNC static inline int digits10() { return NumTraits<Real>::digits10(); }
358  };
359 
360  template<typename T, typename BinaryOp>
361  struct ScalarBinaryOpTraits<T, emilib::Dual<T>, BinaryOp>
362  {
363  using ReturnType = emilib::Dual<T>;
364  };
365 
366  template<typename T, typename BinaryOp>
367  struct ScalarBinaryOpTraits<emilib::Dual<T>, T, BinaryOp>
368  {
369  using ReturnType = emilib::Dual<T>;
370  };
371 
372  } // namespace Eigen
373 #endif // DUALS_EGIEN
Definition: dual.hpp:249
Definition: dual.hpp:28
Definition: coroutine.hpp:18