init
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
#include "geometry/Median.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Featurization.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Median(const Eigen::MatrixXd& m)
|
||||
{
|
||||
const std::vector<double> v(m.data(), m.data() + m.rows() * m.cols());
|
||||
return Median(v);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Median(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter, const EMetric& metric)
|
||||
{
|
||||
if (matrices.empty()) { return false; } // If no matrix in vector
|
||||
if (matrices.size() == 1) // If just one matrix in vector
|
||||
{
|
||||
median = matrices[0];
|
||||
return true;
|
||||
}
|
||||
if (!HaveSameSize(matrices)) // If different sizes
|
||||
{
|
||||
std::cout << "Matrices have different sizes." << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!IsSquare(matrices[0]) && metric == EMetric::Riemann) // If non square for Riemann metric
|
||||
{
|
||||
std::cout << "Non Square Matrix is invalid with " << toString(metric) << " metric." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (metric)
|
||||
{
|
||||
case EMetric::Riemann: return MedianRiemann(matrices, median, epsilon, maxIter);
|
||||
case EMetric::Euclidian: return MedianEuclidian(matrices, median, epsilon, maxIter);
|
||||
case EMetric::Identity: return MedianIdentity(matrices, median);
|
||||
case EMetric::LogEuclidian:
|
||||
case EMetric::LogDet:
|
||||
case EMetric::Kullback:
|
||||
case EMetric::ALE:
|
||||
case EMetric::Harmonic:
|
||||
case EMetric::Wasserstein:
|
||||
std::cout << toString(metric) << " metric not implemented." << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianEuclidian(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter)
|
||||
{
|
||||
if (matrices.empty() || matrices[0].size() == 0) { return false; }
|
||||
const size_t n = matrices.size(); // Number of sample
|
||||
|
||||
// Initial Median is the median of each channel in all matrix of dataset
|
||||
median = matrices[0]; // to copy size
|
||||
for (size_t i = 0; i < size_t(median.size()); ++i)
|
||||
{
|
||||
std::vector<double> tmp;
|
||||
tmp.reserve(n); // Reserve to optimize (a little) the pushback memory access.
|
||||
for (const auto& cov : matrices) { tmp.push_back(cov.data()[i]); } // Stack value number i of all matrix
|
||||
median.data()[i] = Median(tmp);
|
||||
}
|
||||
|
||||
size_t iter = 0; // number of iteration
|
||||
double gain = epsilon; // Gain since last compute
|
||||
while (iter < maxIter && gain >= epsilon)
|
||||
{
|
||||
Eigen::MatrixXd prev = median; // Keep old median
|
||||
median.setZero(); // Reset median
|
||||
double sumCoefs = 0; // Sum of Coefficient
|
||||
for (const auto& cov : matrices)
|
||||
{
|
||||
//Eigen::MatrixXd difference = cov - prev;
|
||||
//double coef = sqrt(difference.cwiseProduct(difference).sum());
|
||||
if (cov.isApprox(prev)) { continue; } // In this case, Median is exactly this current matrix so we don't consider this matrix
|
||||
double coef = (cov - prev).norm();
|
||||
// Personnal hack and security
|
||||
coef = 1.0 / coef;
|
||||
sumCoefs += coef; // Sum for normalization
|
||||
median += coef * cov; // Add to the new median
|
||||
}
|
||||
if (sumCoefs > 0.0) { median /= sumCoefs; } // Normalize
|
||||
|
||||
gain = (median - prev).norm() / median.norm(); // It's the Frobenius norm
|
||||
iter++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianRiemann(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter)
|
||||
{
|
||||
if (matrices.empty() || !IsSquare(matrices[0])) { return false; }
|
||||
const size_t n = matrices.size(); // Number of sample
|
||||
const size_t nf = matrices[0].rows() * (matrices[0].rows() + 1) / 2; // Number of Features in tangent space
|
||||
size_t iter = 0; // number of iteration
|
||||
if (!MeanEuclidian(matrices, median)) { return false; } // Initialize Median
|
||||
|
||||
double gain = epsilon; // Gain since last compute
|
||||
std::vector<Eigen::MatrixXd> mats;
|
||||
mats.reserve(n);
|
||||
for (const auto& m : matrices) { mats.push_back(m); }
|
||||
while (iter < maxIter)
|
||||
{
|
||||
// Compute Tangent space of all matrices & sum of euclidian distance of each transposed matrix
|
||||
std::vector<Eigen::RowVectorXd> ts(n);
|
||||
double sum = 0.0;
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
if (!TangentSpace(mats[i], ts[i], median)) { return false; }
|
||||
sum += sqrt(ts[i].cwiseAbs2().sum());
|
||||
}
|
||||
if (std::abs((sum - gain) / gain) < epsilon) { break; } // std::abs call fabs to keep type
|
||||
|
||||
// Arithmetic median in tangent space
|
||||
std::vector<std::vector<double>> transposeTs(nf, std::vector<double>(n));
|
||||
Eigen::RowVectorXd featureMedian(nf);
|
||||
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < nf; ++j) { transposeTs[j][i] = ts[i][j]; } }
|
||||
for (size_t j = 0; j < nf; ++j) { featureMedian[j] = Median(transposeTs[j]); }
|
||||
|
||||
// back to the manifold
|
||||
Eigen::MatrixXd tmp;
|
||||
if (!UnTangentSpace(featureMedian, tmp, median)) { return false; }
|
||||
gain = sum; // Update gain
|
||||
median = tmp; // Update Median
|
||||
iter++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianIdentity(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median)
|
||||
{
|
||||
median = Eigen::MatrixXd::Identity(matrices[0].rows(), matrices[0].cols());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
} // namespace Geometry
|
||||
Reference in New Issue
Block a user