Bayes Filters Library
utils.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016-2019 Istituto Italiano di Tecnologia (IIT)
3  *
4  * This software may be modified and distributed under the terms of the
5  * BSD 3-Clause license. See the accompanying LICENSE file for details.
6  */
7 
8 #ifndef UTILS_H
9 #define UTILS_H
10 
11 #include <Eigen/Dense>
12 
13 #include <chrono>
14 #include <cmath>
15 #include <memory>
16 #include <string>
17 
18 namespace bfl
19 {
20 namespace utils
21 {
22 
28 template<bool B, class T = void >
29 using enable_if_t = typename std::enable_if<B,T>::type;
30 
31 
55 template<typename T, typename ...Args>
56 std::unique_ptr<T> make_unique(Args&& ...args)
57 {
58  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
59 }
60 
61 
71 template<typename Derived>
72 double log_sum_exp(const Eigen::MatrixBase<Derived>& data)
73 {
74  double max = static_cast<double>(data.maxCoeff());
75 
76  return max + std::log(static_cast<double>((data.array() - max).exp().sum()));
77 }
78 
79 
80 
98 template<typename Derived, typename DerivedScalar = typename Derived::Scalar, bfl::utils::enable_if_t<std::is_floating_point<DerivedScalar>::value, bool> = true>
99 Eigen::Matrix<DerivedScalar, 3, Eigen::Dynamic> quaternion_to_rotation_vector(const Eigen::MatrixBase<Derived>& quaternion)
100 {
101  Eigen::Matrix<DerivedScalar, 3, Eigen::Dynamic> rotation_vectors(3, quaternion.cols());
102 
103  for (std::size_t i = 0; i < quaternion.cols(); ++i)
104  {
105  const DerivedScalar norm_n = quaternion.col(i).tail(3).norm();
106  if (norm_n > 1e-4)
107  {
108  const DerivedScalar w = quaternion.col(i)(0);
109  if (w < 0)
110  rotation_vectors.col(i) = - 2.0 * std::acos(-w) * quaternion.col(i).tail(3) / norm_n;
111  else
112  rotation_vectors.col(i) = 2.0 * std::acos(w) * quaternion.col(i).tail(3) / norm_n;
113  }
114  else
115  rotation_vectors.col(i) = Eigen::Matrix<DerivedScalar, 3, 1>::Zero();
116  }
117 
118  return rotation_vectors;
119 }
120 
121 
138 template<typename Derived, typename DerivedScalar = typename Derived::Scalar, bfl::utils::enable_if_t<std::is_floating_point<DerivedScalar>::value, bool> = true>
139 Eigen::Matrix<DerivedScalar, 4, Eigen::Dynamic> rotation_vector_to_quaternion(const Eigen::MatrixBase<Derived>& rotation_vector)
140 {
141  Eigen::Matrix<DerivedScalar, 4, Eigen::Dynamic> quaternions(4, rotation_vector.cols());
142  for (std::size_t i = 0; i < rotation_vector.cols(); ++i)
143  {
144  const DerivedScalar norm_r = rotation_vector.col(i).norm();
145  if (norm_r > 1e-4)
146  {
147  quaternions.col(i)(0) = std::cos(norm_r / 2.0);
148  quaternions.col(i).tail(3) = std::sin(norm_r / 2.0) * rotation_vector.col(i) / norm_r;
149  }
150  else
151  {
152  quaternions.col(i)(0) = 1.0;
153  quaternions.col(i).tail(3) = Eigen::Matrix<DerivedScalar, 3, 1>::Zero();
154  }
155  }
156 
157  return quaternions;
158 }
159 
160 
182 template<typename Derived, typename DerivedArg2, typename DerivedScalar = typename Derived::Scalar, typename DerivedArg2Scalar = typename DerivedArg2::Scalar,
183  bfl::utils::enable_if_t<std::is_floating_point<DerivedScalar>::value && std::is_same<DerivedScalar, DerivedArg2Scalar>::value, bool> = true>
184 Eigen::Matrix<DerivedScalar, 4, Eigen::Dynamic> sum_quaternion_rotation_vector(const Eigen::MatrixBase<Derived>& quaternion, const Eigen::MatrixBase<DerivedArg2>& rotation_vector)
185 {
186  /* Move rotation vectors to their quaternionic representation. */
187  Eigen::Matrix<DerivedScalar, 4, Eigen::Dynamic> vector_as_quaternion = rotation_vector_to_quaternion(rotation_vector);
188 
189  Eigen::Quaternion<DerivedScalar> q_right(quaternion.col(0)(0), quaternion.col(0)(1), quaternion.col(0)(2), quaternion.col(0)(3));
190 
191  Eigen::Matrix<DerivedScalar, 4, Eigen::Dynamic> quaternions(4, rotation_vector.cols());
192  for (std::size_t i = 0; i < rotation_vector.cols(); ++i)
193  {
194  Eigen::Quaternion<DerivedScalar> q_left(vector_as_quaternion.col(i)(0), vector_as_quaternion.col(i)(1), vector_as_quaternion.col(i)(2), vector_as_quaternion.col(i)(3));
195  Eigen::Quaternion<DerivedScalar> sum = q_left * q_right;
196 
197  quaternions.col(i)(0) = sum.w();
198  quaternions.col(i).tail(3) = sum.vec();
199  }
200 
201  return quaternions;
202 }
203 
204 
225 template<typename Derived, typename DerivedArg2, typename DerivedScalar = typename Derived::Scalar, typename DerivedArg2Scalar = typename DerivedArg2::Scalar,
226  bfl::utils::enable_if_t<std::is_floating_point<DerivedScalar>::value && std::is_same<DerivedScalar, DerivedArg2Scalar>::value, bool> = true>
227 Eigen::Matrix<DerivedScalar, 3, Eigen::Dynamic> diff_quaternion(const Eigen::MatrixBase<Derived>& quaternion_left, const Eigen::MatrixBase<DerivedArg2>& quaternion_right)
228 {
229  Eigen::Matrix<DerivedScalar, 4, Eigen::Dynamic> products(4, quaternion_left.cols());
230 
231  Eigen::Quaternion<DerivedScalar> q_right(quaternion_right.col(0)(0), quaternion_right.col(0)(1), quaternion_right.col(0)(2), quaternion_right.col(0)(3));
232  Eigen::Quaternion<DerivedScalar> q_right_conj = q_right.conjugate();
233 
234  /* Products between each left quaternion and the conjugated right quaternion. */
235  for (std::size_t i = 0; i < quaternion_left.cols(); ++i)
236  {
237  Eigen::Quaternion<DerivedScalar> q_left(quaternion_left.col(i)(0), quaternion_left.col(i)(1), quaternion_left.col(i)(2), quaternion_left.col(i)(3));
238  Eigen::Quaternion<DerivedScalar> product = q_left * q_right_conj;
239 
240  products.col(i)(0) = product.w();
241  products.col(i).tail(3) = product.vec();
242  }
243 
244  /* Express displacements in the tangent space. */
245  return quaternion_to_rotation_vector(products);
246 }
247 
248 
269 template<typename Derived, typename DerivedArg2, typename DerivedScalar = typename Derived::Scalar, typename DerivedArg2Scalar = typename DerivedArg2::Scalar,
270  bfl::utils::enable_if_t<std::is_floating_point<DerivedScalar>::value && std::is_same<DerivedScalar, DerivedArg2Scalar>::value, bool> = true>
271 Eigen::Matrix<DerivedScalar, 4, 1> mean_quaternion(const Eigen::MatrixBase<Derived>& weight, const Eigen::MatrixBase<DerivedArg2>& quaternion)
272 {
273  /* Weighted outer product of quaternions. */
274  Eigen::Matrix<DerivedScalar, 4, 4> outer_product_mean = Eigen::Matrix<DerivedScalar, 4, 4>::Zero();
275  for (std::size_t i = 0; i < weight.rows(); ++i)
276  outer_product_mean.noalias() += weight.col(0)(i) * quaternion.col(i) * quaternion.col(i).transpose();
277 
278  /* Take the weighted mean as the eigenvector corresponding to the maximum eigenvalue. */
279  Eigen::EigenSolver<Eigen::Matrix<DerivedScalar, 4, 4>> eigen_solver(outer_product_mean);
280  Eigen::Matrix<std::complex<DerivedScalar>, 4, 1> eigenvalues(eigen_solver.eigenvalues());
281  int maximum_index;
282  eigenvalues.real().maxCoeff(&maximum_index);
283  return eigen_solver.eigenvectors().real().block(0, maximum_index, 4, 1);
284 }
285 
286 
296 template<typename Derived>
297 Eigen::VectorXd multivariate_gaussian_log_density(const Eigen::MatrixBase<Derived>& input, const Eigen::Ref<const Eigen::VectorXd>& mean, const Eigen::Ref<const Eigen::MatrixXd>& covariance)
298 {
299  const auto diff = input.colwise() - mean;
300 
301  Eigen::VectorXd values(diff.cols());
302  for (std::size_t i = 0; i < diff.cols(); i++)
303  values(i) = - 0.5 * (static_cast<double>(diff.rows()) * std::log(2.0 * M_PI) + std::log(covariance.determinant()) + (diff.col(i).transpose() * covariance.inverse() * diff.col(i)));
304 
305  return values;
306 }
307 
308 
330 template<typename Derived>
331 Eigen::VectorXd multivariate_gaussian_log_density_UVR(const Eigen::MatrixBase<Derived>& input, const Eigen::Ref<const Eigen::VectorXd>& mean, const Eigen::Ref<const Eigen::MatrixXd>& U, const Eigen::Ref<const Eigen::MatrixXd>& V, const Eigen::Ref<const Eigen::MatrixXd>& R)
332 {
333  std::size_t input_size = input.rows();
334  std::size_t block_size = R.rows();
335  std::size_t num_blocks = input_size / block_size;
336 
337  const auto diff = input.colwise() - mean;
338 
339  /* Evaluate inv(R) */
340  Eigen::MatrixXd inv_R(block_size, input_size);
341  if (R.cols() == block_size)
342  {
343  // The diagonal blocks of R are all equal
344  Eigen::MatrixXd inv_R_single = R.inverse();
345  for (std::size_t i = 0; i < num_blocks; i++)
346  {
347  inv_R.block(0, block_size * i, block_size, block_size) = inv_R_single;
348  }
349  }
350  else
351  {
352  // The diagonal blocks of R are different
353  for (std::size_t i = 0; i < num_blocks; i++)
354  {
355  inv_R.block(0, block_size * i, block_size, block_size) = R.block(0, block_size * i, block_size, block_size).inverse();
356  }
357  }
358 
359  /* Evaluate V * inv(R) */
360  Eigen::MatrixXd V_inv_R(V.rows(), V.cols());
361  for (std::size_t i = 0; i < V.cols() / block_size; i++)
362  {
363  V_inv_R.middleCols(i * block_size, block_size) = V.middleCols(i * block_size, block_size) * inv_R.block(0, block_size * i, block_size, block_size);
364  }
365 
366  /* Evaluate diff^{T} * inv(R) */
367  Eigen::MatrixXd diff_T_inv_R(input.cols(), input.rows());
368  for (std::size_t i = 0; i < num_blocks; i++)
369  {
370  diff_T_inv_R.middleCols(i * block_size, block_size) = diff.middleRows(i * block_size, block_size).transpose() * inv_R.block(0, block_size * i, block_size, block_size);
371  }
372 
373  /* Evaluate I + V * inv(R) * U */
374  Eigen::MatrixXd I_V_inv_R_U = Eigen::MatrixXd::Identity(V.rows(), V.rows()) + V_inv_R * U;
375 
381  Eigen::VectorXd weighted_diffs(input.cols());
382  for (std::size_t i = 0; i < weighted_diffs.size(); i++)
383  weighted_diffs(i) = diff_T_inv_R.row(i) * (Eigen::MatrixXd::Identity(U.rows(), U.rows()) - U * I_V_inv_R_U.inverse() * V_inv_R) * diff.col(i);
384 
390  double det_S;
391  double det_R = 1.0;
392  if (R.cols() == block_size)
393  det_R = std::pow(R.determinant(), num_blocks);
394  else
395  for (std::size_t i = 0; i < num_blocks; i++)
396  det_R *= R.block(0, block_size * i, block_size, block_size).determinant();
397 
398  det_S = det_R * I_V_inv_R_U.determinant();
399 
400  /* Evaluate the full logarithm density */
401  Eigen::VectorXd values(input.cols());
402  for (std::size_t i = 0; i < input.cols(); i++)
403  values(i) = - 0.5 * (static_cast<double>(diff.rows()) * std::log(2.0 * M_PI) + std::log(det_S) + weighted_diffs(i));
404 
405  return values;
406 }
407 
408 
418 template<typename Derived>
419 Eigen::VectorXd multivariate_gaussian_density(const Eigen::MatrixBase<Derived>& input, const Eigen::Ref<const Eigen::VectorXd>& mean, const Eigen::Ref<const Eigen::MatrixXd>& covariance)
420 {
421  return multivariate_gaussian_log_density(input, mean, covariance).array().exp();
422 }
423 
424 
446 template<typename Derived>
447 Eigen::VectorXd multivariate_gaussian_density_UVR(const Eigen::MatrixBase<Derived>& input, const Eigen::Ref<const Eigen::VectorXd>& mean, const Eigen::Ref<const Eigen::MatrixXd>& U, const Eigen::Ref<const Eigen::MatrixXd>& V, const Eigen::Ref<const Eigen::MatrixXd>& R)
448 {
449  return multivariate_gaussian_log_density_UVR(input, mean, U, V, R).array().exp();
450 }
451 
452 
462 template<typename timetype = std::chrono::milliseconds>
463 class CpuTimer
464 {
465 public:
469  void start()
470  {
471  start_time_ = std::chrono::steady_clock::now();
472 
473  running_ = true;
474  }
475 
476 
480  void stop()
481  {
482  stop_time_ = std::chrono::steady_clock::now();
483 
484  running_ = false;
485  }
486 
487 
494  double elapsed()
495  {
496  std::chrono::steady_clock::duration time_span;
497 
498  if (!running_)
499  time_span = stop_time_ - start_time_;
500  else
501  time_span = std::chrono::steady_clock::now() - start_time_;
502 
503  return static_cast<double>(time_span.count()) * std::chrono::steady_clock::period::num / std::chrono::steady_clock::period::den * timetype::period::den;
504  }
505 
506 
512  double now()
513  {
514  return static_cast<double>(std::chrono::steady_clock::now().time_since_epoch().count()) * std::chrono::steady_clock::period::num / std::chrono::steady_clock::period::den * timetype::period::den;
515  }
516 
517 
523  bool is_running()
524  {
525  return running_;
526  }
527 
528 private:
532  std::chrono::steady_clock::time_point start_time_ = std::chrono::steady_clock::now();
533 
534 
538  std::chrono::steady_clock::time_point stop_time_ = start_time_;
539 
540 
544  bool running_ = false;
545 };
546 
547 
558 inline std::string throw_message(const std::string& from_where, const std::string& error_message, const std::string& data_log = "")
559 {
560  if (from_where.empty() || error_message.empty())
561  return "UTILS::THROW_MESSAGE::EMPTY_THROW_REPORT";
562 
563  std::string message;
564 
565  message = "ERROR::" + from_where + "\n";
566  message += "MESSAGE:\n";
567  message += "\t" + error_message + "\n";
568 
569  if (!data_log.empty())
570  {
571  message += "LOG:\n";
572  message += "\t" + data_log + "\n";
573  }
574 
575  return message;
576 }
577 
578 }
579 }
580 
581 
582 #endif /* UTILS_H */
bfl::utils::mean_quaternion
Eigen::Matrix< DerivedScalar, 4, 1 > mean_quaternion(const Eigen::MatrixBase< Derived > &weight, const Eigen::MatrixBase< DerivedArg2 > &quaternion)
Evaluate the weighted mean of a set of unit quaternions given a set of weights .
Definition: utils.h:271
bfl::utils::CpuTimer::elapsed
double elapsed()
Get the time passed between a start() and stop() call.
Definition: utils.h:494
bfl::utils::log_sum_exp
double log_sum_exp(const Eigen::MatrixBase< Derived > &data)
Return the element-wise logarithm of the sum of exponentials of the input data.
Definition: utils.h:72
bfl
Port of boost::any for C++11 compilers.
Definition: AdditiveMeasurementModel.h:13
bfl::utils::CpuTimer::start
void start()
Start the timer.
Definition: utils.h:469
bfl::utils::CpuTimer::running_
bool running_
Running status flag.
Definition: utils.h:544
bfl::utils::CpuTimer
This template class provides methods to keep track of time.
Definition: utils.h:463
bfl::utils::diff_quaternion
Eigen::Matrix< DerivedScalar, 3, Eigen::Dynamic > diff_quaternion(const Eigen::MatrixBase< Derived > &quaternion_left, const Eigen::MatrixBase< DerivedArg2 > &quaternion_right)
Evaluate the colwise difference between a set of unit quaternions and a unit quaternion .
Definition: utils.h:227
bfl::utils::sum_quaternion_rotation_vector
Eigen::Matrix< DerivedScalar, 4, Eigen::Dynamic > sum_quaternion_rotation_vector(const Eigen::MatrixBase< Derived > &quaternion, const Eigen::MatrixBase< DerivedArg2 > &rotation_vector)
Evaluate the colwise sum between a unit quaternion and a set of rotation vectors in the tangent spa...
Definition: utils.h:184
bfl::utils::CpuTimer::is_running
bool is_running()
Check if the timer is running or not.
Definition: utils.h:523
bfl::utils::CpuTimer::now
double now()
Get the absolute time from epoch.
Definition: utils.h:512
bfl::utils::throw_message
std::string throw_message(const std::string &from_where, const std::string &error_message, const std::string &data_log="")
Utility function that returns a pre-formatted string for throwing exception reports.
Definition: utils.h:558
bfl::utils::multivariate_gaussian_density
Eigen::VectorXd multivariate_gaussian_density(const Eigen::MatrixBase< Derived > &input, const Eigen::Ref< const Eigen::VectorXd > &mean, const Eigen::Ref< const Eigen::MatrixXd > &covariance)
Evaluate a multivariate Gaussian probability density function.
Definition: utils.h:419
bfl::utils::multivariate_gaussian_density_UVR
Eigen::VectorXd multivariate_gaussian_density_UVR(const Eigen::MatrixBase< Derived > &input, const Eigen::Ref< const Eigen::VectorXd > &mean, const Eigen::Ref< const Eigen::MatrixXd > &U, const Eigen::Ref< const Eigen::MatrixXd > &V, const Eigen::Ref< const Eigen::MatrixXd > &R)
Evaluate the logarithm of a multivariate Gaussian probability density function using the Sherman–Morr...
Definition: utils.h:447
bfl::utils::CpuTimer::stop_time_
std::chrono::steady_clock::time_point stop_time_
The stop time.
Definition: utils.h:538
bfl::utils::enable_if_t
typename std::enable_if< B, T >::type enable_if_t
Helper type 'enable_if_t'.
Definition: utils.h:29
bfl::utils::rotation_vector_to_quaternion
Eigen::Matrix< DerivedScalar, 4, Eigen::Dynamic > rotation_vector_to_quaternion(const Eigen::MatrixBase< Derived > &rotation_vector)
Convert a matrix of rotation vectors in the tangent space at the identity to their unitary quaternio...
Definition: utils.h:139
bfl::utils::quaternion_to_rotation_vector
Eigen::Matrix< DerivedScalar, 3, Eigen::Dynamic > quaternion_to_rotation_vector(const Eigen::MatrixBase< Derived > &quaternion)
Convert a matrix of unit quaternions to their rotation vector representation in the tangent space a...
Definition: utils.h:99
bfl::utils::make_unique
std::unique_ptr< T > make_unique(Args &&...args)
Constructs an object of type T and wraps it in a std::unique_ptr.
Definition: utils.h:56
bfl::utils::CpuTimer::stop
void stop()
Stop the timer.
Definition: utils.h:480
bfl::utils::CpuTimer::start_time_
std::chrono::steady_clock::time_point start_time_
The start time.
Definition: utils.h:532
bfl::utils::multivariate_gaussian_log_density_UVR
Eigen::VectorXd multivariate_gaussian_log_density_UVR(const Eigen::MatrixBase< Derived > &input, const Eigen::Ref< const Eigen::VectorXd > &mean, const Eigen::Ref< const Eigen::MatrixXd > &U, const Eigen::Ref< const Eigen::MatrixXd > &V, const Eigen::Ref< const Eigen::MatrixXd > &R)
Evaluate the logarithm of a multivariate Gaussian probability density function using the Sherman–Morr...
Definition: utils.h:331
bfl::utils::multivariate_gaussian_log_density
Eigen::VectorXd multivariate_gaussian_log_density(const Eigen::MatrixBase< Derived > &input, const Eigen::Ref< const Eigen::VectorXd > &mean, const Eigen::Ref< const Eigen::MatrixXd > &covariance)
Evaluate the logarithm of a multivariate Gaussian probability density function.
Definition: utils.h:297