MathFunc.h
Go to the documentation of this file.
1 /*
2  * Copyright 2012-2021 CNRS-UM LIRMM, CNRS-AIST JRL
3  */
4 
5 #pragma once
6 
7 #include <cmath>
8 #include <limits>
9 
10 namespace sva
11 {
12 
13 namespace details
14 {
15 template<typename T>
16 T constexpr sqrtNewtonRaphson(T x, T curr, T prev)
17 {
18  return curr == prev ? curr : sqrtNewtonRaphson(x, static_cast<T>(0.5) * (curr + x / curr), curr);
19 }
20 
32 template<typename T>
33 T constexpr sqrt(T x)
34 {
35  return x >= static_cast<T>(0) && x < std::numeric_limits<T>::infinity() ? sqrtNewtonRaphson(x, x, static_cast<T>(0))
36  : std::numeric_limits<T>::quiet_NaN();
37 }
38 
39 template<typename T>
40 bool constexpr eq(T x, T y)
41 {
42  return x < y ? (y - x) <= y * std::numeric_limits<T>::epsilon() : (x - y) <= x * std::numeric_limits<T>::epsilon();
43 }
44 
45 template<typename T>
46 T constexpr cbrtNewtonRaphson(T x, T curr, T prev)
47 {
48  return eq(curr, prev)
49  ? curr
50  : cbrtNewtonRaphson(x, (static_cast<T>(2) * curr + x / (curr * curr)) / static_cast<T>(3), curr);
51 }
52 
53 template<typename T>
54 T constexpr cbrtSub(T x)
55 {
56  return x < std::numeric_limits<T>::infinity() ? cbrtNewtonRaphson(x, sqrt(x), static_cast<T>(0))
57  : std::numeric_limits<T>::quiet_NaN();
58 }
59 
68 template<typename T>
69 T constexpr cbrt(T x)
70 {
71  return x >= static_cast<T>(0) ? cbrtSub(x) : -cbrtSub(-x);
72 }
73 
76 template<typename T>
77 inline T SO3JacF2(const T & x)
78 {
79  // Taylor expansion at 0 is 1/360 * (1 + x^2/60 + x^4/2520 + x^6/100800 + x^8/3991680)
80  using details::cbrt;
81  using details::sqrt;
82  constexpr T ulp = std::numeric_limits<T>::epsilon();
83  constexpr T taylor_2_bound = sqrt(static_cast<T>(60) * ulp);
84  constexpr T taylor_4_bound = sqrt(sqrt(static_cast<T>(2520) * ulp));
85  constexpr T taylor_6_bound = sqrt(cbrt(static_cast<T>(100800) * ulp));
86  constexpr T taylor_8_bound = sqrt(sqrt(sqrt(static_cast<T>(3991680) * ulp)));
87 
88  double absx = std::abs(x);
89  if(absx >= taylor_8_bound)
90  {
91  return static_cast<T>(1) / (x * x) - (static_cast<T>(1) + std::cos(x)) / (static_cast<T>(2) * x * std::sin(x));
92  }
93  else
94  {
95  // approximation by taylor series in x at 0 up to order 0
96  T result = static_cast<T>(1);
97 
98  if(absx >= taylor_2_bound)
99  {
100  T x2 = x * x;
101  // approximation by taylor series in x at 0 up to order 2
102  result += x2 / static_cast<T>(60);
103 
104  if(absx >= taylor_4_bound)
105  {
106  T x4 = x2 * x2;
107  // approximation by taylor series in x at 0 up to order 4
108  result += x4 / static_cast<T>(2520);
109  if(absx >= taylor_6_bound)
110  {
111  // approximation by taylor series in x at 0 up to order 6
112  result += (x2 * x4) / static_cast<T>(100800);
113  }
114  }
115  }
116 
117  return result / static_cast<T>(12);
118  }
119 }
120 
124 template<typename T>
125 inline T dSO3JacF2(const T & x)
126 {
127  // Taylor expansion at 0 is x/360 * (1 + x^2/21 + x^4/560 + x^6/16632 + (691*x^8)/363242880) + x^10/17297280 +
128  // (3617*x^12)/2117187072000 We need to go this far in the Taylor expansion, because for numbers around
129  // taylor_8_bound, the analytical formula gives a relative error of about 1e-6 with doubles, which is too high. Going
130  // up to 13th order gives us a max error of about 3e-10.
131  using details::cbrt;
132  using details::sqrt;
133  constexpr T ulp = std::numeric_limits<T>::epsilon();
134  constexpr T taylor_2_bound = sqrt(static_cast<T>(21) * ulp);
135  constexpr T taylor_4_bound = sqrt(sqrt(static_cast<T>(560) * ulp));
136  constexpr T taylor_6_bound = sqrt(cbrt(static_cast<T>(16632) * ulp));
137  constexpr T taylor_8_bound = sqrt(sqrt(sqrt(static_cast<T>(363242880) * ulp / static_cast<T>(691))));
138  constexpr T taylor_12_bound = sqrt(sqrt(cbrt(static_cast<T>(2117187072000) * ulp / static_cast<T>(3617))));
139 
140  double absx = std::abs(x);
141  if(absx >= taylor_12_bound)
142  {
143  T x2 = x * x;
144  return (x + std::sin(x)) / (static_cast<T>(2) * x2 * (1 - std::cos(x))) - static_cast<T>(2) / (x2 * x);
145  }
146  else
147  {
148  // approximation by taylor series in x at 0 up to order 1
149  T result = static_cast<T>(1);
150 
151  if(absx >= taylor_2_bound)
152  {
153  T x2 = x * x;
154  // approximation by taylor series in x at 0 up to order 3
155  result += x2 / static_cast<T>(21);
156 
157  if(absx >= taylor_4_bound)
158  {
159  T x4 = x2 * x2;
160  // approximation by taylor series in x at 0 up to order 5
161  result += x4 / static_cast<T>(560);
162  if(absx >= taylor_6_bound)
163  {
164  // approximation by taylor series in x at 0 up to order 7
165  result += (x2 * x4) / static_cast<T>(16632);
166 
167  if(absx >= taylor_8_bound)
168  {
169  T x8 = x4 * x4;
170  // approximation by taylor series in x at 0 up to order 11
171  result += (static_cast<T>(691) * x8) / static_cast<T>(363242880) + (x2 * x8) / static_cast<T>(17297280);
172  }
173  }
174  }
175  }
176 
177  return x * result / static_cast<T>(360);
178  }
179 }
180 
181 } // namespace details
182 
186 template<typename T>
187 T sinc(const T x)
188 {
189  constexpr T taylor_0_bound = std::numeric_limits<double>::epsilon();
190  constexpr T taylor_2_bound = details::sqrt(taylor_0_bound);
191  constexpr T taylor_n_bound = details::sqrt(taylor_2_bound);
192 
193  if(std::abs(x) >= taylor_n_bound)
194  {
195  return (std::sin(x) / x);
196  }
197  else
198  {
199  // approximation by taylor series in x at 0 up to order 0
200  T result = static_cast<T>(1);
201 
202  if(std::abs(x) >= taylor_0_bound)
203  {
204  T x2 = x * x;
205 
206  // approximation by taylor series in x at 0 up to order 2
207  result -= x2 / static_cast<T>(6);
208 
209  if(std::abs(x) >= taylor_2_bound)
210  {
211  // approximation by taylor series in x at 0 up to order 4
212  result += (x2 * x2) / static_cast<T>(120);
213  }
214  }
215 
216  return (result);
217  }
218 }
219 
224 template<typename T>
225 T sinc_inv(const T x)
226 {
227  constexpr T taylor_0_bound = std::numeric_limits<T>::epsilon();
228  constexpr T taylor_2_bound = details::sqrt(taylor_0_bound);
229  constexpr T taylor_n_bound = details::sqrt(taylor_2_bound);
230 
231  // We use the 4th order taylor series around 0 of x/sin(x) to compute
232  // this function:
233  //
234  // x^2 7x^4
235  // 1 + ── + ──── + O(x^6)
236  // 6 360
237  // this approximation is valid around 0.
238  // if x is far from 0, our approximation is not valid
239  // since x^6 becomes non negligible we use the normal computation of the function
240  // (i.e. taylor_2_bound^6 + taylor_0_bound == taylor_0_bound but
241  // taylor_n_bound^6 + taylor_0_bound != taylor_0).
242 
243  if(std::abs(x) >= taylor_n_bound)
244  {
245  return (x / std::sin(x));
246  }
247  else
248  {
249  // x is below taylor_n_bound so we don't care about the 6th order term of
250  // the taylor series.
251  // We set the 0 order term.
252  T result = static_cast<T>(1);
253 
254  if(std::abs(x) >= taylor_0_bound)
255  {
256  // x is above the machine epsilon so x^2 is meaningful.
257  T x2 = x * x;
258  result += x2 / static_cast<T>(6);
259 
260  if(std::abs(x) >= taylor_2_bound)
261  {
262  // x is above the machine sqrt(epsilon) so x^4 is meaningful.
263  result += static_cast<T>(7) * (x2 * x2) / static_cast<T>(360);
264  }
265  }
266 
267  return (result);
268  }
269 }
270 
281 template<typename T>
283 {
284  auto nu = u.norm();
285 
287  auto f2 = details::SO3JacF2(nu);
288  auto xx = f2 * u.x() * u.x();
289  auto xy = f2 * u.x() * u.y();
290  auto xz = f2 * u.x() * u.z();
291  auto yy = f2 * u.y() * u.y();
292  auto yz = f2 * u.y() * u.z();
293  auto zz = f2 * u.z() * u.z();
294  // clang-format off
295  C2 << -yy - zz, xy , xz,
296  xy , -xx - zz, yz,
297  xz , yz , -xx - yy;
298  // clang-format on
299 
301 
302  return Eigen::Matrix3<T>::Identity() + C + C2;
303 }
304 
310 template<typename T>
312 {
313  auto nu = u.norm();
316  auto f2 = details::SO3JacF2(nu);
317  auto df2 = (u.dot(du) / nu) * details::dSO3JacF2(nu);
318  auto xx = df2 * u.x() * u.x();
319  auto xy = df2 * u.x() * u.y();
320  auto xz = df2 * u.x() * u.z();
321  auto yy = df2 * u.y() * u.y();
322  auto yz = df2 * u.y() * u.z();
323  auto zz = df2 * u.z() * u.z();
324  auto dxx = 2 * f2 * du.x() * u.x();
325  auto dyy = 2 * f2 * du.y() * u.y();
326  auto dzz = 2 * f2 * du.z() * u.z();
327  auto c12 = f2 * (du.y() * u.x() + du.x() * u.y());
328  auto c13 = f2 * (du.z() * u.x() + du.x() * u.z());
329  auto c23 = f2 * (du.z() * u.y() + du.y() * u.z());
330  // clang-format off
331  C2 << -yy - zz, xy , xz,
332  xy , -xx - zz, yz,
333  xz , yz , -xx - yy;
334  C3 << -dyy - dzz, c12 , c13,
335  c12 , -dxx - dzz, c23,
336  c13 , c23 , -dxx - dyy;
337  // clang-format on
338 
340 
341  return C + C2 + C3;
342 }
343 
344 } // namespace sva
sva::details::cbrt
constexpr T cbrt(T x)
Definition: MathFunc.h:69
Eigen::Matrix3
Matrix< T, 3, 3 > Matrix3
Definition: EigenTypedef.h:20
sva::sinc_inv
T sinc_inv(const T x)
Definition: MathFunc.h:225
sva::details::SO3JacF2
T SO3JacF2(const T &x)
Definition: MathFunc.h:77
sva::SO3RightJacInv
Eigen::Matrix3< T > SO3RightJacInv(const Eigen::Vector3< T > &u)
Definition: MathFunc.h:282
sva::details::eq
constexpr bool eq(T x, T y)
Definition: MathFunc.h:40
sva
Definition: ABInertia.h:10
Eigen::vector3ToCrossMatrix
Matrix3< T > vector3ToCrossMatrix(const Vector3< T > &vec)
Definition: EigenUtility.h:16
Eigen::Vector3
Matrix< T, 3, 1 > Vector3
Definition: EigenTypedef.h:18
sva::sinc
T sinc(const T x)
Definition: MathFunc.h:187
sva::SO3RightJacInvDot
Eigen::Matrix3< T > SO3RightJacInvDot(const Eigen::Vector3< T > &u, const Eigen::Vector3< T > &du)
Definition: MathFunc.h:311
sva::details::dSO3JacF2
T dSO3JacF2(const T &x)
Definition: MathFunc.h:125
sva::details::sqrtNewtonRaphson
constexpr T sqrtNewtonRaphson(T x, T curr, T prev)
Definition: MathFunc.h:16
sva::details::sqrt
constexpr T sqrt(T x)
Definition: MathFunc.h:33
sva::details::cbrtNewtonRaphson
constexpr T cbrtNewtonRaphson(T x, T curr, T prev)
Definition: MathFunc.h:46
sva::details::cbrtSub
constexpr T cbrtSub(T x)
Definition: MathFunc.h:54