This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,223 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Basics.hpp
/// \brief Basic functions of Eigen matrix manipulation and verification.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <Eigen/Dense>
#include <vector>
#include <cmath> // Ceil
#include <type_traits> // Template type
namespace Geometry {
/// <summary> Enumeration of Standardization method for features matrix data. </summary>
enum class EStandardization
{
None, ///< No change.
Center, ///< Standardize data by removing the mean (on each feature separately).
StandardScale ///< Standardize data by removing the mean and scaling to unit variance (on each feature separately).
};
//************************************************
//******************** Matrix ********************
//************************************************
/// <summary> Apply an affine transformation and return the result (The last transpose is useless if matrix is SPD).
/// \f[
/// B = R^{-1/2} * A * {R^{-1/2}}^{\mathsf{T}}
/// \f]
/// </summary>
/// <param name="ref"> The reference matrix which transforms. </param>
/// <param name="matrix"> the matrix to transform. </param>
/// <returns> The transformed matrix </returns>
Eigen::MatrixXd AffineTransformation(const Eigen::MatrixXd& ref, const Eigen::MatrixXd& matrix);
/// <summary> Standardize data row by row with selected method (destructive operation). </summary>
/// <param name="matrix"> The matrix to standardize. </param>
/// <param name="standard"> Standard method. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MatrixStandardization(Eigen::MatrixXd& matrix, EStandardization standard = EStandardization::None);
/// <summary> Standardize data row by row with selected method (non destructive operation). </summary>
/// <param name="in"> The matrix to standardize. </param>
/// <param name="out"> The matrix standardized. </param>
/// <param name="standard"> Standard method. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MatrixStandardization(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, EStandardization standard = EStandardization::None);
/// <summary> Removes the mean of each row at the matrix (destructive operation).\n
/// So \f$\mu=0\f$. </summary>
/// <param name="matrix"> The Matrix to center. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MatrixCenter(Eigen::MatrixXd& matrix);
/// <summary> Removes the mean of each row at the matrix (non destructive operation).\n
/// So \f$\mu=0\f$. </summary>
/// <param name="in"> The Matrix to center. </param>
/// <param name="out"> The Matrix centered. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MatrixCenter(const Eigen::MatrixXd& in, Eigen::MatrixXd& out);
/// <summary> Removes the mean of each row at the matrix and divide by the variance (destructive operation with scale return).\n
/// So \f$\mu=0\f$ and \f$\sigma=1\f$. </summary>
/// <param name="matrix"> The Matrix to standardize. </param>
/// <param name="scale"> The scale vector. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> Adaptation of <a href="http://scikit-learn.org">sklearn</a> <a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html">StandardScaler</a> (<a href="https://github.com/scikit-learn/scikit-learn/blob/master/COPYING">License</a>). </remarks>
bool MatrixStandardScaler(Eigen::MatrixXd& matrix, Eigen::RowVectorXd& scale);
/// <summary> Removes the mean of each row at the matrix and divide by the variance (destructive operation).\n
/// So \f$\mu=0\f$ and \f$\sigma=1\f$. </summary>
/// <param name="matrix"> The Matrix to standardize. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> Adaptation of <a href="http://scikit-learn.org">sklearn</a> <a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html">StandardScaler</a> (<a href="https://github.com/scikit-learn/scikit-learn/blob/master/COPYING">License</a>). </remarks>
bool MatrixStandardScaler(Eigen::MatrixXd& matrix);
/// <summary> Removes the mean of each row at the matrix and divide by the variance (non destructive operation with scale return).\n
/// So \f$\mu=0\f$ and \f$\sigma=1\f$. </summary>
/// <param name="in"> The Matrix to standardize. </param>
/// <param name="out"> The Matrix standardized. </param>
/// <param name="scale"> The scale vector. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> Adaptation of <a href="http://scikit-learn.org">sklearn</a> <a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html">StandardScaler</a> (<a href="https://github.com/scikit-learn/scikit-learn/blob/master/COPYING">License</a>). </remarks>
bool MatrixStandardScaler(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, Eigen::RowVectorXd& scale);
/// <summary> Removes the mean of each row at the matrix and divide by the variance (non destructive operation).\n
/// So \f$\mu=0\f$ and \f$\sigma=1\f$. </summary>
/// <param name="in"> The Matrix to standardize. </param>
/// <param name="out"> The Matrix standardized. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> Adaptation of <a href="http://scikit-learn.org">sklearn</a> <a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html">StandardScaler</a> (<a href="https://github.com/scikit-learn/scikit-learn/blob/master/COPYING">License</a>). </remarks>
bool MatrixStandardScaler(const Eigen::MatrixXd& in, Eigen::MatrixXd& out);
/// <summary> Give the string format of Matrix. </summary>
/// <param name="matrix"> The Matrix to display. </param>
/// <returns> The string format. </returns>
std::string MatrixPrint(const Eigen::MatrixXd& matrix);
/// <summary> Check first the size, then if not empty matrix and then if they are almost equal. </summary>
/// <param name="matrix1"> First Matrix. </param>
/// <param name="matrix2"> Second Matrix. </param>
/// <param name="precision"> Precision for matrix comparison. </param>
/// <returns> <c>True</c> if the two elements are equals (with a precision tolerance), <c>False</c> otherwise. </returns>
bool AreEquals(const Eigen::MatrixXd& matrix1, const Eigen::MatrixXd& matrix2, double precision = 1e-6);
//*************************************************************
//******************** Index Manipulations ********************
//*************************************************************
/// <summary> Gets the items selected by the index. </summary>
/// <param name="row"> the original row. </param>
/// <param name="index"> Elements to select. </param>
/// <returns> Row with selected elements. </returns>
Eigen::RowVectorXd GetElements(const Eigen::RowVectorXd& row, const std::vector<size_t>& index);
/// <summary> <a href="https://numpy.org/doc/stable/reference/generated/numpy.arange.html">Numpy arange</a> implementation in C++. </summary>
/// <typeparam name="T"> Generic numeric type parameter. </typeparam>
/// <param name="start"> The start. </param>
/// <param name="stop"> The stop. </param>
/// <param name="step"> (Optional) Amount to increment by. </param>
/// <returns> vector&lt;T&gt; </returns>
template <typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
std::vector<T> ARange(const T start, const T stop, const T step = 1)
{
std::vector<T> result;
result.reserve(size_t(ceil(1.0 * (stop - start) / step)));
for (T i = start; i < stop; i += step) { result.push_back(i); }
return result;
}
/// <summary> Turn vector of vector into vector. </summary>
/// <typeparam name="T"> Generic type parameter. </typeparam>
/// <param name="in"> vector of vector. </param>
/// <returns> <c>vector&lt;T&gt;</c> </returns>
template <typename T>
std::vector<T> Vector2DTo1D(const std::vector<std::vector<T>>& in)
{
std::vector<T> result;
size_t sum = 0;
for (const auto& v : in) { sum += v.size(); }
result.reserve(sum);
for (const auto& v : in) { for (const auto& e : v) { result.push_back(e); } }
return result;
}
/// <summary> Turn vector into vector of vector with position repartition. </summary>
/// <typeparam name="T"> Generic type parameter. </typeparam>
/// <param name="in"> vector of vector. </param>
/// <param name="position"> position of element (size of position is the number of row the values are the number of element on each row). </param>
/// <returns> <c>vector&lt;T&gt;</c> </returns>
template <typename T>
std::vector<std::vector<T>> Vector1DTo2D(const std::vector<T>& in, const std::vector<size_t>& position)
{
const size_t n = position.size();
std::vector<std::vector<T>> result(n);
size_t idx = 0;
for (size_t i = 0; i < n; ++i)
{
const size_t nbSample = position[i];
result[i].resize(nbSample);
for (size_t j = 0; j < nbSample; ++j) { result[i][j] = in[idx++]; }
}
return result;
}
//***************************************************
//******************** Validates ********************
//***************************************************
/// <summary> Validate if value is in [min;max]. </summary>
/// <param name="value"> The value. </param>
/// <param name="min"> The minimum. </param>
/// <param name="max"> The maximum. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool InRange(const double value, const double min, const double max);
/// <summary> Validate if the vector is not empty and the matrices are validate. </summary>
/// <param name="matrices"> Vector of Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool AreNotEmpty(const std::vector<Eigen::MatrixXd>& matrices);
/// <summary> Validates if matrix is not empty. </summary>
/// <param name="matrix"> Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool IsNotEmpty(const Eigen::MatrixXd& matrix);
/// <summary> Validates if two matrix have same size. </summary>
/// <param name="a"> Matrix A. </param>
/// <param name="b"> Matrix B. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool HaveSameSize(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b);
/// <summary> Validate if the vector is not empty and the matrices have same size. </summary>
/// <param name="matrices"> Vector of Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool HaveSameSize(const std::vector<Eigen::MatrixXd>& matrices);
/// <summary> Validates if matrix is square matrix and not empty. </summary>
/// <param name="matrix"> Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool IsSquare(const Eigen::MatrixXd& matrix);
/// <summary> Validate if the vector is not empty and the matrices are square matrix and not empty. </summary>
/// <param name="matrices"> Vector of Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool AreSquare(const std::vector<Eigen::MatrixXd>& matrices);
//********************************************************
//******************** CSV MANAGEMENT ********************
//********************************************************
/// <summary>Return the string split by the \p sep parameter. </summary>
/// <param name="s"> The string to split. </param>
/// <param name="sep"> the separator string which splits. </param>
/// <returns> Vector of string part. </returns>
std::vector<std::string> Split(const std::string& s, const std::string& sep);
} // namespace Geometry
@@ -0,0 +1,46 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Classification.hpp
/// \brief All functions to help Matrix Classifiers.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks
/// - LSQR inspired by <a href="http://scikit-learn.org">sklearn</a> <a href="https://scikit-learn.org/stable/modules/generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html">LinearDiscriminantAnalysis</a> (<a href="https://github.com/scikit-learn/scikit-learn/blob/master/COPYING">License</a>).
/// - FgDA inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>).
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <vector>
#include <Eigen/Dense>
namespace Geometry {
/// <summary> Compute the weight of Linear Discriminant Analysis with Least squares (LSQR) Solver. </summary>
/// <param name="dataset"> The dataset (first dimension is the class, second dimension the trial as Feature Vector). </param>
/// <param name="weight"> The weight to apply. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> Inspired by <a href="http://scikit-learn.org">sklearn</a> <a href="https://scikit-learn.org/stable/modules/generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html">LinearDiscriminantAnalysis</a> (<a href="https://github.com/scikit-learn/scikit-learn/blob/master/COPYING">License</a>). </remarks>
bool LSQR(const std::vector<std::vector<Eigen::RowVectorXd>>& dataset, Eigen::MatrixXd& weight);
/// <summary> Compute Least squares (LSQR) Weight and transform to FgDA Weight. \n
/// \f[ W_{\text{FgDA}} = W^{\mathsf{T}} \times (W \times W^{\mathsf{T}})^{-1} \times W \f]
/// </summary>
/// <param name="dataset"> The dataset (first dimension is the class, second dimension is the trial as Feature Vector). </param>
/// <param name="weight"> The Weight to apply. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> Method inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>). </remarks>
bool FgDACompute(const std::vector<std::vector<Eigen::RowVectorXd>>& dataset, Eigen::MatrixXd& weight);
/// <summary> Apply the weight on the vector. (just a matrix product) </summary>
/// <param name="in"> Sample to transform. </param>
/// <param name="out"> Transformed Sample. </param>
/// <param name="weight"> The Weight to apply. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> Method inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>). </remarks>
bool FgDAApply(const Eigen::RowVectorXd& in, Eigen::RowVectorXd& out, const Eigen::MatrixXd& weight);
} // namespace Geometry
@@ -0,0 +1,224 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Covariance.hpp
/// \brief All functions to estimate the Covariance Matrix.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks
/// - List of Estimator inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>).
/// - <a href="http://scikit-learn.org/stable/modules/generated/sklearn.covariance.LedoitWolf.html">Ledoit and Wolf Estimator</a> inspired by <a href="http://scikit-learn.org">sklearn</a> (<a href="https://github.com/scikit-learn/scikit-learn/blob/master/COPYING">License</a>).
/// - <a href="http://scikit-learn.org/stable/modules/generated/sklearn.covariance.OAS.html">Oracle Approximating Shrinkage (OAS) Estimator</a> Inspired by <a href="http://scikit-learn.org">sklearn</a> (<a href="https://github.com/scikit-learn/scikit-learn/blob/master/COPYING">License</a>).
/// - <b>Minimum Covariance Determinant (MCD) Estimator isn't implemented. </b>
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/Basics.hpp"
#include <Eigen/Dense>
namespace Geometry {
//***************************************************
//******************** CONSTANTS ********************
//***************************************************
/// <summary> Enumeration of the covariance matrix estimator. Inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a>. </summary>
enum class EEstimator
{
COV, ///< The Simple Covariance Estimator.
SCM, ///< The Normalized Spatial Covariance Matrix (SCM) Estimator.
LWF, ///< The Ledoit and Wolf Estimator.
OAS, ///< The Oracle Approximating Shrinkage (OAS) Estimator.
MCD, ///< The Minimum Covariance Determinant (MCD) Estimator.
COR, ///< The Pearson Correlation Estimator.
IDE ///< The Identity Matrix.
};
/// <summary> Convert estimators to string. </summary>
/// <param name="estimator"> The estimator. </param>
/// <returns> <c>std::string</c> </returns>
inline std::string toString(const EEstimator estimator)
{
switch (estimator)
{
case EEstimator::COV: return "Covariance";
case EEstimator::SCM: return "Normalized Spatial Covariance Matrix (SCM)";
case EEstimator::LWF: return "Ledoit and Wolf";
case EEstimator::OAS: return "Oracle Approximating Shrinkage (OAS)";
case EEstimator::MCD: return "Minimum Covariance Determinant (MCD)";
case EEstimator::COR: return "Pearson Correlation";
case EEstimator::IDE: return "Identity";
}
return "Invalid";
}
/// <summary> Convert string to estimators. </summary>
/// <param name="estimator"> The estimator. </param>
/// <returns> <see cref="EEstimator"/> </returns>
inline EEstimator StringToEstimator(const std::string& estimator)
{
if (estimator == "Covariance") { return EEstimator::COV; }
if (estimator == "Normalized Spatial Covariance Matrix (SCM)") { return EEstimator::SCM; }
if (estimator == "Ledoit and Wolf") { return EEstimator::LWF; }
if (estimator == "Oracle Approximating Shrinkage (OAS)") { return EEstimator::OAS; }
if (estimator == "Minimum Covariance Determinant (MCD)") { return EEstimator::MCD; }
if (estimator == "Pearson Correlation") { return EEstimator::COR; }
return EEstimator::IDE;
}
//***********************************************************
//******************** COVARIANCES BASES ********************
//***********************************************************
/// <summary> Calculation of the Variance of a double dataset \f$\vec{X}\f$.\n
/// \f[ V(X) = \left(\frac{1}{n} \sum_{i=1}^{N}x_{i}^{2}\right) - \left(\frac{1}{n} \sum_{i=1}^{N}x_{i}\right)^{2} \f]
/// </summary>
/// <param name="x"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Samples. </param>
/// <returns> The Variance. </returns>
double Variance(const Eigen::RowVectorXd& x);
/// <summary> Calculation of the Covariance between two double dataset \f$\vec{X}, \vec{Y}\f$.\n
/// \f[ \operatorname{Cov}\left(x,y\right) = \frac{\sum_{i=1}^{N}{x_{i}y_{i}} - \left(\sum_{i=1}^{N}{x_{i}}\sum_{i=1}^{N}{y_{i}}\right)/N}{N}\f]
/// </summary>
/// <param name="x"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Samples. </param>
/// <param name="y"> The dataset \f$\vec{Y}\f$. With \f$ N \f$ Samples. </param>
/// <returns> The Covariance. </returns>
double Covariance(const Eigen::RowVectorXd& x, const Eigen::RowVectorXd& y);
/// <summary> Shrunks the Covariance Matrix \f$ M \f$ (destructive operation).\n
/// \f[ (1 - \text{shrinkage}) \times M_{\operatorname{Cov}} + \frac{\text{shrinkage} \times \operatorname{trace}(M_{Cov})}{N} \times I_N \f]
/// </summary>
/// <param name="cov"> The Covariance Matrix to shrink. </param>
/// <param name="shrinkage"> (Optional) The shrinkage coefficient : \f$ 0\leq \text{shrinkage} \leq 1\f$. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool ShrunkCovariance(Eigen::MatrixXd& cov, double shrinkage = 0.1);
/// <summary> Shrunks the Covariance Matrix \f$ M \f$ (non destructive operation).\n
/// \f[ (1 - \text{shrinkage}) \times M_{\operatorname{Cov}} + \frac{\text{shrinkage} \times \operatorname{trace}(M_{Cov})}{N} \times I_N \f]
/// </summary>
/// <param name="in"> The covariance matrix to shrink. </param>
/// <param name="out"> The shrunk covariance matrix. </param>
/// <param name="shrinkage"> (Optional) The shrinkage coefficient : \f$ 0\leq \text{shrinkage} \leq 1\f$. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool ShrunkCovariance(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, double shrinkage = 0.1);
/// <summary> Select the function to call for the covariance matrix.\n
/// - centralizing the data is useless for <c><see cref="EEstimator::COV"/></c> and <c><see cref="EEstimator::COR"/></c>.\n
/// - centralizing the data is not usual for <c><see cref="EEstimator::SCM"/></c>.
/// </summary>
/// <param name="in"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Rows (features) and \f$ S \f$ columns (samples). </param>
/// <param name="out"> The Covariance Matrix. </param>
/// <param name="estimator"> (Optional) The selected estimator (see <see cref="EEstimator"/>). </param>
/// <param name="standard"> (Optional) Standardize the data (see <see cref="EStandardization"/>). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool CovarianceMatrix(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, EEstimator estimator = EEstimator::COV,
EStandardization standard = EStandardization::Center);
//***********************************************************
//******************** COVARIANCES TYPES ********************
//***********************************************************
/// <summary> Calculation of the covariance matrix.\n
/// \f[ M_{\operatorname{Cov}} =
/// \begin{pmatrix}
/// V\left(x_1\right) & \operatorname{Cov}\left(x_1,x_2\right) &\cdots & \operatorname{Cov}\left(x_1,x_N\right)\\
/// \operatorname{Cov}\left(x_2,x_1\right) &\ddots & \ddots & \vdots \\
/// \vdots & \ddots & \ddots & \vdots \\
/// \operatorname{Cov}\left(x_N,x_1\right) &\cdots & \cdots & V\left(x_N\right)
/// \end{pmatrix}
/// \quad\quad \text{with } x_i \text{ the feature } i
/// \f]\n
/// With the <see cref="Variance"/> and <see cref="Covariance"/> function.
/// </summary>
/// <param name="samples"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Rows (features) and \f$ S \f$ columns (samples). </param>
/// <param name="cov"> The Covariance Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool CovarianceMatrixCOV(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov);
/// <summary> Calculation of the covariance matrix by the method : Normalized Spatial Covariance Matrix (SCM).\n
/// \f[ M_{\operatorname{Cov_{SCM}}} = \frac{XX^{\mathsf{T}}}{\operatorname{trace}{\left(XX^{\mathsf{T}}\right)}} \f]
/// </summary>
/// <param name="samples"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Rows (features) and \f$ S \f$ columns (samples). </param>
/// <param name="cov"> The Covariance Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool CovarianceMatrixSCM(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov);
/// <summary> Calculation of the covariance matrix and shrinkage by the method : Ledoit and Wolf.\n
/// -# Compute the Covariance Matrix (see <see cref="CovarianceMatrixCOV"/>) \f$ M_{\operatorname{Cov}} \f$
/// -# Compute the Ledoit and Wolf Shrinkage
/// -# Shrunk the Matrix (see <see cref="ShrunkCovariance"/>)
///
/// Ledoit and Wolf Shrinkage (from <a href="http://scikit-learn.org/stable/modules/generated/sklearn.covariance.LedoitWolf.html">Sklearn LedoitWolf Estimator</a>)
/// described in "A Well-Conditioned Estimator for Large-Dimensional Covariance Matrices", Ledoit and Wolf, Journal of Multivariate Analysis, Volume 88, Issue 2, February 2004, pages 365-411. : \n
/// \f[
/// \begin{aligned}
/// \vec{X}^2 &= \begin{pmatrix}x_{0,0}^2 & \cdots & x_{0,S}^2 \\ \vdots & \ddots &\vdots \\ x_{N,0}^2 & \cdots & x_{N,S}^2\end{pmatrix} \quad \text{with } x_{i,j} \in \vec{X}\\
/// M_{\mu} &= \mu\times I_N = \begin{pmatrix} \mu & 0 & \cdots & 0 \\ 0 & \ddots &\ddots & \vdots \\ \vdots & \ddots &\ddots & 0 \\ 0 & \cdots & 0 & \mu\end{pmatrix}
/// \quad \text{with } \mu = \frac{\operatorname{trace}(M_{\operatorname{Cov}})}{N}\\
/// M_{\delta} &= M_{\operatorname{Cov}}-M_{\mu}\\
/// M_{\delta}^2 &= M_{\delta} * M_{\delta}\\
/// M_{\beta} &= \frac{1}{S} \times \left(\vec{X}^2 * \vec{X}^{2\mathsf{T}}\right) - M_{Cov} * M_{Cov}\\
/// \Sigma\left( M \right) &=\text{ the sum of the elements of the matrix } M\\
/// \end{aligned}
/// \f]
/// \f[ \text{Shrinkage}_\text{LWF} = \frac{\beta}{\delta} \quad \text{with } \delta = \frac{\Sigma\left( M_{\delta}^2 \right)}{N} \quad\text{and}\quad
/// \beta = \operatorname{min}\left(\frac{\Sigma\left( M_{\beta}^2 \right)}{N \times S},~ \delta\right)\f]
/// </summary>
/// <param name="samples"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Rows (features) and \f$ S \f$ columns (samples). </param>
/// <param name="cov"> The Covariance Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool CovarianceMatrixLWF(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov);
/// <summary> Calculation of the covariance matrix and shrinkage by the method : Oracle Approximating Shrinkage (OAS).\n
/// -# Compute the Covariance Matrix (see <see cref="CovarianceMatrixCOV"/>) \f$ M_{\operatorname{Cov}} \f$
/// -# Compute the Oracle Approximating Shrinkage
/// -# Shrunk the Matrix (see <see cref="ShrunkCovariance"/>)
///
/// Oracle Approximating Shrinkage (from <a href="http://scikit-learn.org/stable/modules/generated/sklearn.covariance.OAS.html">Sklearn Oracle Approximating Shrinkage Estimator</a>)
/// describe in "Shrinkage Algorithms for MMSE Covariance Estimation" Chen et al., IEEE Trans. on Sign. Proc., Volume 58, Issue 10, October 2010. : \n
/// \f[
/// \begin{aligned}
/// \mu &= \frac{\operatorname{trace}(M_{\operatorname{Cov}})}{N}\\
/// \mu \left( M \right) &=\text{ the mean of the elements of the matrix } M\\
/// \alpha &= \mu \left( M_{\operatorname{Cov}} * M_{\operatorname{Cov}} \right)\\
/// \text{num} &= \alpha + \mu^2\\
/// \text{den} &= (S + 1) \times \frac{\alpha - \mu^2}{N}\\
/// \end{aligned}
/// \f]
/// \f[
/// \text{Shrinkage}_\text{OAS} = \begin{cases}
/// 1, & \text{if}\ \text{den} = 0 \text{ or num} > \text{den} \\
/// \frac{\text{num}}{\text{den}}, & \text{otherwise}
/// \end{cases}
/// \f]
/// </summary>
/// <param name="samples"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Rows (features) and \f$ S \f$ columns (samples). </param>
/// <param name="cov"> The Covariance Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool CovarianceMatrixOAS(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov);
/// <summary>Calculation of the covariance matrix and shrinkage by the method : Minimum Covariance Determinant (MCD). </summary>
/// <param name="samples"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Rows (features) and \f$ S \f$ columns (samples). </param>
/// <param name="cov"> The Covariance Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// \todo Not implemented.
bool CovarianceMatrixMCD(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov);
/// <summary> Calculation of the covariance matrix by the method : Pearson Correlation.\n
/// \f[
/// M_{\operatorname{Cov_{COR}}}\left(i,j\right)
/// = \frac{ M_{\operatorname{Cov}}\left(i,j\right) } { \sqrt{ M_{\operatorname{Cov}}\left(i,i\right) * M_{\operatorname{Cov}}\left(j,j\right) } }
/// \f]
/// </summary>
/// <param name="samples"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Rows (features) and \f$ S \f$ columns (samples). </param>
/// <param name="cov"> The Covariance Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool CovarianceMatrixCOR(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov);
/// <summary> Return the Identity matrix \f$ I_N \f$. </summary>
/// <param name="samples"> The dataset \f$\vec{X}\f$. With \f$ N \f$ Rows (features) and \f$ S \f$ columns (samples). </param>
/// <param name="cov"> The Covariance Matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool CovarianceMatrixIDE(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov);
} // namespace Geometry
@@ -0,0 +1,87 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Distance.hpp
/// \brief All functions to estimate the Distance between two Covariance Matrix.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks
/// - List of Metrics inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>).
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/Metrics.hpp"
#include <Eigen/Dense>
namespace Geometry {
/// <summary> Compute the distance between two matrix with the selected \p metric. </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <param name="metric"> (Optional) The metric (see <see cref="EMetric"/>). </param>
/// <returns> The Distance between A and B. </returns>
double Distance(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, EMetric metric = EMetric::Riemann);
/// <summary> Compute the Riemannian Distance between two covariance matrices A and B.\n
/// \f[ d_{\text{R}}(A,B) = \sqrt{\left( \sum_i \log\left(\lambda_i\right)^2 \right)} \f]
/// with : \f$\lambda_i\f$ the joint eigenvalues of \f$A\f$ and \f$B\f$.
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <returns> The Riemannian Distance between A and B. </returns>
double DistanceRiemann(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b);
/// <summary> Compute the Euclidian Distance between two covariance matrices A and B.\n
/// \f[ d_{\text{E}}(A,B) = \left\lVert B - A \right\rVert\f]
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <returns> The Eclidean Distance between A and B. </returns>
double DistanceEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b);
/// <summary> Compute the Log Euclidian Distance between two covariance matrices A and B.\n
/// \f[ d_{\text{lE}}(A,B) = \left\lVert \log\left(B\right) - \log\left(A\right) \right\rVert\f]
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <returns> The Log Eclidean Distance between A and B. </returns>
double DistanceLogEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b);
/// <summary> Compute the Log-det Distance between two covariance matrices A and B.\n
/// \f[ d_{\text{lD}}(A,B) = \sqrt{\log\left(\left\lvert\frac{A + B}{2}\right\rvert\right) - 0.5 \times \log\left( \left\lvert A \right\rvert \times \left\lvert B \right\rvert \right)} \f]
/// with : \f$\left\lvert A \right\rvert\f$ the determinant of \f$A\f$.
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <returns> The Log-det Distance between A and B. </returns>
double DistanceLogDet(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b);
/// <summary> Compute the Kullback Leibler Divergence between two covariance matrices A and B.\n
/// \f[ d_{\text{K}}(A,B) = 0.5 \times \left( \operatorname{trace}\left(B^{-1} ~ A\right) - N + \log\left( \frac{\left\lvert B \right\rvert }{\left\lvert A \right\rvert} \right) \right) \f]
/// with : \f$\left\lvert A \right\rvert\f$ the determinant of \f$A\f$.
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <returns> The Kullback Leibler Divergence Distance between A and B. </returns>
double DistanceKullback(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b);
/// <summary> Compute the Symetric Kullback Leibler Divergence between two covariance matrices A and B.\n
/// \f[ d_{\text{sK}}(A,B) = d_\text{K}(A, B) + d_\text{K}(B, A) \f]
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <returns> The Symetric Kullback Leibler Divergence Distance between A and B. </returns>
double DistanceKullbackSym(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b);
/// <summary> Compute the Wasserstein Distance between two covariance matrices A and B.\n
/// \f[ d_{\text{W}}(A,B) = \sqrt{ \operatorname{trace}\left(A + B - 2 \times \left(A^{1/2} ~ B ~ A^{1/2}\right)^{1/2}\right) } \f]
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <returns> The Wasserstein Distance between A and B. </returns>
double DistanceWasserstein(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b);
} // namespace Geometry
@@ -0,0 +1,109 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Featurization.hpp
/// \brief All functions to transform Covariance matrix to feature vector.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <Eigen/Dense>
namespace Geometry {
/// <summary> Compute the features vector of covariance matrix with the selected method. </summary>
/// <param name="in"> The covariance in. </param>
/// <param name="out"> The Feature Vector. </param>
/// <param name="tangent"> (Optional) True to use tangent space featurization, Upper Triangle Squeeze if false. </param>
/// <param name="ref"> The reference Matrix (usefull for Tangent Space Featurization). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool Featurization(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, bool tangent = true, const Eigen::MatrixXd& ref = Eigen::MatrixXd());
/// <summary> Compute the covariance matrix of features vector with the selected method. </summary>
/// <param name="in"> The Feature Vector. </param>
/// <param name="out"> The covariance out. </param>
/// <param name="tangent"> (Optional) True to use tangent space featurization, Upper Triangle Squeeze if false. </param>
/// <param name="ref"> The reference Matrix (usefull for Tangent Space Featurization). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool UnFeaturization(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, bool tangent = true, const Eigen::MatrixXd& ref = Eigen::MatrixXd());
/// <summary> Squeeze the upper triangle of \f$N \times N\f$ square matrix to a \f$\frac{N\left(N+1\right)}{2}\f$ Vector.
/// <table align="center" border="0">
/// <tr><th>Upper Triangle Matrix</th><th></th><th>Row Major Upper Triangle Squeeze</th> <th></th> <th>Diagonal Major Upper Triangle Squeeze</th></tr>
/// <tr><td>\f[ \begin{pmatrix} a&b&c\\d&e&f\\g&h&i \end{pmatrix} \Rightarrow \begin{pmatrix} a&b&c\\0&e&f\\0&0&i \end{pmatrix} \f]</td>
/// <td><pre> </pre></td>
/// <td>\f[ \begin{pmatrix} a&b&c\\d&e&f\\g&h&i \end{pmatrix} \Rightarrow \begin{pmatrix} a&b&c&e&f&i \end{pmatrix} \f]</td>
/// <td><pre> </pre></td>
/// <td>\f[\begin{pmatrix} a&b&c\\d&e&f\\g&h&i \end{pmatrix} \Rightarrow \begin{pmatrix} a&e&i&b&f&c \end{pmatrix} \f]</td></tr>
/// </table>
/// </summary>
/// <param name="in"> The \f$N \times N\f$ square matrix. </param>
/// <param name="out"> The \f$\frac{N\left(N+1\right)}{2}\f$ Vector. </param>
/// <param name="rowMajor"> Get the values row by row if true, diagonal by diagonal if false. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool SqueezeUpperTriangle(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, bool rowMajor = true);
/// <summary> Compute the upper triangle of \f$\frac{N\left(N+1\right)}{2}\f$ Vector to a \f$N \times N\f$ square matrix.
/// <table align="center" border="0">
/// <tr><th>Row Major Method</th> <th></th> <th>Diagonal Major Method</th></tr>
/// <tr><td>\f[ \begin{pmatrix} a&b&c&d&e&f \end{pmatrix} \Rightarrow \begin{pmatrix} a&b&c\\0&d&e\\0&0&f \end{pmatrix} \f]</td>
/// <td><pre> </pre></td>
/// <td>\f[ \begin{pmatrix} a&b&c&d&e&f \end{pmatrix} \Rightarrow \begin{pmatrix} a&d&f\\0&b&e\\0&0&c \end{pmatrix} \f]</td></tr>
/// </table>
/// </summary>
/// <param name="in"> The \f$\frac{N\left(N+1\right)}{2}\f$ Vector. </param>
/// <param name="out"> The \f$N \times N\f$ square matrix. </param>
/// <param name="rowMajor"> Get the values row by row if true, diagonal by diagonal if false. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool UnSqueezeUpperTriangle(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, bool rowMajor = true);
/// <summary> Project a covariance matrices (\f$M\f$) in the tangent space (\f$\mathcal{T}\f$) according to the given reference point (\f$M_\text{Ref}\f$). <br/>
///
/// - Compute the transformation matrix for the covariance matrix \f$M\f$ with the reference matrix \f$M_\text{Ref}\f$ and squeeze ths matrix (see <see cref="SqueezeUpperTriangle"/>).
/// \f[
/// \begin{aligned}
/// J &= \log{\left(M_\text{Ref}^{-1/2} \times M \times M_\text{Ref}^{-1/2}\right)}\\
/// V_J &= \operatorname{SqueezeUpperTriangle}(J)
/// \end{aligned}
/// \f]
/// - Compute a coefficient Vector to apply to transformation vector.
/// \f[
/// \begin{aligned}
/// M_\text{Coeffs} &= \begin{pmatrix}
/// 1 & \sqrt{2} & \cdots & \sqrt{2} \\
/// 0 & 1 & \ddots & \sqrt{2} \\
/// \vdots & \ddots & \ddots & \vdots\\
/// 0 & \cdots & \cdots & 1
/// \end{pmatrix}\\
/// V_\text{Coeffs} &= \operatorname{SqueezeUpperTriangle}(M_\text{Coeffs})\\
/// \end{aligned}
/// \f]
/// - Compute the element wise product of the two vectors to have the tangent space Projection \f$\zeta_M\f$
/// \f[ \zeta_M = V_J \odot V_\text{Coeffs} \f]
/// </summary>
/// <param name="in"> The \f$N \times N\f$ covariance matrix. </param>
/// <param name="out"> The \f$\frac{N\left(N+1\right)}{2}\f$ row. </param>
/// <param name="ref"> (Optional) The \f$N \times N\f$ reference in (use the identity Matrix if empty). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool TangentSpace(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, const Eigen::MatrixXd& ref = Eigen::MatrixXd());
/// <summary> Project a Tangent space vectors in the manifold according to the given reference point. <br/>
/// \f[
/// \begin{aligned}
/// \text{With : } M_\text{Ts} &= \operatorname{UnSqueezeUpperTriangle}(V_\text{Ts}) \quad \text{ and } \quad \mathsf{U}_{M}\text{ the upper triangular out.}\\
/// M_\text{Coeffs} &= \operatorname{diag}\left(M_\text{Ts}\right) + \frac{\mathsf{U}_{M_\text{Ts}} + \mathsf{U}_{M_\text{Ts}}^{\mathsf{T}}}{\sqrt{2}}\\
/// \Rightarrow M &= M_\text{Ref}^{1/2} ~ \exp{\left(M_\text{Coeffs}\right)} ~ M_\text{Ref}^{1/2}
/// \end{aligned}
/// \f]
/// </summary>
/// <param name="in"> The \f$\frac{N\left(N+1\right)}{2}\f$ row. </param>
/// <param name="out"> The \f$N \times N\f$ covariance matrix. </param>
/// <param name="ref"> (Optional) The \f$N \times N\f$ reference out (use the identity Matrix if empty). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool UnTangentSpace(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, const Eigen::MatrixXd& ref = Eigen::MatrixXd());
} // namespace Geometry
@@ -0,0 +1,72 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Geodesic.hpp
/// \brief All functions to estimate the Geodesic position of two Covariance Matrix.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks
/// - List of Metrics inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>).
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/Metrics.hpp"
#include <Eigen/Dense>
namespace Geometry {
/// <summary> Compute the matrix at the position alpha on the geodesic between A and B with the selected \p metric.\n
/// - Allowed Metrics : <c>Riemann</c>, <c>Euclidian</c>, <c>LogEuclidian</c>, <c>Identity</c>
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <param name="g"> The Geodesic. </param>
/// <param name="metric"> (Optional) The metric (see <see cref="EMetric"/>). </param>
/// <param name="alpha"> (Optional) Position on the Geodesic : \f$ 0\leq \text{alpha} \leq 1\f$. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool Geodesic(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, EMetric metric = EMetric::Riemann, double alpha = 0.5);
/// <summary> Compute the matrix at the position alpha on the Riemannian geodesic between A and B. \n
/// \f[ \gamma_\text{R} = A^{1/2} ~ \left( A^{-1/2} ~ B ~ A^{-1/2} \right)^\alpha ~ A^{1/2} \f]
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <param name="g"> The Geodesic. </param>
/// <param name="alpha"> (Optional) Position on the Geodesic : \f$ 0\leq \text{alpha} \leq 1\f$. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool GeodesicRiemann(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, double alpha = 0.5);
/// <summary> Compute the matrix at the position alpha on the Euclidean geodesic between A and B.\n
/// \f[ \gamma_\text{E} = \left(1 - \alpha \right) \times A + \alpha \times B \f]
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <param name="g"> The Geodesic. </param>
/// <param name="alpha"> (Optional) Position on the Geodesic : \f$ 0\leq \text{alpha} \leq 1\f$. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool GeodesicEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, double alpha = 0.5);
/// <summary> Compute the matrix at the position alpha on the Log Euclidean geodesic between A and B. \n
/// \f[ \gamma_\text{LogE} = \exp\left(\left(1 - \alpha \right) \times \log\left(A\right) + \alpha \times \log\left(B\right) \right)\f]
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <param name="g"> The Geodesic. </param>
/// <param name="alpha"> (Optional) Position on the Geodesic : \f$ 0\leq \text{alpha} \leq 1\f$. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool GeodesicLogEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, double alpha = 0.5);
/// <summary> Compute the matrix at the position alpha on the Identity geodesic. \n
/// \f[ \gamma_\text{I} = I_N \f]
/// </summary>
/// <param name="a"> The First Covariance matrix. </param>
/// <param name="b"> The Second Covariance matrix. </param>
/// <param name="g"> The Geodesic. </param>
/// <param name="alpha"> (Optional) Position on the Geodesic : \f$ 0\leq \text{alpha} \leq 1\f$. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool GeodesicIdentity(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, double alpha = 0.5);
} // namespace Geometry
@@ -0,0 +1,163 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Mean.hpp
/// \brief All functions to estimate the mean of Vector of Covariance Matrix.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks
/// - List of Metrics inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>).
/// - The Approximate joint diagonalization based on pham's algorithm is not implemented.
/// - The Approximate joint diagonalization based log-Euclidean (ALE) Mean doesn't work => Need to implement <see cref="AJDPham"/> and check if it works next.
/// - The Wasserstein Mean Doesn't work so good (after \f$10^{-3}\f$ precision with the pyriemann library).
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/Metrics.hpp"
#include <Eigen/Dense>
#include <vector>
namespace Geometry {
/// <summary> Compute the mean of vector of covariance matrix with the selected \p metric. </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The computed mean. </param>
/// <param name="metric"> (Optional) The metric (see <see cref="EMetric"/>). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool Mean(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean, EMetric metric = EMetric::Riemann);
/// <summary> Approximate Joint Diagonalization based on pham's algorithm.\n
/// \f[ C_\text{AJD} = \cdots \f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="ajd"> The computed Approximate Joint Diagonalization. </param>
/// <param name="epsilon"> (Optional) The epsilon. </param>
/// <param name="maxIter"> (Optional) The maximum iterator. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// \todo Not implemented.
bool AJDPham(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& ajd, double epsilon = 0.0001, int maxIter = 15);
/// <summary> Compute the Mean with the Riemannian Mean.\n
/// -# Compute the Classical Mean \f$ C_{\mu_\text{E}} \f$ (see <see cref="MeanEuclidian"/>)
/// -# Update with an iterative procedure that stops after 50 iterations or when one of two criterions is under \f$ 10^{-4}\f$
///
/// \f[ C_{\mu_\text{R}} = C_{\mu_\text{E}} \\ \nu=1.0 \\ \tau=+\infty \f]
/// Iterative process with \f$J\f$ while \f$ \text{iteration} < 50 \f$ and \f$ 10^{-4} < \left\lVert J \right\rVert \f$ and \f$ 10^{-4} < \nu \f$
/// \f[ \begin{aligned}
/// J &= \frac{1}{N} \sum_i \log\left(C_{\mu_\text{R}}^{-1/2} + C_i ~ C_{\mu_\text{R}}^{-1/2}\right)\\
/// C_{\mu_\text{R}} &= C_{\mu_\text{R}}^{1/2} ~ \exp(\nu \times J) ~ C_{\mu_\text{R}}^{1/2}\\
/// \end{aligned}
/// \f]
/// \f[ \begin{cases}
/// \text{if } \nu \times \left\lVert J \right\rVert < \tau & \nu = 0.95 \times \nu,~\tau = \nu \times \left\lVert J \right\rVert\\
/// \text{otherwise } & \nu = 0.5 \times \nu
/// \end{cases}
/// \f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MeanRiemann(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
/// <summary> Compute the Euclidian Mean.\n
/// \f[ C_{\mu_\text{E}} =\frac{1}{N} \sum_i{C_i}\f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MeanEuclidian(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
/// <summary> Compute the Log Euclidiean Mean.\n
/// \f[ C_{\mu_\text{lE}} =\exp\left(\frac{1}{N} \sum_i{\log\left(C_i\right)}\right)\f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MeanLogEuclidian(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
/// <summary> Compute the Log Determinant Mean.\n
/// -# Compute the Classical Mean \f$ C_{\mu_\text{E}} \f$ (see <see cref="MeanEuclidian"/>)
/// -# Update with an iterative procedure that stops after 50 iterations or when criterion is under \f$ 10^{-4}\f$
///
/// \f[ C_{\mu_\text{lD}} = C_{\mu_\text{E}}\f]
/// Iterative process with \f$J\f$ while \f$ \text{iteration} < 50 \f$ and \f$ 10^{-4} < \left\lVert J-C_\mu \right\rVert \f$
/// \f[ \begin{aligned}
/// J &= \left(\frac{1}{N} \sum_i \left( 0.5 \times\left(C_{\mu_\text{lD}} + C_i \right)\right)^{-1} \right)^{-1}\\
/// C_{\mu_\text{lD}} &= J
/// \end{aligned}\f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MeanLogDet(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
/// <summary> Compute the Kullback Mean.\n
/// The mean is the Geodesic center between the Euclidian and the Harmonic Mean.\n
/// \f[ C_{\mu_\text{K}} = \gamma \left( C_{\mu_{\text{E}}}, C_{\mu_{\text{H}}} \right) \f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MeanKullback(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
/// <summary> Compute the Wasserstein Mean.\n
/// -# Compute the Classical Mean \f$ C_{\mu_\text{E}} \f$ (see <see cref="MeanEuclidian"/>)
/// -# Update with an iterative procedure that stops after 50 iterations or when criterion is under \f$ 10^{-4}\f$
///
/// \f[ C_{\mu_\text{W}} = C_{\mu_{\text{E}}}\f]
/// Iterative process with \f$J\f$ while \f$ \text{iteration} < 50 \f$ and \f$ 10^{-4} < \left\lVert J-J_{-1} \right\rVert \f$
/// \f[ \begin{aligned}
/// J &= C_{\mu_\text{W}}^{1/2}\\
/// J &= \left(\frac{1}{N} \sum_i \left( J C_i J \right)^{1/2} \right)^{1/2}\\
/// \end{aligned}\f]
/// After the Iterative process : \f$ C_{\mu_\text{W}} = J*J \f$
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// \todo Doesn't work so good (after \f$10^{-3}\f$ precision with the pyriemann library).
bool MeanWasserstein(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
/// <summary> Compute the Approximate joint diagonalization based log-Euclidean (ALE) Mean. \n
/// -# Compute the Approximate Joint Diagonalization \f$ C_\text{AJD} \f$ (see <see cref="AJDPham"/>)
/// -# Update with an iterative procedure that stops after 50 iterations or when criterion is under \f$ 10^{-4}\f$
///
/// \f[ C_{\mu_\text{ALE}} = C_\text{AJD}\f]
/// Iterative process with \f$J\f$ (and \f$U = \operatorname{diag}(\operatorname{diag}(\exp(J))\f$) while \f$ \text{iteration} < 50 \f$ and \f$ 10^{-4} < d_\text{R}(I_N,U) \f$
/// \f[ \begin{aligned}
/// J &= \frac{1}{N} \log\left(\sum_i \left( C_{\mu_\text{ALE}}^{\mathsf{T}} C_i C_{\mu_\text{ALE}} \right) \right)\\
/// U &= \operatorname{diag}(\operatorname{diag}(\exp(J))\\
/// C_{\mu_\text{ALE}} &= C_{\mu_\text{ALE}} * U^{-1/2}\\
/// \end{aligned}\f]
/// After the Iterative process :
/// \f[ \begin{aligned}
/// J &= \frac{1}{N} \log\left(\sum_i \left( C_{\mu_\text{ALE}}^{\mathsf{T}} C_i C_{\mu_\text{ALE}} \right) \right)\\
/// C_{\mu_\text{ALE}} &= \left(C_{\mu_\text{ALE}}^{-1}\right)^{\mathsf{T}} ~ \exp(J) ~ C_{\mu_\text{ALE}}^{-1}
/// \end{aligned}\f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// \todo Doesn't work => Need to implement <see cref="AJDPham"/> and check if it works next.
bool MeanALE(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
/// <summary> Compute the Harmonic Mean.\n
/// \f[ C_{\mu_\text{H}} = (\frac{1}{N} \sum_i{C_i}^{-1})^{-1} \f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MeanHarmonic(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
/// <summary> Give the Identity Matrix.\n
/// \f[ C_{\mu_\text{I}} = I_N \f]
/// </summary>
/// <param name="covs"> Vector of Covariance Matrix. </param>
/// <param name="mean"> The mean. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MeanIdentity(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean);
} // namespace Geometry
@@ -0,0 +1,108 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Median.hpp
/// \brief All Median functions for array or matrix.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 29/07/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks This algortihms is inspired by the plugin clean_rawdata in <a href="https://sccn.ucsd.edu/eeglab/index.php">EEGLAB</a> (<a href="https://github.com/sccn/clean_rawdata/blob/master/LICENSE">License</a>).
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <Eigen/Dense>
#include <vector>
#include "geometry/Metrics.hpp"
namespace Geometry {
//---------------------------------------------------------------------------
//------------------------------ Matrix Median ------------------------------
//---------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Find the median of stl vector. </summary>
/// <typeparam name="T"> The type of the values (only arithmetic type). </typeparam>
/// <param name="v"> the vector of values. </param>
/// <returns> The median of vector. </returns>
template <typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
T Median(const std::vector<T>& v)
{
std::vector<T> tmp = v;
const size_t n = tmp.size() / 2; // Where is the middle (if odd number of value the decimal part is floor by cast)
std::stable_sort(tmp.begin(), tmp.end()); // We sort all because nth_element doesn't have same behaviour in Windows and Unix
return (tmp.size() % 2 == 0) ? (tmp[n] + tmp[n - 1]) / 2 : tmp[n]; // For Even number of value we take the mean of the two middle value
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Find the median of values of the Eigen Matrix. </summary>
/// <param name="m"> the matrix. </param>
/// <returns> The median of matrix. </returns>
double Median(const Eigen::MatrixXd& m);
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Compute the median of vector of matrix with the Weiszfeld's algorithm. <br/>
/// To compute this median, we start by computing the initial median of the dataset by taking each element of the matrices independently.
/// That is to say that for the element at position i, j(a_i, j) of the matrices, we computes the median of the elements a_i, j of all the matrices of the dataset.
/// We thus have an initial median for our dataset. <br/>
/// Then, we refine our median by the iterative algorithm of Weiszfeld:
/// - We remove the median in our dataset.
/// - For each new matrices, we compute the norm.
/// - We sum the the matrices in initial dataset (divided by their own norm) and we normalize the result by the sum of inverse norms.
/// - We iterate this previous step until we have a difference between the old and new median is under an epsilon or that the number of iterations is above the limit.
/// </summary>
/// <param name="matrices"> Vector of Matrix. </param>
/// <param name="median"> The computed median. </param>
/// <param name="epsilon"> (Optional) The epsilon value to stop algorithm. </param>
/// <param name="maxIter"> (Optional) The maximum iteration allowed to find best Median. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> it's an iterative algorithm, so we have a limit of iterations and an epsilon value to consider the calculation as satisfactory. </remarks>
bool MedianEuclidian(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon = 0.0001, const size_t maxIter = 50);
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Compute the median of vector of matrix with the Riemman Barycentre. <br/>
/// - Initialize the median with the euclidian mean of matrices.
/// - Iterate until the stop criterion (<c>iteration</c> over <c>maxIter</c> or \f$\text{gain}\f$ under <c>epsilon</c>).
/// - Compute the tangent space projection of each matrices with median as reference.
/// - Compute the sum (\f$\mathcal{S}\f$) of euclidian distance of each tangent space projection. <br/>
/// \f[ \delta_E=\sqrt{\sum_{i \in N}{x_i^2}} \quad \text{with } x_i \text{ the feature } i \text{ of the tangent space projection}\f]
/// - Compare with previous sum and stop if \f$\text{gain} < \varepsilon\f$. <br/>
/// \f[ \text{gain} = \left|\frac{\mathcal{S} - \mathcal{S}_\text{prev}}{\mathcal{S}_\text{prev}}\right| \f]
/// - Compute Median of each feature \f$i\f$ of tangent space projection.
/// - Transform this tangent space projection median to riemann space with previous median as reference and update the median by this new matrix.
/// </summary>
/// <param name="matrices"> Vector of Matrix. </param>
/// <param name="median"> The computed median. </param>
/// <param name="epsilon"> (Optional) The epsilon value to stop algorithm. </param>
/// <param name="maxIter"> (Optional) The maximum iteration allowed to find best Median. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MedianRiemann(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon = 0.0001, const size_t maxIter = 50);
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Give the identity matrix has median. </summary>
/// <param name="matrices"> Vector of Matrix. </param>
/// <param name="median"> The computed median. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool MedianIdentity(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median);
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Compute the median of vector of matrix with the Weiszfeld's algorithm for Euclidian Metric and Riemman Barycentre. </summary>
/// <param name="matrices"> Vector of Matrix. </param>
/// <param name="median"> The computed median. </param>
/// <param name="epsilon"> (Optional) The epsilon value to stop algorithm. </param>
/// <param name="maxIter"> (Optional) The maximum iteration allowed to find best Median. </param>
/// <param name="metric"> (Optional) THe metric to use. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> it's an iterative algorithm, so we have a limit of iterations and an epsilon value to consider the calculation as satisfactory. </remarks>
bool Median(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median,
const double epsilon = 0.0001, const size_t maxIter = 50, const EMetric& metric = EMetric::Euclidian);
//-------------------------------------------------------------------------------------------------
} // namespace Geometry
@@ -0,0 +1,69 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Metrics.hpp
/// \brief All Metrics.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 26/10/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks
/// - List of Metrics inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>).
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <string>
namespace Geometry {
/// <summary> Enumeration of metrics. Inspired by the work of Alexandre Barachant : <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a>. </summary>
enum class EMetric
{
Riemann, ///< The Riemannian Metric.
Euclidian, ///< The Euclidian Metric.
LogEuclidian, ///< The Log Euclidian Metric.
LogDet, ///< The Log Determinant Metric.
Kullback, ///< The Kullback Metric.
ALE, ///< The AJD-based log-Euclidean (ALE) Metric.
Harmonic, ///< The Harmonic Metric.
Wasserstein, ///< The Wasserstein Metric.
Identity ///< The Identity Metric.
};
/// <summary> Convert metric to string. </summary>
/// <param name="metric"> The metric. </param>
/// <returns> <c>std::string</c> </returns>
inline std::string toString(const EMetric metric)
{
switch (metric)
{
case EMetric::Riemann: return "Riemann";
case EMetric::Euclidian: return "Euclidian";
case EMetric::LogEuclidian: return "Log Euclidian";
case EMetric::LogDet: return "Log Determinant";
case EMetric::Kullback: return "Kullback";
case EMetric::ALE: return "AJD-based log-Euclidean";
case EMetric::Harmonic: return "Harmonic";
case EMetric::Wasserstein: return "Wasserstein";
case EMetric::Identity: return "Identity";
}
return "Invalid Metric";
}
/// <summary> Convert string to metric. </summary>
/// <param name="metric"> The metric. </param>
/// <returns> <see cref="EMetric"/> </returns>
inline EMetric StringToMetric(const std::string& metric)
{
if (metric == "Riemann") { return EMetric::Riemann; }
if (metric == "Euclidian") { return EMetric::Euclidian; }
if (metric == "Log Euclidian") { return EMetric::LogEuclidian; }
if (metric == "Log Determinant") { return EMetric::LogDet; }
if (metric == "Kullback") { return EMetric::Kullback; }
if (metric == "AJD-based log-Euclidean") { return EMetric::ALE; }
if (metric == "Harmonic") { return EMetric::Harmonic; }
if (metric == "Wasserstein") { return EMetric::Wasserstein; }
return EMetric::Identity;
}
} // namespace Geometry
@@ -0,0 +1,104 @@
///-------------------------------------------------------------------------------------------------
///
/// \file Misc.hpp
/// \brief All misc functions.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 29/07/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
/// \remarks This algortihms is inspired by the plugin clean_rawdata in <a href="https://sccn.ucsd.edu/eeglab/index.php">EEGLAB</a> (<a href="https://github.com/sccn/clean_rawdata/blob/master/LICENSE">License</a>).
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <Eigen/Dense>
#include <vector>
#include "geometry/Metrics.hpp"
namespace Geometry {
//-------------------------------------------------------------------
//------------------------------ Range ------------------------------
//-------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Create a range of double value. </summary>
/// <param name="begin"> Beginning of the range. </param>
/// <param name="end"> End of the range. </param>
/// <param name="step"> Step of the range. </param>
/// <param name="closed"> Authorize the end in range if <c>True</c>. </param>
/// <returns> The range vector. </returns>
/// <remarks>Use [std::iota](https://en.cppreference.com/w/cpp/algorithm/iota) function and a struct for this specific used. </remarks>
std::vector<double> doubleRange(const double begin, const double end, const double step = 1.0, const bool closed = true);
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Create a range of index with double value rounded. </summary>
/// <param name="begin"> Beginning of the range. </param>
/// <param name="end"> End of the range. </param>
/// <param name="step"> Step of the range. </param>
/// <param name="closed"> Authorize the end in range if <c>True</c>. </param>
/// <param name="unique"> Remove duplicate value if <c>True</c>. </param>
/// <returns> The range vector. </returns>
/// <remarks>Use [std::iota](https://en.cppreference.com/w/cpp/algorithm/iota) function and a struct for this specific used. </remarks>
std::vector<size_t> RoundIndexRange(const double begin, const double end, const double step, const bool closed = true, const bool unique = true);
//-------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------ Fit Distribution ------------------------------
//------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Commputes histogram of dataset extended in <c>n</c> bins, bins are computed from \f$[0;max]\f$ (values) to \f$[0;n]\f$ (bins). </summary>
/// <param name="dataset"> Input vector (all datas are positive). </param>
/// <param name="n"> Number of bin of the final histogram. </param>
std::vector<size_t> BinHist(const std::vector<double>& dataset, const size_t n);
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Get a Fit distribution. </summary>
/// <param name="values"> The values. </param>
/// <param name="mu"> The mu. </param>
/// <param name="sigma"> The sigma. </param>
/// <param name="betas"> List of wanted \f$\beta\f$ shapes. </param>
/// <param name="minQuant"> Minimum of wanted quantile (in range [0, 1]). </param>
/// <param name="maxQuant"> Maximum of wanted quantile (in range [0, 1]). </param>
/// <param name="minClean"> Minimum of estimated clean datas (only positive value). </param>
/// <param name="maxDropout"> Maximum of estimated artifact datas (only positive value). </param>
/// <param name="stepBound"> Step used to select beginning of datas subset (in range [0.0001, 0.1]). </param>
/// <param name="stepScale"> Step used to select size of datas subset (in range [0.0001, 0.1]). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool FitDistribution(const std::vector<double>& values, double& mu, double& sigma,
const std::vector<double>& betas = doubleRange(1.7, 3.5, 0.15),
const double minQuant = 0.022, const double maxQuant = 0.60,
const double minClean = 0.250, const double maxDropout = 0.10,
const double stepBound = 0.010, const double stepScale = 0.01);
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
//------------------------------ Riemannian Eigen Values ------------------------------
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Compute sorted eigen vector of the matrix. </summary>
/// <param name="matrix"> the input matrix. </param>
/// <param name="vectors"> Sorted eigen vectors. </param>
/// <param name="values"> Sorted eigen values. </param>
/// <param name="metric"> metric used for vectors. </param>
/// <remarks> Actually only euclidian method is implemented. <br/>
/// For Riemmanian metric, we must have some optimisation algorithm. </remarks>
void sortedEigenVector(const Eigen::MatrixXd& matrix, Eigen::MatrixXd& vectors, std::vector<double>& values, const EMetric metric = EMetric::Euclidian);
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/// <summary> Compute the eigen vector of the input matrix. </summary>
/// <param name="matrix"> input Matrix. </param>
/// <param name="vectors"> Sorted eigen vectors. </param>
/// <param name="values"> Sorted eigen values. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> This algorithm is in <a href="https://sccn.ucsd.edu/eeglab/index.php">EEGLAB</a> plugin and inspired by the paper "A Riemannian Newton Algorithm for Nonlinear Eigenvalue Problems", Zhi Zhao, Zheng - Jian Bai, and Xiao - Qing Jin, SIAM Journal on Matrix Analysisand Applications, 36(2), 752 - 774, 2015. </remarks>
//bool RiemannianNonLinearEigenVector(const Eigen::MatrixXd& matrix, Eigen::MatrixXd& vectors, std::vector<double>& values);
//-------------------------------------------------------------------------------------------------
} // namespace Geometry
@@ -0,0 +1,161 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CASR.hpp
/// \brief Class used to use Artifact Subspace Reconstruction Algorithm.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 27/08/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <string>
#include <vector>
#include <Eigen/Dense>
#include "geometry/Basics.hpp"
#include "geometry/Metrics.hpp"
namespace Geometry {
/// <summary> Class For Artifact Subspace Reconstruction (ASR) Algorithm. </summary>
class CASR
{
public:
CASR() = default; ///< Initializes a new instance of the <see cref="CASR"/> class.
/// <summary> Initializes a new instance of the <see cref="CASR"/> class with specified <c>metric</c>. </summary>
/// <remarks> Only Euclidian and Riemmann metrics are implemented If other is selected, Euclidian is used. </remarks>
explicit CASR(const EMetric& metric) { setMetric(metric); }
/// <summary> Initializes a new instance of the <see cref="CASR"/> class with specified <c>metric</c> and train with the specified <c>dataset</c>. </summary>
/// <remarks> Only Euclidian and Riemmann metrics are implemented If other is selected, Euclidian is used. </remarks>
explicit CASR(const EMetric& metric, const std::vector<Eigen::MatrixXd>& dataset)
{
setMetric(metric);
train(dataset);
}
~CASR() = default; ///< Finalizes an instance of the <see cref="CASR"/> class.
/// <summary> Trains the specified dataset. </summary>
/// <param name="dataset"> The dataset (Vector of signal window). </param>
/// <param name="rejectionLimit"> The rejection limit. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool train(const std::vector<Eigen::MatrixXd>& dataset, const double rejectionLimit = 5);
/// <summary> Apply the ASR algorithm to the input signal. </summary>
/// <param name="in"> The input signal. </param>
/// <param name="out"> The corrected signal. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool process(const Eigen::MatrixXd& in, Eigen::MatrixXd& out);
//***************************
//***** Getter / Setter *****
//***************************
/// <summary> Set the metric to use (only Riemann and euclidian is used). </summary>
/// <param name="metric">The metric. </param>
/// <remarks> If invalid metric is used Euclidian is selected. </remarks>
void setMetric(const EMetric& metric) { m_metric = (metric == EMetric::Riemann) ? EMetric::Riemann : EMetric::Euclidian; }
/// <summary> Sets the number of channel (dimension) to reconstruct in fraction, 0 for nothing 1 for all. </summary>
/// <param name="max"> The maximum ratio. </param>
/// <remarks> If value isn't in [0;1], this function does nothing. </remarks>
void setMaxChannel(const double max) { if (InRange(max, 0.0, 1.0)) { m_maxChannel = max; } }
/// <summary> Sets the differents matrices : median matrix, trheshold matrix, reconstruction matrix and covariance matrix. </summary>
/// <param name="median"> The median matrix. </param>
/// <param name="threshold"> The threshold matrix. </param>
/// <param name="reconstruct"> (Optional) The reconstruct matrix. </param>
/// <param name="covariance"> (Optional) The covariance matrix. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> All matrices must be square with same size (or empty for reconstruct and covariance matrix).
/// Trivial trigger is set to true. </remarks>
bool setMatrices(const Eigen::MatrixXd& median, const Eigen::MatrixXd& threshold,
const Eigen::MatrixXd& reconstruct = Eigen::MatrixXd(), const Eigen::MatrixXd& covariance = Eigen::MatrixXd());
EMetric getMetric() const { return m_metric; } ///< Get the metric.
size_t getChannelNumber() const { return m_nChannel; } ///< Get the matrices number of channel.
double getMaxChannel() const { return m_maxChannel; } ///< Get the number of channel (dimension) to reconstruct in fraction.
bool getTrivial() const { return m_trivial; } ///< Get is last reconstruct was trivial (first time or if previous doesn't need reconstruct).
Eigen::MatrixXd getMedian() const { return m_median; } ///< Get the median matrix.
Eigen::MatrixXd getThresholdMatrix() const { return m_threshold; } ///< Get the threshold matrix.
//***********************
//***** XML Manager *****
//***********************
/// <summary> Saves the ASR information in an XML file. </summary>
/// <param name="filename"> Filename. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool saveXML(const std::string& filename) const;
/// <summary> Loads the ASR information from an XML file. </summary>
/// <param name="filename"> Filename. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool loadXML(const std::string& filename);
//*****************************
//***** Override Operator *****
//*****************************
/// <summary> Check if object are equals (with a precision tolerance). </summary>
/// <param name="obj"> The second object. </param>
/// <param name="precision"> Precision for matrix comparison. </param>
/// <returns> <c>True</c> if the two elements are equals (with a precision tolerance), <c>False</c> otherwise. </returns>
bool isEqual(const CASR& obj, const double precision = 1e-6) const;
/// <summary> Copy object value. </summary>
/// <param name="obj"> The object to copy. </param>
void copy(const CASR& obj);
/// <summary> Get the ASR information for output. </summary>
/// <returns> The ASR print in stringstream. </returns>
std::stringstream print() const;
/// <summary> Override the affectation operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> The copied object. </returns>
CASR& operator=(const CASR& obj)
{
copy(obj);
return *this;
}
/// <summary> Override the equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CASR"/> are equals. </returns>
bool operator==(const CASR& obj) const { return isEqual(obj); }
/// <summary> Override the not equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CASR"/> are diffrents. </returns>
bool operator!=(const CASR& obj) const { return !isEqual(obj); }
/// <summary> Override the ostream operator. </summary>
/// <param name="os"> The ostream. </param>
/// <param name="obj"> The object. </param>
/// <returns> Return the modified ostream. </returns>
friend std::ostream& operator <<(std::ostream& os, const CASR& obj)
{
os << obj.print().str();
return os;
}
protected:
//*********************
//***** Variables *****
//*********************
EMetric m_metric = EMetric::Euclidian; ///< Metric Used to compute (only euclidian and Riemann are implemented
size_t m_nChannel = 0; ///< Number of channels (dimension)
double m_maxChannel = 1; ///< Maximum number of channels (dimension) to reconstruct if needed (in fraction, 0 for nothing 1 for all).
bool m_trivial = true; ///< Define if previous sample was trivial to reconstruct
Eigen::MatrixXd m_median; ///< Median computed with train dataset
Eigen::MatrixXd m_threshold; ///< Threshold matrix computed with train dataset
Eigen::MatrixXd m_r; ///< Last Reconstruction matrix
Eigen::MatrixXd m_cov; ///< Last Covariance matrix
};
} // namespace Geometry
@@ -0,0 +1,141 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBias.hpp
/// \brief Class used to add Rebias to Other Classifier.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 27/08/2019.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <Eigen/Dense>
#include <vector>
#include "geometry/Metrics.hpp"
#include "geometry/3rd-party/tinyxml2.h"
namespace Geometry {
/// <summary> Class For Bias Algorithm for covariance matrices. </summary>
class CBias
{
public:
/// <summary> Initializes a new instance of the <see cref="CBias"/> class. </summary>
CBias() = default;
/// <summary> Finalizes an instance of the <see cref="CBias"/> class. </summary>
~CBias() = default;
/// <summary> Computes the Bias matrix and reset the number of classification. </summary>
/// <param name="dataset"> The dataset (first dimension is the classes, second dimension is the trials as matrix). </param>
/// <param name="metric"> The metric. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool computeBias(const std::vector<std::vector<Eigen::MatrixXd>>& dataset, const EMetric metric = EMetric::Riemann);
/// <summary> Computes the Bias matrix and reset the number of classification. </summary>
/// <param name="dataset"> The dataset is a vector of trial as matrix. </param>
/// <param name="metric"> The metric. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool computeBias(const std::vector<Eigen::MatrixXd>& dataset, const EMetric metric = EMetric::Riemann);
/// <summary> Applies the Bias on 2D vector of Matrix. </summary>
/// <param name="in"> The input 2D vector of matrix. </param>
/// <param name="out"> The output 2D vector of matrix. </param>
void applyBias(const std::vector<std::vector<Eigen::MatrixXd>>& in, std::vector<std::vector<Eigen::MatrixXd>>& out);
/// <summary> Applies the Bias on vector of Matrix. </summary>
/// <param name="in"> The input vector of matrix. </param>
/// <param name="out"> The output vector of matrix. </param>
void applyBias(const std::vector<Eigen::MatrixXd>& in, std::vector<Eigen::MatrixXd>& out);
/// <summary> Applies the Bias on Matrix. </summary>
/// <param name="in"> The input matrix. </param>
/// <param name="out"> The output matrix. </param>
void applyBias(const Eigen::MatrixXd& in, Eigen::MatrixXd& out);
/// <summary> Updates the Bias. </summary>
/// <param name="sample"> The sample. </param>
/// <param name="metric"> The metric. </param>
void updateBias(const Eigen::MatrixXd& sample, const EMetric metric = EMetric::Riemann);
const Eigen::MatrixXd& getBias() const { return m_bias; } ///< Get the bias matrix.
void setBias(const Eigen::MatrixXd& bias); ///< Set the bias matrix and the inverse square root of biais.
size_t getClassificationNumber() const { return m_n; } ///< Get the Number of classification (used for update).
void setClassificationNumber(const size_t& n) { m_n = n; } ///< Set the Number of classification (used for update).
//***********************
//***** XML Manager *****
//***********************
/// <summary> Saves the Bias information in an XML file. </summary>
/// <param name="filename"> Filename. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool saveXML(const std::string& filename) const;
/// <summary> Loads the Bias information from an XML file. </summary>
/// <param name="filename"> Filename. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool loadXML(const std::string& filename);
/// <summary> Save informations in xml element (Bias and number of classification). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const;
/// <summary> Load informations in xml element (Bias and number of classification). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool loadAdditional(tinyxml2::XMLElement* data);
//*****************************
//***** Override Operator *****
//*****************************
/// <summary> Check if object are equals (with a precision tolerance). </summary>
/// <param name="obj"> The second object. </param>
/// <param name="precision"> Precision for matrix comparison. </param>
/// <returns> <c>True</c> if the two elements are equals (with a precision tolerance), <c>False</c> otherwise. </returns>
bool isEqual(const CBias& obj, const double precision = 1e-6) const;
/// <summary> Copy object value. </summary>
/// <param name="obj"> The object to copy. </param>
void copy(const CBias& obj);
/// <summary> Get the Classifier information for output. </summary>
/// <returns> The Classifier print in stringstream. </returns>
std::stringstream print() const;
/// <summary> Override the affectation operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> The copied object. </returns>
CBias& operator=(const CBias& obj)
{
copy(obj);
return *this;
}
/// <summary> Override the equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CBias"/> are equals. </returns>
bool operator==(const CBias& obj) const { return isEqual(obj); }
/// <summary> Override the not equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CBias"/> are diffrents. </returns>
bool operator!=(const CBias& obj) const { return !isEqual(obj); }
/// <summary> Override the ostream operator. </summary>
/// <param name="os"> The ostream. </param>
/// <param name="obj"> The object. </param>
/// <returns> Return the modified ostream. </returns>
friend std::ostream& operator <<(std::ostream& os, const CBias& obj)
{
os << obj.print().str();
return os;
}
protected:
//*********************
//***** Variables *****
//*********************
size_t m_n = 0; ///< Number of classification launched (used for update).
Eigen::MatrixXd m_bias; ///< Bias Matrix.
Eigen::MatrixXd m_biasIS; ///< Inverse squared root bias matrix (stored and pre-computed for application of bias).
};
} // namespace Geometry
@@ -0,0 +1,117 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CMatrixClassifierFgMDM.hpp
/// \brief Class of Minimum Distance to Mean with geodesic filtering (FgMDM) Classifier.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 10/12/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/classifier/CMatrixClassifierFgMDMRT.hpp"
namespace Geometry {
/// <summary> Class of Minimum Distance to Mean with geodesic filtering (FgMDM) Classifier. </summary>
/// <seealso cref="CMatrixClassifierMDM" />
class CMatrixClassifierFgMDM final : public CMatrixClassifierFgMDMRT
{
public:
//***********************
//***** Constructor *****
//***********************
/// <summary> Initializes a new instance of the <see cref="CMatrixClassifierFgMDM"/> class. </summary>
CMatrixClassifierFgMDM() = default;
/// <summary> Default Copy constructor. Initializes a new instance of the <see cref="CMatrixClassifierFgMDM"/> class. </summary>
/// <param name="obj"> Initial object. </param>
CMatrixClassifierFgMDM(const CMatrixClassifierFgMDM& obj) { *this = obj; }
/// <summary> Copy constructor with parent class. Initializes a new instance of the <see cref="CMatrixClassifierFgMDM"/> class. </summary>
/// <param name="obj"> Initial object. </param>
explicit CMatrixClassifierFgMDM(const CMatrixClassifierFgMDMRT& obj) { copy(obj); }
/// <summary> Initializes a new instance of the <see cref="CMatrixClassifierFgMDM"/> class and set base members. </summary>
/// <param name="nbClass"> The number of classes. </param>
/// <param name="metric"> Metric to use to calculate means (see also <see cref="EMetric" />). </param>
explicit CMatrixClassifierFgMDM(const size_t nbClass, const EMetric metric) : CMatrixClassifierFgMDMRT(nbClass, metric) { }
/// <summary> Finalizes an instance of the <see cref="CMatrixClassifierFgMDM"/> class. </summary>
/// <remarks> clear the <see cref="m_means"/> vector of Matrix and the <see cref="m_dataset"/> member. </remarks>
~CMatrixClassifierFgMDM() override;
//***************************
//***** Getter / Setter *****
//***************************
void setDataset(const std::vector<std::vector<Eigen::MatrixXd>>& dataset) { m_dataset = dataset; } ///< Set dataset.
const std::vector<std::vector<Eigen::MatrixXd>>& getDataset() const { return m_dataset; } ///< Get dataset.
//**********************
//***** Classifier *****
//**********************
/// <summary> Train the classifier with the dataset.
/// -# Compute the Riemann mean of all trials as reference and store this in <see cref="m_ref"/> member.
/// -# Set the good number of classes
/// -# Trasnform data to the Tangent Space with the reference
/// -# Compute the FgDA Weight (<see cref="FgDACompute" />).
/// -# Apply the FgDA Weight and return to Original Manifold.
/// -# Apply the train function of MDM Classifier (see <see cref="CMatrixClassifierMDM::train"/>)
/// </summary>
/// <param name="dataset"> The dataset (first dimension is the classes, second dimension is the trials as covariance matrix). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <remarks> the dataset is saved. </remarks>
bool train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset) override;
/// <summary> Classify the matrix and return the class id, the distance and the probability of each class.\n
/// -# Transform the sample to the Tangent Space.\n
/// -# Apply the FgDA weight.\n
/// -# Return to the original Manifold.\n
/// -# Apply the classify function of MDM Classifier (see <see cref="CMatrixClassifierMDM::classify"/>)
/// </summary>
/// <remarks> The classifier is train with the new sample. </remarks>
/// \copydetails IMatrixClassifier::classify(const Eigen::MatrixXd&, size_t&, std::vector<double>&, std::vector<double>&, const EAdaptations, const size_t&)
bool classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance, std::vector<double>& probability,
EAdaptations adaptation = EAdaptations::None, const size_t& realClassId = std::numeric_limits<size_t>::max()) override;
//*****************************
//***** Override Operator *****
//*****************************
/// <summary> Get the type of the classifier. </summary>
/// <returns> Minimum Distance to Mean with geodesic filtering (FgMDM). </returns>
std::string getType() const override { return toString(EMatrixClassifiers::FgMDM); }
/// <summary> Override the affectation operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> The copied object. </returns>
CMatrixClassifierFgMDM& operator=(const CMatrixClassifierFgMDM& obj)
{
copy(obj);
return *this;
}
/// <summary> Override the ostream operator. </summary>
/// <param name="os"> The ostream. </param>
/// <param name="obj"> The object. </param>
/// <returns> Return the modified ostream. </returns>
friend std::ostream& operator <<(std::ostream& os, const CMatrixClassifierFgMDM& obj)
{
os << obj.print().str();
return os;
}
protected:
///<summary> train with the actual dataset (<see cref="m_dataset"/>). </summary>
bool train() { return CMatrixClassifierFgMDMRT::train(m_dataset); }
//*********************
//***** Variables *****
//*********************
std::vector<std::vector<Eigen::MatrixXd>> m_dataset; ///< Data set for train and adaptation (it can quickly rise).
};
} // namespace Geometry
@@ -0,0 +1,151 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CMatrixClassifierFgMDMRT.hpp
/// \brief Class of Minimum Distance to Mean with geodesic filtering (FgMDM) Classifier RT (adaptation is Real Time Assumed)
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 10/12/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/classifier/CMatrixClassifierMDM.hpp"
namespace Geometry {
/// <summary> Class of Minimum Distance to Mean with geodesic filtering (FgMDM) Classifier RT (adaptation is Real Time Assumed). </summary>
/// <seealso cref="CMatrixClassifierMDM" />
class CMatrixClassifierFgMDMRT : public CMatrixClassifierMDM
{
public:
//***********************
//***** Constructor *****
//***********************
/// <summary> Initializes a new instance of the <see cref="CMatrixClassifierFgMDMRT"/> class. </summary>
CMatrixClassifierFgMDMRT() = default;
/// <summary> Default Copy constructor. Initializes a new instance of the <see cref="CMatrixClassifierFgMDMRT"/> class. </summary>
/// <param name="obj"> Initial object. </param>
CMatrixClassifierFgMDMRT(const CMatrixClassifierFgMDMRT& obj) { *this = obj; }
/// <summary> Initializes a new instance of the <see cref="CMatrixClassifierFgMDMRT"/> class and set base members. </summary>
/// <param name="nbClass"> The number of classes. </param>
/// <param name="metric"> Metric to use to calculate means (see also <see cref="EMetric" />). </param>
explicit CMatrixClassifierFgMDMRT(const size_t nbClass, const EMetric metric) : CMatrixClassifierMDM(nbClass, metric) { }
/// <summary> Finalizes an instance of the <see cref="CMatrixClassifierFgMDMRT"/> class. </summary>
/// <remarks> clear the <see cref="m_means"/> vector of Matrix. </remarks>
~CMatrixClassifierFgMDMRT() override = default;
//***************************
//***** Getter / Setter *****
//***************************
const Eigen::MatrixXd& getRef() const { return m_ref; } ///< Get reference of tangent space.
void setRef(const Eigen::MatrixXd& ref) { m_ref = ref; } ///< Set reference of tangent space.
const Eigen::MatrixXd& getWeight() const { return m_weight; } ///< Get weight matrix of geodesic filter.
void setWeight(const Eigen::MatrixXd& weight) { m_weight = weight; } ///< Set weight matrix of geodesic filter.
//**********************
//***** Classifier *****
//**********************
/// <summary> Train the classifier with the dataset.
/// -# Compute the Riemann mean of all trials as reference and store this in <see cref="m_ref"/> member.
/// -# Set the good number of classes
/// -# Trasnform data to the Tangent Space with the reference
/// -# Compute the FgDA Weight (<see cref="FgDACompute" />).
/// -# Apply the FgDA Weight and return to Original Manifold.
/// -# Apply the train function of MDM Classifier (see <see cref="CMatrixClassifierMDM::train"/>)
/// </summary>
/// <param name="dataset"> The dataset (first dimension is the classes, second dimension is the trials as covariance matrix). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset) override;
/// <summary> Classify the matrix and return the class id, the distance and the probability of each class.\n
/// -# Transform the sample to the Tangent Space.\n
/// -# Apply the FgDA weight.\n
/// -# Return to the original Manifold.\n
/// -# Apply the classify function of MDM Classifier (see <see cref="CMatrixClassifierMDM::classify"/>)
/// </summary>
/// <remarks>
/// <b>Remark</b> : We use the MDM classification whatever the adaptation method chosen.
/// Thus the MDM part evolves but the geodesic filtering does not evolve to keep an execution online.
/// A version allowing the adaptation of the Filter will be implemented for offline execution.
/// </remarks>
/// \copydetails IMatrixClassifier::classify(const Eigen::MatrixXd&, size_t&, std::vector<double>&, std::vector<double>&, const EAdaptations, const size_t&)
bool classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance, std::vector<double>& probability,
EAdaptations adaptation = EAdaptations::None, const size_t& realClassId = std::numeric_limits<size_t>::max()) override;
//*****************************
//***** Override Operator *****
//*****************************
/// <summary> Check if object are equals (with a precision tolerance). </summary>
/// <param name="obj"> The second object. </param>
/// <param name="precision"> Precision for matrix comparison. </param>
/// <returns> <c>True</c> if the two elements are equals (with a precision tolerance). </returns>
bool isEqual(const CMatrixClassifierFgMDMRT& obj, double precision = 1e-6) const;
/// <summary> Copy object value. </summary>
/// <param name="obj"> The object to copy. </param>
void copy(const CMatrixClassifierFgMDMRT& obj);
/// <summary> Get the type of the classifier. </summary>
/// <returns> Minimum Distance to Mean with geodesic filtering (FgMDM). </returns>
std::string getType() const override { return toString(EMatrixClassifiers::FgMDM_RT); }
/// <summary> Override the affectation operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> The copied object. </returns>
CMatrixClassifierFgMDMRT& operator=(const CMatrixClassifierFgMDMRT& obj)
{
copy(obj);
return *this;
}
/// <summary> Override the equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CMatrixClassifierFgMDMRT"/> are equals. </returns>
bool operator==(const CMatrixClassifierFgMDMRT& obj) const { return isEqual(obj); }
/// <summary> Override the not equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CMatrixClassifierFgMDMRT"/> are diffrents. </returns>
bool operator!=(const CMatrixClassifierFgMDMRT& obj) const { return !isEqual(obj); }
/// <summary> Override the ostream operator. </summary>
/// <param name="os"> The ostream. </param>
/// <param name="obj"> The object. </param>
/// <returns> Return the modified ostream. </returns>
friend std::ostream& operator <<(std::ostream& os, const CMatrixClassifierFgMDMRT& obj)
{
os << obj.print().str();
return os;
}
protected:
//***********************
//***** XML Manager *****
//***********************
/// <summary> Save Additionnal informations (Reference and LDA Weight). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const override;
/// <summary> Load Additionnal informations (Reference and LDA Weight). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool loadAdditional(tinyxml2::XMLElement* data) override;
/// <summary> Prints the Additional informations (Reference and LDA Weight). </summary>
/// <returns> Additional informations in stringstream. </returns>
std::stringstream printAdditional() const override;
//*********************
//***** Variables *****
//*********************
Eigen::MatrixXd m_ref; ///< Reference matrix of tanget space.
Eigen::MatrixXd m_weight; ///< Weght matrix of Filter Geodesic Discriminant Analysis.
};
} // namespace Geometry
@@ -0,0 +1,148 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CMatrixClassifierFgMDMRTRebias.hpp
/// \brief Class of Minimum Distance to Mean with geodesic filtering (FgMDM) Classifier RT (adaptation is Real Time Assumed)
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 10/12/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/classifier/CMatrixClassifierFgMDMRT.hpp"
#include "geometry/classifier/CBias.hpp"
namespace Geometry {
/// <summary> Class of Minimum Distance to Mean with geodesic filtering (FgMDM) Classifier RT (adaptation is Real Time Assumed). </summary>
/// <seealso cref="CMatrixClassifierFgMDMRT" />
class CMatrixClassifierFgMDMRTRebias final : public CMatrixClassifierFgMDMRT
{
public:
//***********************
//***** Constructor *****
//***********************
/// <summary> Initializes a new instance of the <see cref="CMatrixClassifierFgMDMRTRebias"/> class. </summary>
CMatrixClassifierFgMDMRTRebias() = default;
/// <summary> Default Copy constructor. Initializes a new instance of the <see cref="CMatrixClassifierFgMDMRTRebias"/> class. </summary>
/// <param name="obj"> Initial object. </param>
CMatrixClassifierFgMDMRTRebias(const CMatrixClassifierFgMDMRTRebias& obj) { *this = obj; }
/// <summary> Initializes a new instance of the <see cref="CMatrixClassifierFgMDMRTRebias"/> class and set base members. </summary>
/// <param name="nbClass"> The number of classes. </param>
/// <param name="metric"> Metric to use to calculate means (see also <see cref="EMetric" />). </param>
explicit CMatrixClassifierFgMDMRTRebias(const size_t nbClass, const EMetric metric) : CMatrixClassifierFgMDMRT(nbClass, metric) { }
/// <summary> Finalizes an instance of the <see cref="CMatrixClassifierFgMDMRTRebias"/> class. </summary>
/// <remarks> clear the <see cref="m_means"/> vector of Matrix. </remarks>
~CMatrixClassifierFgMDMRTRebias() override = default;
//***************************
//***** Getter / Setter *****
//***************************
const CBias& getBias() const { return m_bias; } ///< Get Rebias Method.
void setBias(const CBias& bias) { m_bias = bias; } ///< Set Rebias Method.
//**********************
//***** Classifier *****
//**********************
/// <summary> Train the classifier with the dataset.
/// -# Compute the Riemann mean of all trials as reference and store this in <see cref="m_ref"/> member.
/// -# Set the good number of classes
/// -# Trasnform data to the Tangent Space with the reference
/// -# Compute the FgDA Weight (<see cref="FgDACompute" />).
/// -# Apply the FgDA Weight and return to Original Manifold.
/// -# Apply the train function of MDM Classifier (see <see cref="CMatrixClassifierMDM::train"/>)
/// </summary>
/// <param name="dataset"> The dataset (first dimension is the classes, second dimension is the trials as covariance matrix). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset) override;
/// <summary> Classify the matrix and return the class id, the distance and the probability of each class.\n
/// -# Transform the sample to the Tangent Space.\n
/// -# Apply the FgDA weight.\n
/// -# Return to the original Manifold.\n
/// -# Apply the classify function of MDM Classifier (see <see cref="CMatrixClassifierMDM::classify"/>)
/// </summary>
/// <remarks>
/// <b>Remark</b> : We use the MDM classification whatever the adaptation method chosen.
/// Thus the MDM part evolves but the geodesic filtering does not evolve to keep an execution online.
/// A version allowing the adaptation of the Filter will be implemented for offline execution.
/// </remarks>
/// \copydetails IMatrixClassifier::classify(const Eigen::MatrixXd&, size_t&, std::vector<double>&, std::vector<double>&, const EAdaptations, const size_t&)
bool classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance, std::vector<double>& probability,
EAdaptations adaptation = EAdaptations::None, const size_t& realClassId = std::numeric_limits<size_t>::max()) override;
//*****************************
//***** Override Operator *****
//*****************************
/// <summary> Check if object are equals (with a precision tolerance). </summary>
/// <param name="obj"> The second object. </param>
/// <param name="precision"> Precision for matrix comparison. </param>
/// <returns> <c>True</c> if the two elements are equals (with a precision tolerance). </returns>
bool isEqual(const CMatrixClassifierFgMDMRTRebias& obj, double precision = 1e-6) const;
/// <summary> Copy object value. </summary>
/// <param name="obj"> The object to copy. </param>
void copy(const CMatrixClassifierFgMDMRTRebias& obj);
/// <summary> Get the type of the classifier. </summary>
/// <returns> Minimum Distance to Mean with geodesic filtering (FgMDM). </returns>
std::string getType() const override { return toString(EMatrixClassifiers::FgMDM_RT_Rebias); }
/// <summary> Override the affectation operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> The copied object. </returns>
CMatrixClassifierFgMDMRTRebias& operator=(const CMatrixClassifierFgMDMRTRebias& obj)
{
copy(obj);
return *this;
}
/// <summary> Override the equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CMatrixClassifierFgMDMRTRebias"/> are equals. </returns>
bool operator==(const CMatrixClassifierFgMDMRTRebias& obj) const { return isEqual(obj); }
/// <summary> Override the not equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CMatrixClassifierFgMDMRTRebias"/> are diffrents. </returns>
bool operator!=(const CMatrixClassifierFgMDMRTRebias& obj) const { return !isEqual(obj); }
/// <summary> Override the ostream operator. </summary>
/// <param name="os"> The ostream. </param>
/// <param name="obj"> The object. </param>
/// <returns> Return the modified ostream. </returns>
friend std::ostream& operator <<(std::ostream& os, const CMatrixClassifierFgMDMRTRebias& obj)
{
os << obj.print().str();
return os;
}
protected:
//***********************
//***** XML Manager *****
//***********************
/// <summary> Save Additionnal informations (Reference and LDA Weight). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const override;
/// <summary> Load Additionnal informations (Reference and LDA Weight). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool loadAdditional(tinyxml2::XMLElement* data) override;
/// <summary> Prints the Additional informations (Reference and LDA Weight). </summary>
/// <returns> Additional informations in stringstream. </returns>
std::stringstream printAdditional() const override;
//*********************
//***** Variables *****
//*********************
CBias m_bias; ///< Rebias Method.
};
} // namespace Geometry
@@ -0,0 +1,164 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CMatrixClassifierMDM.hpp
/// \brief Class of Minimum Distance to Mean (MDM) Classifier
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 10/12/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/classifier/IMatrixClassifier.hpp"
#include "geometry/Metrics.hpp"
namespace Geometry {
/// <summary> Class of Minimum Distance to Mean (MDM) Classifier. </summary>
/// <seealso cref="IMatrixClassifier" />
class CMatrixClassifierMDM : public IMatrixClassifier
{
public:
//***********************
//***** Constructor *****
//***********************
/// <summary> Default constructor. Initializes a new instance of the <see cref="CMatrixClassifierMDM"/> class. </summary>
CMatrixClassifierMDM() { CMatrixClassifierMDM::setClassCount(m_nbClass); }
/// <summary> Default Copy constructor. Initializes a new instance of the <see cref="CMatrixClassifierMDM"/> class. </summary>
/// <param name="obj"> Initial object. </param>
CMatrixClassifierMDM(const CMatrixClassifierMDM& obj) { *this = obj; }
/// <summary> Initializes a new instance of the <see cref="CMatrixClassifierMDM"/> class and set base members. </summary>
/// <param name="nbClass"> The number of classes. </param>
/// <param name="metric"> Metric to use to calculate means (see also <see cref="EMetric" />). </param>
explicit CMatrixClassifierMDM(size_t nbClass, EMetric metric);
/// <summary> Finalizes an instance of the <see cref="CMatrixClassifierMDM"/> class. </summary>
/// <remarks> clear the <see cref="m_means"/> vector of Matrix. </remarks>
~CMatrixClassifierMDM() override;
//***************************
//***** Getter / Setter *****
//***************************
const std::vector<Eigen::MatrixXd>& getMeans() const { return m_means; } ///< Get Means of classes.
void setMeans(const std::vector<Eigen::MatrixXd>& means) { m_means = means; } ///< Set Means of classes.
const std::vector<size_t>& getTrialNumbers() const { return m_nbTrials; } ///< Get the number of trial used for train.
void setTrialNumbers(const std::vector<size_t>& nbTrials) { m_nbTrials = nbTrials; } ///< Set the number of trial used for train.
//**********************
//***** Classifier *****
//**********************
/// <summary> Set the class count. </summary>
/// <remarks> resize the <see cref="m_means"/> vector of Matrix. </remarks>
void setClassCount(size_t nbClass) override;
/// <summary> Train the classifier with the dataset.
/// -# Set the good number of classes
/// -# Compute the mean of each class (row) with the metric (<see cref="EMetric" />) in <see cref="m_metric"/> member.
/// -# Set the number of trials for each class.
/// </summary>
/// <param name="dataset"> The dataset (first dimension is the classes, second dimension is the trials as covariance matrix). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset) override;
/// <summary> Classify the matrix and return the class id, the distance and the probability of each class.\n
/// - Compute the distance between the sample and each mean matrix.\n
/// - The class with the closest mean is the predicted class.\n
/// - The distances are returned.\n
/// - The probability \f$ \mathcal{P}_i \f$ to be the class \f$ i \f$ is compute as :
/// \f[
/// \begin{aligned}
/// p_i &= \frac{d_{\text{min}}}{d_i}\\
/// \mathcal{P}_i &= \frac{p_i}{\sum{\left(p_i\right)}}
/// \end{aligned}
/// \f]
/// </summary>
/// <remarks>
/// <b>Remark</b> : The probability is normalized \f$ \sum{\left(\mathcal{P}_i\right)} = 1 \f$\n
/// If the classfier is adapted, launch adaptation method (expected class if supervised, predicted class if unsupervised).\n
/// With \f$ C_k \f$ the prototype (mean) of the Class \f$ k \f$, \f$ \gamma_m \f$ the Geodesic (<see cref="Geodesic" />) with the metric \f$ m \f$ (<see cref="EMetric" />),
/// \f$ S \f$ the current trial (sample) and \f$ N_k \f$ the number of trials for the class \f$ k \f$ (with the current trial).
/// \f[
/// C_k = \gamma_m\left( C_k,S,\frac{1}{N_k}\right)
/// \f]
/// </remarks>
/// \copydetails IMatrixClassifier::classify(const Eigen::MatrixXd&, size_t&, std::vector<double>&, std::vector<double>&, const EAdaptations, const size_t&)
bool classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance, std::vector<double>& probability,
EAdaptations adaptation = EAdaptations::None, const size_t& realClassId = std::numeric_limits<size_t>::max()) override;
//*****************************
//***** Override Operator *****
//*****************************
/// <summary> Check if object are equals (with a precision tolerance). </summary>
/// <param name="obj"> The second object. </param>
/// <param name="precision"> Precision for matrix comparison. </param>
/// <returns> <c>True</c> if the two elements are equals (with a precision tolerance). </returns>
bool isEqual(const CMatrixClassifierMDM& obj, double precision = 1e-6) const;
/// <summary> Copy object value. </summary>
/// <param name="obj"> The object to copy. </param>
void copy(const CMatrixClassifierMDM& obj);
/// <summary> Get the type of the classifier. </summary>
/// <returns> Minimum Distance to Mean. </returns>
std::string getType() const override { return toString(EMatrixClassifiers::MDM); }
/// <summary> Override the affectation operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> The copied object. </returns>
CMatrixClassifierMDM& operator=(const CMatrixClassifierMDM& obj)
{
copy(obj);
return *this;
}
/// <summary> Override the equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CMatrixClassifierMDM"/> are equals. </returns>
bool operator==(const CMatrixClassifierMDM& obj) const { return isEqual(obj); }
/// <summary> Override the not equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CMatrixClassifierMDM"/> are diffrents. </returns>
bool operator!=(const CMatrixClassifierMDM& obj) const { return !isEqual(obj); }
/// <summary> Override the ostream operator. </summary>
/// <param name="os"> The ostream. </param>
/// <param name="obj"> The object. </param>
/// <returns> Return the modified ostream. </returns>
friend std::ostream& operator <<(std::ostream& os, const CMatrixClassifierMDM& obj)
{
os << obj.print().str();
return os;
}
protected:
//***********************
//***** XML Manager *****
//***********************
/// <summary> Save Classes informations (Mean and number of trials of each class). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool saveClasses(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const override;
/// <summary> Load Classes informations (Mean and number of trials of each class). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool loadClasses(tinyxml2::XMLElement* data) override;
//*****************************
//***** Override Operator *****
//*****************************
std::stringstream printClasses() const override;
//*********************
//***** Variables *****
//*********************
std::vector<Eigen::MatrixXd> m_means; ///< Mean Matrix of each class.
std::vector<size_t> m_nbTrials; ///< Number of trials of each class.
};
} // namespace Geometry
@@ -0,0 +1,141 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CMatrixClassifierMDMRebias.hpp
/// \brief Class of Minimum Distance to Mean (MDM) Classifier with Rebias.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 10/12/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include "geometry/classifier/CMatrixClassifierMDM.hpp"
#include "geometry/Metrics.hpp"
#include "geometry/classifier/CBias.hpp"
namespace Geometry {
/// <summary> Class of Minimum Distance to Mean (MDM) Classifier with Rebias. </summary>
/// <seealso cref="IMatrixClassifier" />
class CMatrixClassifierMDMRebias final : public CMatrixClassifierMDM
{
public:
//***********************
//***** Constructor *****
//***********************
/// <summary> Default constructor. Initializes a new instance of the <see cref="CMatrixClassifierMDMRebias"/> class. </summary>
CMatrixClassifierMDMRebias() = default;
/// <summary> Default Copy constructor. Initializes a new instance of the <see cref="CMatrixClassifierMDMRebias"/> class. </summary>
/// <param name="obj"> Initial object. </param>
CMatrixClassifierMDMRebias(const CMatrixClassifierMDMRebias& obj) { *this = obj; }
/// <summary> Initializes a new instance of the <see cref="CMatrixClassifierMDMRebias"/> class and set base members. </summary>
/// <param name="nbClass"> The number of classes. </param>
/// <param name="metric"> Metric to use to calculate means (see also <see cref="EMetric" />). </param>
explicit CMatrixClassifierMDMRebias(const size_t nbClass, const EMetric metric) : CMatrixClassifierMDM(nbClass, metric) { }
/// <summary> Finalizes an instance of the <see cref="CMatrixClassifierMDMRebias"/> class. </summary>
~CMatrixClassifierMDMRebias() override = default;
//***************************
//***** Getter / Setter *****
//***************************
const CBias& getBias() const { return m_bias; } ///< Get Rebias Method.
void setBias(const CBias& bias) { m_bias = bias; } ///< Set Rebias Method.
//**********************
//***** Classifier *****
//**********************
/// <summary> Train the classifier with the dataset.
/// -# Compute the mean of all trials with the metric (<see cref="EMetric" />) in <see cref="m_metric"/> member as reference and store this in <see cref="m_bias"/> member.
/// -# Set the good number of classes
/// -# Apply an affine transformation on each trials with the reference : \f$ S_\text{new} = R^{-1/2} * S * {R^{-1/2}}^{\mathsf{T}} \f$
/// -# Compute the mean of each class (row), on transformed trials, with the metric (<see cref="EMetric" />) in <see cref="m_metric"/> member.
/// -# Set the number of trials for each class.
/// </summary>
/// <param name="dataset"> The dataset (first dimension is the classes, second dimension is the trials as covariance matrix). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset) override;
/// <summary> Classify the matrix and return the class id, the distance and the probability of each class.
/// -# Apply an affine transformation on the trial (sample) with the reference : \f$ S_\text{new} = R^{-1/2} * S * {R^{-1/2}}^{\mathsf{T}} \f$
/// -# Update the reference with the current sample the first time and next with the Geodesic between the reference and the current sample.\n
/// With \f$ \gamma_m \f$ the Geodesic (<see cref="Geodesic" />) with the metric \f$ m \f$ (<see cref="EMetric" />) and \f$ N_c \f$ the number of classification : \f$ R = \gamma_\text{m}\left( R,S,\frac{1}{N_c} \right) \f$
/// -# Apply the classify function of MDM Classifier (see <see cref="CMatrixClassifierMDM::classify"/>)
/// </summary>
/// \copydetails IMatrixClassifier::classify(const Eigen::MatrixXd&, size_t&, std::vector<double>&, std::vector<double>&, const EAdaptations, const size_t&)
bool classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance, std::vector<double>& probability,
EAdaptations adaptation = EAdaptations::None, const size_t& realClassId = std::numeric_limits<size_t>::max()) override;
//*****************************
//***** Override Operator *****
//*****************************
/// <summary> Check if object are equals (with a precision tolerance). </summary>
/// <param name="obj"> The second object. </param>
/// <param name="precision"> Precision for matrix comparison. </param>
/// <returns> <c>True</c> if the two elements are equals (with a precision tolerance). </returns>
bool isEqual(const CMatrixClassifierMDMRebias& obj, double precision = 1e-6) const;
/// <summary> Copy object value. </summary>
/// <param name="obj"> The object to copy. </param>
void copy(const CMatrixClassifierMDMRebias& obj);
/// <summary> Get the type of the classifier. </summary>
/// <returns> Minimum Distance to Mean REBIAS. </returns>
std::string getType() const override { return toString(EMatrixClassifiers::MDM_Rebias); }
/// <summary> Override the affectation operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> The copied object. </returns>
CMatrixClassifierMDMRebias& operator=(const CMatrixClassifierMDMRebias& obj)
{
copy(obj);
return *this;
}
/// <summary> Override the equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CMatrixClassifierMDMRebias"/> are equals. </returns>
bool operator==(const CMatrixClassifierMDMRebias& obj) const { return isEqual(obj); }
/// <summary> Override the not equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="CMatrixClassifierMDMRebias"/> are diffrents. </returns>
bool operator!=(const CMatrixClassifierMDMRebias& obj) const { return !isEqual(obj); }
/// <summary> Override the ostream operator. </summary>
/// <param name="os"> The ostream. </param>
/// <param name="obj"> The object. </param>
/// <returns> Return the modified ostream. </returns>
friend std::ostream& operator <<(std::ostream& os, const CMatrixClassifierMDMRebias& obj)
{
os << obj.print().str();
return os;
}
protected:
/// <summary> Save Additionnal informations (reference and number of classification). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const override;
/// <summary> Load Additionnal informations (reference and number of classification). </summary>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
bool loadAdditional(tinyxml2::XMLElement* data) override;
/// <summary> Prints the Additional informations (reference and number of classification). </summary>
/// <returns> Additional informations in stringstream. </returns>
std::stringstream printAdditional() const override;
//*********************
//***** Variables *****
//*********************
CBias m_bias; ///< Rebias Method.
};
} // namespace Geometry
@@ -0,0 +1,315 @@
///-------------------------------------------------------------------------------------------------
///
/// \file IMatrixClassifier.hpp
/// \brief Abstract class of Matrix Classifier
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 10/12/2018.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#include <Eigen/Dense>
#include <vector>
#include <limits>
#include "geometry/Metrics.hpp"
#include "geometry/3rd-party/tinyxml2.h"
namespace Geometry {
///-------------------------------------------------------------------------------------------------
/// <summary> Enumeration of Adaptation Methods for classifier. </summary>
enum class EAdaptations
{
None, ///< No Adaptation.
Supervised, ///< Supervised Adaptation.
Unsupervised ///< Unsupervised Adaptation.
};
/// <summary> Convert adaptations to string. </summary>
/// <param name="type"> The type of adaptation. </param>
/// <returns> <c>std::string</c> </returns>
inline std::string toString(const EAdaptations type)
{
switch (type)
{
case EAdaptations::None: return "No";
case EAdaptations::Supervised: return "Supervised";
case EAdaptations::Unsupervised: return "Unsupervised";
}
return "Invalid";
}
/// <summary> Convert string to adaptations. </summary>
/// <param name="type"> The type of adaptation. </param>
/// <returns> <see cref="EAdaptations"/> </returns>
inline EAdaptations StringToAdaptation(const std::string& type)
{
if (type == "No") { return EAdaptations::None; }
if (type == "Supervised") { return EAdaptations::Supervised; }
return EAdaptations::Unsupervised;
}
///-------------------------------------------------------------------------------------------------
///-------------------------------------------------------------------------------------------------
/// <summary> Enumeration of Matrix Classifiers. </summary>
enum class EMatrixClassifiers
{
MDM, ///< Minimum Distance To Mean (MDM) Classifier.
MDM_Rebias, ///< Minimum Distance To Mean Rebias (MDM Rebias) Classifier.
FgMDM_RT, ///< Minimum Distance to Mean with geodesic filtering (FgMDM) (Real Time adaptation assumed).
FgMDM, ///< Minimum Distance to Mean with geodesic filtering (FgMDM).
FgMDM_RT_Rebias, ///< Minimum Distance to Mean with geodesic filtering & Rebias adaptation (FgMDM Rebias) (Real Time adaptation assumed).
FgMDM_Rebias ///< Minimum Distance to Mean with geodesic filtering & Rebias adaptation (FgMDM Rebias).
};
/// <summary> Convert Matrix Classifiers to string. </summary>
/// <param name="type"> The type of classifier. </param>
/// <returns> <c>std::string</c> </returns>
inline std::string toString(const EMatrixClassifiers type)
{
switch (type)
{
case EMatrixClassifiers::MDM: return "Minimum Distance to Mean (MDM)";
case EMatrixClassifiers::MDM_Rebias: return "Minimum Distance to Mean Rebias (MDM Rebias)";
case EMatrixClassifiers::FgMDM_RT: return "Minimum Distance to Mean with geodesic filtering (FgMDM) (Real Time adaptation assumed)";
case EMatrixClassifiers::FgMDM: return "Minimum Distance to Mean with geodesic filtering (FgMDM)";
case EMatrixClassifiers::FgMDM_RT_Rebias: return "Minimum Distance to Mean with geodesic filtering Rebias (FgMDM Rebias) (Real Time adaptation assumed)";
case EMatrixClassifiers::FgMDM_Rebias: return "Minimum Distance to Mean with geodesic filtering Rebias (FgMDM Rebias)";
}
return "Invalid";
}
/// <summary> Convert string to Matrix Classifiers. </summary>
/// <param name="type"> The type of classifier. </param>
/// <returns> <see cref="EMatrixClassifiers"/> </returns>
inline EMatrixClassifiers StringToMatrixClassifier(const std::string& type)
{
if (type == "Minimum Distance to Mean (MDM)") { return EMatrixClassifiers::MDM; }
if (type == "Minimum Distance to Mean Rebias (MDM Rebias)") { return EMatrixClassifiers::MDM_Rebias; }
if (type == "Minimum Distance to Mean with geodesic filtering (FgMDM) (Real Time adaptation assumed)") { return EMatrixClassifiers::FgMDM_RT; }
if (type == "Minimum Distance to Mean with geodesic filtering (FgMDM)") { return EMatrixClassifiers::FgMDM; }
if (type == "Minimum Distance to Mean with geodesic filtering Rebias (FgMDM Rebias) (Real Time adaptation assumed)")
{
return EMatrixClassifiers::FgMDM_RT_Rebias;
}
return EMatrixClassifiers::FgMDM_Rebias;
}
///-------------------------------------------------------------------------------------------------
/// <summary> Format the Eigen matrix with Full Precision. </summary>
#define MATRIX_FORMAT Eigen::IOFormat(-2, 0, " ", "\n", "", "", "", "")
///-------------------------------------------------------------------------------------------------
/// <summary> Abstract class of Matrix Classifier. </summary>
class IMatrixClassifier
{
public:
//****************************
//***** Static Functions *****
//****************************
/// <summary> Format the Matrix for XML Saving. </summary>
/// <param name="in"> Matrix. </param>
/// <param name="out"> Stringstream. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
static bool convertMatrixToXMLFormat(const Eigen::MatrixXd& in, std::stringstream& out);
/// <summary> Fill the Matrix From XML Format. </summary>
/// <param name="in"> Stringstream. </param>
/// <param name="out"> Matrix. </param>
/// <param name="rows"> Number of rows. </param>
/// <param name="cols"> Number of cols. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
static bool convertXMLFormatToMatrix(std::stringstream& in, Eigen::MatrixXd& out, size_t rows, size_t cols);
/// <summary> Saves matrix. </summary>
/// <param name="element"> Matrix Node. </param>
/// <param name="matrix"> Matrix to save. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
static bool saveMatrix(tinyxml2::XMLElement* element, const Eigen::MatrixXd& matrix);
/// <summary> Load matrix. </summary>
/// <param name="element"> Matrix Node. </param>
/// <param name="matrix"> Matrix to load. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
static bool loadMatrix(tinyxml2::XMLElement* element, Eigen::MatrixXd& matrix);
//***********************
//***** Constructor *****
//***********************
/// <summary> Default constructor. Initializes a new instance of the <see cref="IMatrixClassifier"/> class. </summary>
IMatrixClassifier() = default;
/// <summary> Default Copy constructor. Initializes a new instance of the <see cref="IMatrixClassifier"/> class. </summary>
/// <param name="obj"> Initial object. </param>
IMatrixClassifier(const IMatrixClassifier& obj) { *this = obj; }
/// <summary> Initializes a new instance of the <see cref="IMatrixClassifier"/> class and set members. </summary>
/// <param name="nbClass"> The number of classes. </param>
/// <param name="metric"> Metric to use to calculate means (see also <see cref="EMetric" />). </param>
explicit IMatrixClassifier(size_t nbClass, EMetric metric);
/// <summary> Finalizes an instance of the <see cref="IMatrixClassifier"/> class. </summary>
virtual ~IMatrixClassifier() = default;
//**********************
//***** Classifier *****
//**********************
virtual size_t getClassCount() const { return m_nbClass; } ///< Get the class count.
virtual void setClassCount(size_t nbClass); ///< Set the class count.
/// <summary> Train the classifier with the dataset. </summary>
/// <param name="dataset"> The dataset (first dimension is the classes, second dimension is the trials as covariance matrix). </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
virtual bool train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset) = 0;
/// <summary> Classify the matrix and return the class id (override of same function with all argument). </summary>
/// <param name="sample"> The sample to classify. </param>
/// <param name="classId"> The predicted class. </param>
/// <param name="adaptation"> Adaptation method for the classfier <see cref="EAdaptations" />. </param>
/// <param name="realClassId"> The expected class id if supervised adaptation. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
/// <seealso cref="classify(const Eigen::MatrixXd&, size_t&, std::vector<double>&, std::vector<double>&, EAdaptations, const size_t&)"/>
virtual bool classify(const Eigen::MatrixXd& sample, size_t& classId,
EAdaptations adaptation = EAdaptations::None, const size_t& realClassId = std::numeric_limits<size_t>::max());
/// <summary> Classify the matrix and return the class id, the distance and the probability of each class. </summary>
/// <param name="sample"> The sample to classify. </param>
/// <param name="classId"> The predicted class. </param>
/// <param name="distance"> The distance of the sample with each class. </param>
/// <param name="probability"> The probability of the sample with each class. </param>
/// <param name="adaptation"> Adaptation method for the classfier <see cref="EAdaptations" />. </param>
/// <param name="realClassId"> The expected class id if supervised adaptation. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
virtual bool classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance, std::vector<double>& probability,
EAdaptations adaptation = EAdaptations::None, const size_t& realClassId = std::numeric_limits<size_t>::max()) = 0;
//***********************
//***** XML Manager *****
//***********************
/// <summary> Saves the classifier information in an XML file. </summary>
/// <param name="filename"> Filename. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
virtual bool saveXML(const std::string& filename) const;
/// <summary> Loads the classifier information from an XML file. </summary>
/// <param name="filename"> Filename. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
virtual bool loadXML(const std::string& filename);
//*****************************
//***** Override Operator *****
//*****************************
/// <summary> Check if object are equals (with a precision tolerance). </summary>
/// <param name="obj"> The second object. </param>
/// <param name="precision"> Precision for matrix comparison. </param>
/// <returns> <c>True</c> if the two elements are equals (with a precision tolerance). </returns>
bool isEqual(const IMatrixClassifier& obj, double precision = 1e-6) const;
/// <summary> Copy object value. </summary>
/// <param name="obj"> The object to copy. </param>
void copy(const IMatrixClassifier& obj);
/// <summary> Get the type of the classifier. </summary>
/// <returns> The type in string. </returns>
virtual std::string getType() const = 0;
/// <summary> Get the Classifier information for output. </summary>
/// <returns> The Classifier print in stringstream. </returns>
virtual std::stringstream print() const;
/// <summary> Override the affectation operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> The copied object. </returns>
IMatrixClassifier& operator=(const IMatrixClassifier& obj)
{
copy(obj);
return *this;
}
/// <summary> Override the equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two <see cref="IMatrixClassifier"/> are equals. </returns>
bool operator==(const IMatrixClassifier& obj) const { return isEqual(obj); }
/// <summary> Override the not equal operator. </summary>
/// <param name="obj"> The second object. </param>
/// <returns> <c>True</c> if the two objects are diffrents. </returns>
bool operator!=(const IMatrixClassifier& obj) const { return !isEqual(obj); }
/// <summary> Override the ostream operator. </summary>
/// <param name="os"> The ostream. </param>
/// <param name="obj"> The object. </param>
/// <returns> Return the modified ostream. </returns>
friend std::ostream& operator <<(std::ostream& os, const IMatrixClassifier& obj)
{
os << obj.print().str();
return os;
}
protected:
/// <summary> Prints the header informations. </summary>
/// <returns> Header informations in stringstream. </returns>
virtual std::stringstream printHeader() const;
/// <summary> Prints the Additional informations. </summary>
/// <returns> Additional informations in stringstream. </returns>
virtual std::stringstream printAdditional() const { return std::stringstream(); }
/// <summary> Prints the Classes informations. </summary>
/// <returns> Classes informations in stringstream. </returns>
virtual std::stringstream printClasses() const { return std::stringstream(); }
//***********************
//***** XML Manager *****
//***********************
/// <summary> Add the attribute on the first node (general informations as classifier type, number of class...).
///
/// -# The type of the classifier : <see cref="getType"/>
/// -# The number of classes : <see cref="m_nbClass"/>
/// -# The metric to use : <see cref="m_metric"/>
/// </summary>
/// <param name="data"> Node to modify. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
virtual bool saveHeader(tinyxml2::XMLElement* data) const;
/// <summary> Loads the attribute on the first node (general informations as classifier type, number of class...).
///
/// -# Check the type : <see cref="getType"/>
/// -# The number of classes : <see cref="m_nbClass"/>
/// -# The metric to use : <see cref="m_metric"/>
/// </summary>
/// <param name="data"> Node to read. </param>
/// <returns> <c>True</c> if it succeeds, <c>False</c> otherwise. </returns>
virtual bool loadHeader(tinyxml2::XMLElement* data);
/// <summary> Save Additionnal informations (none at this level). </summary>
/// <returns> <c>True</c>. </returns>
virtual bool saveAdditional(tinyxml2::XMLDocument& /*doc*/, tinyxml2::XMLElement* /*data*/) const { return true; }
/// <summary> Load Additionnal informations (none at this level). </summary>
/// <returns> <c>True</c>. </returns>
virtual bool loadAdditional(tinyxml2::XMLElement* /*data*/) { return true; }
/// <summary> Save Classes informations (none at this level). </summary>
/// <returns> <c>True</c>. </returns>
virtual bool saveClasses(tinyxml2::XMLDocument& /*doc*/, tinyxml2::XMLElement* /*data*/) const { return true; }
/// <summary> Load Classes informations (none at this level). </summary>
/// <returns> <c>True</c>. </returns>
virtual bool loadClasses(tinyxml2::XMLElement* /*data*/) { return true; }
//*********************
//***** Variables *****
//*********************
size_t m_nbClass = 2; ///< Number of classes to classify.
EMetric m_metric = EMetric::Riemann; ///< Metric to use to calculate means and distances (see also <see cref="EMetric" />).
};
} // namespace Geometry