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.

Median.cpp 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. #include "geometry/Median.hpp"
  2. #include <iostream>
  3. #include "geometry/Basics.hpp"
  4. #include "geometry/Featurization.hpp"
  5. #include "geometry/Mean.hpp"
  6. namespace Geometry {
  7. //---------------------------------------------------------------------------------------------------
  8. double Median(const Eigen::MatrixXd& m)
  9. {
  10. const std::vector<double> v(m.data(), m.data() + m.rows() * m.cols());
  11. return Median(v);
  12. }
  13. //---------------------------------------------------------------------------------------------------
  14. //---------------------------------------------------------------------------------------------------
  15. bool Median(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter, const EMetric& metric)
  16. {
  17. if (matrices.empty()) { return false; } // If no matrix in vector
  18. if (matrices.size() == 1) // If just one matrix in vector
  19. {
  20. median = matrices[0];
  21. return true;
  22. }
  23. if (!HaveSameSize(matrices)) // If different sizes
  24. {
  25. std::cout << "Matrices have different sizes." << std::endl;
  26. return false;
  27. }
  28. if (!IsSquare(matrices[0]) && metric == EMetric::Riemann) // If non square for Riemann metric
  29. {
  30. std::cout << "Non Square Matrix is invalid with " << toString(metric) << " metric." << std::endl;
  31. return false;
  32. }
  33. switch (metric)
  34. {
  35. case EMetric::Riemann: return MedianRiemann(matrices, median, epsilon, maxIter);
  36. case EMetric::Euclidian: return MedianEuclidian(matrices, median, epsilon, maxIter);
  37. case EMetric::Identity: return MedianIdentity(matrices, median);
  38. case EMetric::LogEuclidian:
  39. case EMetric::LogDet:
  40. case EMetric::Kullback:
  41. case EMetric::ALE:
  42. case EMetric::Harmonic:
  43. case EMetric::Wasserstein:
  44. std::cout << toString(metric) << " metric not implemented." << std::endl;
  45. return false;
  46. }
  47. return true;
  48. }
  49. //---------------------------------------------------------------------------------------------------
  50. //---------------------------------------------------------------------------------------------------
  51. bool MedianEuclidian(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter)
  52. {
  53. if (matrices.empty() || matrices[0].size() == 0) { return false; }
  54. const size_t n = matrices.size(); // Number of sample
  55. // Initial Median is the median of each channel in all matrix of dataset
  56. median = matrices[0]; // to copy size
  57. for (size_t i = 0; i < size_t(median.size()); ++i)
  58. {
  59. std::vector<double> tmp;
  60. tmp.reserve(n); // Reserve to optimize (a little) the pushback memory access.
  61. for (const auto& cov : matrices) { tmp.push_back(cov.data()[i]); } // Stack value number i of all matrix
  62. median.data()[i] = Median(tmp);
  63. }
  64. size_t iter = 0; // number of iteration
  65. double gain = epsilon; // Gain since last compute
  66. while (iter < maxIter && gain >= epsilon)
  67. {
  68. Eigen::MatrixXd prev = median; // Keep old median
  69. median.setZero(); // Reset median
  70. double sumCoefs = 0; // Sum of Coefficient
  71. for (const auto& cov : matrices)
  72. {
  73. //Eigen::MatrixXd difference = cov - prev;
  74. //double coef = sqrt(difference.cwiseProduct(difference).sum());
  75. if (cov.isApprox(prev)) { continue; } // In this case, Median is exactly this current matrix so we don't consider this matrix
  76. double coef = (cov - prev).norm();
  77. // Personnal hack and security
  78. coef = 1.0 / coef;
  79. sumCoefs += coef; // Sum for normalization
  80. median += coef * cov; // Add to the new median
  81. }
  82. if (sumCoefs > 0.0) { median /= sumCoefs; } // Normalize
  83. gain = (median - prev).norm() / median.norm(); // It's the Frobenius norm
  84. iter++;
  85. }
  86. return true;
  87. }
  88. //---------------------------------------------------------------------------------------------------
  89. //---------------------------------------------------------------------------------------------------
  90. bool MedianRiemann(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter)
  91. {
  92. if (matrices.empty() || !IsSquare(matrices[0])) { return false; }
  93. const size_t n = matrices.size(); // Number of sample
  94. const size_t nf = matrices[0].rows() * (matrices[0].rows() + 1) / 2; // Number of Features in tangent space
  95. size_t iter = 0; // number of iteration
  96. if (!MeanEuclidian(matrices, median)) { return false; } // Initialize Median
  97. double gain = epsilon; // Gain since last compute
  98. std::vector<Eigen::MatrixXd> mats;
  99. mats.reserve(n);
  100. for (const auto& m : matrices) { mats.push_back(m); }
  101. while (iter < maxIter)
  102. {
  103. // Compute Tangent space of all matrices & sum of euclidian distance of each transposed matrix
  104. std::vector<Eigen::RowVectorXd> ts(n);
  105. double sum = 0.0;
  106. for (size_t i = 0; i < n; ++i)
  107. {
  108. if (!TangentSpace(mats[i], ts[i], median)) { return false; }
  109. sum += sqrt(ts[i].cwiseAbs2().sum());
  110. }
  111. if (std::abs((sum - gain) / gain) < epsilon) { break; } // std::abs call fabs to keep type
  112. // Arithmetic median in tangent space
  113. std::vector<std::vector<double>> transposeTs(nf, std::vector<double>(n));
  114. Eigen::RowVectorXd featureMedian(nf);
  115. for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < nf; ++j) { transposeTs[j][i] = ts[i][j]; } }
  116. for (size_t j = 0; j < nf; ++j) { featureMedian[j] = Median(transposeTs[j]); }
  117. // back to the manifold
  118. Eigen::MatrixXd tmp;
  119. if (!UnTangentSpace(featureMedian, tmp, median)) { return false; }
  120. gain = sum; // Update gain
  121. median = tmp; // Update Median
  122. iter++;
  123. }
  124. return true;
  125. }
  126. //---------------------------------------------------------------------------------------------------
  127. //---------------------------------------------------------------------------------------------------
  128. bool MedianIdentity(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median)
  129. {
  130. median = Eigen::MatrixXd::Identity(matrices[0].rows(), matrices[0].cols());
  131. return true;
  132. }
  133. //---------------------------------------------------------------------------------------------------
  134. } // namespace Geometry