init
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
PROJECT(openvibe-module-geometry)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
|
||||
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp include/*.hpp include/*.h)
|
||||
INCLUDE_DIRECTORIES(include)
|
||||
|
||||
# We use static library to allow template functions and stl object in class
|
||||
ADD_LIBRARY(${PROJECT_NAME} STATIC ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${MODULES_FOLDER})
|
||||
|
||||
if(UNIX)
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-fPIC")
|
||||
ENDIF(UNIX)
|
||||
|
||||
if(WIN32)
|
||||
ADD_DEFINITIONS(/bigobj) # Definition for big obj file in debug mode with visual studio
|
||||
ENDIF(WIN32)
|
||||
|
||||
ADD_DEFINITIONS(-D_USE_MATH_DEFINES) # Definition for constant math as M_PI
|
||||
|
||||
|
||||
# OpenViBE Third Party
|
||||
INCLUDE("FindThirdPartyEigen")
|
||||
INCLUDE("FindThirdPartyBoost")
|
||||
|
||||
# ---------------------------------
|
||||
# Target macros
|
||||
# Defines target operating system, architecture and compiler
|
||||
# ---------------------------------
|
||||
SET_BUILD_PLATFORM()
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
|
||||
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h")
|
||||
|
||||
# ---------------------------------
|
||||
# Test applications
|
||||
# ---------------------------------
|
||||
IF(OV_COMPILE_TESTS)
|
||||
ADD_SUBDIRECTORY(test)
|
||||
ENDIF()
|
||||
+2053
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<T> </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<T></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<T></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
|
||||
+46
@@ -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
|
||||
+109
@@ -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
|
||||
+161
@@ -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
|
||||
+141
@@ -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
|
||||
+117
@@ -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
|
||||
+151
@@ -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
|
||||
+148
@@ -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
|
||||
+164
@@ -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
|
||||
+141
@@ -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
|
||||
+315
@@ -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
|
||||
+2557
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
||||
#include "geometry/Basics.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//************************************************
|
||||
//******************** Matrix ********************
|
||||
//************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
Eigen::MatrixXd AffineTransformation(const Eigen::MatrixXd& ref, const Eigen::MatrixXd& matrix)
|
||||
{
|
||||
const Eigen::MatrixXd isR = ref.sqrt().inverse(); // Inverse Square root of Reference matrix => isR
|
||||
return isR * matrix * isR.transpose(); // Affine transformation : isR * sample * isR^T
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardization(Eigen::MatrixXd& matrix, const EStandardization standard)
|
||||
{
|
||||
if (standard == EStandardization::Center) { return MatrixCenter(matrix); }
|
||||
if (standard == EStandardization::StandardScale) { return MatrixStandardization(matrix); }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardization(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, const EStandardization standard)
|
||||
{
|
||||
out = in;
|
||||
return MatrixStandardization(out, standard);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixCenter(Eigen::MatrixXd& matrix)
|
||||
{
|
||||
for (size_t i = 0, r = matrix.rows(), c = matrix.cols(); i < r; ++i)
|
||||
{
|
||||
const double mu = matrix.row(i).mean();
|
||||
for (size_t j = 0; j < c; ++j) { matrix(i, j) -= mu; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixCenter(const Eigen::MatrixXd& in, Eigen::MatrixXd& out)
|
||||
{
|
||||
out = in;
|
||||
return MatrixCenter(out);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardScaler(Eigen::MatrixXd& matrix)
|
||||
{
|
||||
Eigen::RowVectorXd dummyScale;
|
||||
return MatrixStandardScaler(matrix, dummyScale);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardScaler(Eigen::MatrixXd& matrix, Eigen::RowVectorXd& scale)
|
||||
{
|
||||
const size_t r = matrix.rows(), c = matrix.cols();
|
||||
std::vector<double> mu(r, 0), sigma(r, 0);
|
||||
scale.resize(r);
|
||||
|
||||
for (size_t i = 0; i < r; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < c; ++j)
|
||||
{
|
||||
const double value = matrix(i, j);
|
||||
mu[i] += value;
|
||||
sigma[i] += value * value;
|
||||
}
|
||||
|
||||
mu[i] /= double(c);
|
||||
sigma[i] = sigma[i] / double(c) - mu[i] * mu[i];
|
||||
scale[i] = sigma[i] == 0 ? 1 : sqrt(sigma[i]);
|
||||
|
||||
for (size_t j = 0; j < c; ++j) { matrix(i, j) = (matrix(i, j) - mu[i]) / scale[i]; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardScaler(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, Eigen::RowVectorXd& scale)
|
||||
{
|
||||
out = in;
|
||||
return MatrixStandardScaler(out, scale);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MatrixStandardScaler(const Eigen::MatrixXd& in, Eigen::MatrixXd& out)
|
||||
{
|
||||
Eigen::RowVectorXd dummyScale;
|
||||
return MatrixStandardScaler(in, out, dummyScale);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::string MatrixPrint(const Eigen::MatrixXd& matrix)
|
||||
{
|
||||
std::stringstream sstream;
|
||||
sstream << matrix;
|
||||
return sstream.str();
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool AreEquals(const Eigen::MatrixXd& matrix1, const Eigen::MatrixXd& matrix2, const double precision)
|
||||
{
|
||||
return matrix1.size() == matrix2.size() && (matrix1.size() == 0 || matrix1.isApprox(matrix2, precision));
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//************************************************
|
||||
//************************************************
|
||||
//************************************************
|
||||
|
||||
//*************************************************************
|
||||
//******************** Index Manipulations ********************
|
||||
//*************************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
Eigen::RowVectorXd GetElements(const Eigen::RowVectorXd& row, const std::vector<size_t>& index)
|
||||
{
|
||||
const size_t k = index.size();
|
||||
Eigen::RowVectorXd result(k);
|
||||
for (size_t i = 0; i < k; ++i) { result[i] = row[index[i]]; }
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
//*************************************************************
|
||||
//*************************************************************
|
||||
//*************************************************************
|
||||
|
||||
//***************************************************
|
||||
//******************** Validates ********************
|
||||
//***************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool InRange(const double value, const double min, const double max) { return (min <= value && value <= max); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool AreNotEmpty(const std::vector<Eigen::MatrixXd>& matrices)
|
||||
{
|
||||
if (matrices.empty()) { return false; }
|
||||
for (const auto& m : matrices) { if (!IsNotEmpty(m)) { return false; } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool IsNotEmpty(const Eigen::MatrixXd& matrix) { return (matrix.size() != 0); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool HaveSameSize(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b) { return (IsNotEmpty(a) && a.rows() == b.rows() && a.cols() == b.cols()); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
bool IsSquare(const Eigen::MatrixXd& matrix) { return (IsNotEmpty(matrix) && matrix.rows() == matrix.cols()); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool AreSquare(const std::vector<Eigen::MatrixXd>& matrices)
|
||||
{
|
||||
if (matrices.empty()) { return false; }
|
||||
for (const auto& m : matrices) { if (!IsSquare(m)) { return false; } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool HaveSameSize(const std::vector<Eigen::MatrixXd>& matrices)
|
||||
{
|
||||
if (matrices.empty()) { return false; }
|
||||
const size_t r = matrices[0].rows(), c = matrices[0].cols();
|
||||
for (const auto& m : matrices) { if (size_t(m.rows()) != r || size_t(m.cols()) != c) { return false; } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
//***************************************************
|
||||
//***************************************************
|
||||
//***************************************************
|
||||
|
||||
//********************************************************
|
||||
//******************** CSV MANAGEMENT ********************
|
||||
//********************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::vector<std::string> Split(const std::string& s, const std::string& sep)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::string::size_type i = 0, j;
|
||||
const std::string::size_type n = sep.size();
|
||||
|
||||
while ((j = s.find(sep, i)) != std::string::npos)
|
||||
{
|
||||
result.emplace_back(s, i, j - i); // Add part
|
||||
i = j + n; // Update pos
|
||||
}
|
||||
result.emplace_back(s, i, s.size() - 1 - i); // Last without \n
|
||||
return result;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
//********************************************************
|
||||
//********************************************************
|
||||
//********************************************************
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "geometry/Classification.hpp"
|
||||
#include "geometry/Covariance.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool LSQR(const std::vector<std::vector<Eigen::RowVectorXd>>& dataset, Eigen::MatrixXd& weight)
|
||||
{
|
||||
// Precomputation
|
||||
if (dataset.empty()) { return false; }
|
||||
const size_t nbClass = dataset.size(), nbFeatures = dataset[0][0].size();
|
||||
std::vector<size_t> nbSample(nbClass);
|
||||
size_t totalSample = 0;
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
if (dataset[k].empty()) { return false; }
|
||||
nbSample[k] = dataset[k].size();
|
||||
totalSample += nbSample[k];
|
||||
}
|
||||
|
||||
// Compute Class Euclidian mean
|
||||
Eigen::MatrixXd mean = Eigen::MatrixXd::Zero(nbClass, nbFeatures);
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
for (size_t i = 0; i < nbSample[k]; ++i) { mean.row(k) += dataset[k][i]; }
|
||||
mean.row(k) /= double(nbSample[k]);
|
||||
}
|
||||
|
||||
// Compute Class Covariance
|
||||
Eigen::MatrixXd cov = Eigen::MatrixXd::Zero(nbFeatures, nbFeatures);
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
//Fit Data to existing covariance matrix method
|
||||
Eigen::MatrixXd classData(nbFeatures, nbSample[k]);
|
||||
for (size_t i = 0; i < nbSample[k]; ++i) { classData.col(i) = dataset[k][i]; }
|
||||
|
||||
// Standardize Features
|
||||
Eigen::RowVectorXd scale;
|
||||
MatrixStandardScaler(classData, scale);
|
||||
|
||||
//Compute Covariance of this class
|
||||
Eigen::MatrixXd classCov;
|
||||
if (!CovarianceMatrix(classData, classCov, EEstimator::LWF)) { return false; }
|
||||
|
||||
// Rescale
|
||||
for (size_t i = 0; i < nbFeatures; ++i) { for (size_t j = 0; j < nbFeatures; ++j) { classCov(i, j) *= scale[i] * scale[j]; } }
|
||||
|
||||
//Add to cov with good weight
|
||||
cov += (double(nbSample[k]) / double(totalSample)) * classCov;
|
||||
}
|
||||
|
||||
// linear least squares systems solver
|
||||
// Chosen solver with the performance table of this page : https://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html
|
||||
weight = cov.colPivHouseholderQr().solve(mean.transpose()).transpose();
|
||||
//weight = cov.completeOrthogonalDecomposition().solve(mean.transpose()).transpose();
|
||||
//weight = cov.bdcSvd(ComputeThinU | ComputeThinV).solve(mean.transpose()).transpose();
|
||||
|
||||
// Treat binary case as a special case
|
||||
if (nbClass == 2)
|
||||
{
|
||||
const Eigen::MatrixXd tmp = weight.row(1) - weight.row(0); // Need to use a tmp variable otherwise sometimes error
|
||||
weight = tmp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool FgDACompute(const std::vector<std::vector<Eigen::RowVectorXd>>& dataset, Eigen::MatrixXd& weight)
|
||||
{
|
||||
// Compute LSQR Weight
|
||||
Eigen::MatrixXd w;
|
||||
if (!LSQR(dataset, w)) { return false; }
|
||||
const size_t nbClass = w.rows();
|
||||
|
||||
// Transform to FgDA Weight
|
||||
const Eigen::MatrixXd wT = w.transpose();
|
||||
weight = (wT * (w * wT).colPivHouseholderQr().solve(Eigen::MatrixXd::Identity(nbClass, nbClass))) * w;
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool FgDAApply(const Eigen::RowVectorXd& in, Eigen::RowVectorXd& out, const Eigen::MatrixXd& weight)
|
||||
{
|
||||
if (in.cols() != weight.rows()) { return false; }
|
||||
out = in * weight;
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,172 @@
|
||||
# include "geometry/Covariance.hpp"
|
||||
#include <algorithm> // std::min/max
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//***********************************************************
|
||||
//******************** COVARIANCES BASES ********************
|
||||
//***********************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Variance(const Eigen::RowVectorXd& x)
|
||||
{
|
||||
const size_t S = x.cols(); // Number of Samples => S
|
||||
if (S == 0) { return 0; } // If false input
|
||||
|
||||
const double mu = x.mean();
|
||||
return x.cwiseProduct(x).sum() / S - mu * mu;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Covariance(const Eigen::RowVectorXd& x, const Eigen::RowVectorXd& y)
|
||||
{
|
||||
const size_t xS = x.cols(), yS = y.cols(); // Number of Samples => S
|
||||
if (xS == 0 || xS != yS) { return 0; } // If false input
|
||||
return (x.cwiseProduct(y).sum() - x.sum() * y.sum() / xS) / xS;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool ShrunkCovariance(Eigen::MatrixXd& cov, const double shrinkage)
|
||||
{
|
||||
if (!InRange(shrinkage, 0, 1)) { return false; } // Verification
|
||||
const size_t n = cov.rows(); // Number of Features => N
|
||||
|
||||
const double coef = shrinkage * cov.trace() / n; // Diagonal Coefficient
|
||||
cov = (1 - shrinkage) * cov; // Shrinkage
|
||||
for (size_t i = 0; i < n; ++i) { cov(i, i) += coef; } // Add Diagonal Coefficient
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool ShrunkCovariance(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, const double shrinkage)
|
||||
{
|
||||
out = in;
|
||||
return ShrunkCovariance(out, shrinkage);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrix(const Eigen::MatrixXd& in, Eigen::MatrixXd& out, const EEstimator estimator, const EStandardization standard)
|
||||
{
|
||||
if (!IsNotEmpty(in)) { return false; } // Verification
|
||||
Eigen::MatrixXd sample;
|
||||
MatrixStandardization(in, sample, standard); // Standardization
|
||||
switch (estimator) // Switch Method
|
||||
{
|
||||
case EEstimator::COV: return CovarianceMatrixCOV(sample, out);
|
||||
case EEstimator::SCM: return CovarianceMatrixSCM(sample, out);
|
||||
case EEstimator::LWF: return CovarianceMatrixLWF(sample, out);
|
||||
case EEstimator::OAS: return CovarianceMatrixOAS(sample, out);
|
||||
case EEstimator::MCD: return CovarianceMatrixMCD(sample, out);
|
||||
case EEstimator::COR: return CovarianceMatrixCOR(sample, out);
|
||||
default: return CovarianceMatrixIDE(sample, out);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************************************************
|
||||
//***********************************************************
|
||||
//***********************************************************
|
||||
|
||||
//***********************************************************
|
||||
//******************** COVARIANCES TYPES ********************
|
||||
//***********************************************************
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixCOV(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
const size_t n = samples.rows(); // Number of Features => N
|
||||
|
||||
cov.resize(n, n); // Init size of matrix
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
const Eigen::RowVectorXd ri = samples.row(i);
|
||||
cov(i, i) = Variance(ri); // Diagonal Value
|
||||
|
||||
for (size_t j = i + 1; j < n; ++j)
|
||||
{
|
||||
const Eigen::RowVectorXd rj = samples.row(j);
|
||||
cov(i, j) = cov(j, i) = Covariance(ri, rj); // Symetric covariance
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixSCM(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
cov = samples * samples.transpose(); // X*X^T
|
||||
cov /= cov.trace(); // X*X^T / trace(X*X^T)
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixLWF(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
const size_t n = samples.rows(), S = samples.cols(); // Number of Features & Samples => N & S
|
||||
|
||||
CovarianceMatrixCOV(samples, cov); // Initial Covariance Matrix => Cov
|
||||
const double mu = cov.trace() / n;
|
||||
Eigen::MatrixXd mDelta = cov; // mDelta = cov - mu * I_n
|
||||
for (size_t i = 0; i < n; ++i) { mDelta(i, i) -= mu; }
|
||||
const Eigen::MatrixXd x2 = samples.cwiseProduct(samples), // Squared each sample => X^2
|
||||
cov2 = cov.cwiseProduct(cov); // Squared each element of Cov => Cov^2
|
||||
|
||||
const double delta = mDelta.cwiseProduct(mDelta).sum() / n,
|
||||
beta = 1. / double(n * S) * (x2 * x2.transpose() / double(S) - cov2).sum(),
|
||||
shrinkage = std::min(beta, delta) / delta; // Assure shrinkage <= 1
|
||||
|
||||
return ShrunkCovariance(cov, shrinkage); // Shrinkage of the matrix
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixOAS(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
const size_t n = samples.rows(), S = samples.cols(); // Number of Features & Samples => N & S
|
||||
CovarianceMatrixCOV(samples, cov); // Initial Covariance Matrix => Cov
|
||||
|
||||
// Compute Shrinkage : Formula from Chen et al.'s
|
||||
const double mu = cov.trace() / n,
|
||||
mu2 = mu * mu,
|
||||
alpha = cov.cwiseProduct(cov).mean(),
|
||||
num = alpha + mu2,
|
||||
den = (S + 1) * (alpha - mu2 / n),
|
||||
shrinkage = (den == 0) ? 1.0 : std::min(num / den, 1.0);
|
||||
|
||||
return ShrunkCovariance(cov, shrinkage); // Shrinkage of the matrix
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixMCD(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov) { return CovarianceMatrixIDE(samples, cov); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixCOR(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
const size_t n = samples.rows(); // Number of Features => N
|
||||
CovarianceMatrixCOV(samples, cov); // Initial Covariance Matrix => Cov
|
||||
const Eigen::MatrixXd d = cov.diagonal().cwiseSqrt(); // Squared root of diagonal
|
||||
|
||||
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < n; ++j) { cov(i, j) /= d(i) * d(j); } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool CovarianceMatrixIDE(const Eigen::MatrixXd& samples, Eigen::MatrixXd& cov)
|
||||
{
|
||||
cov = Eigen::MatrixXd::Identity(samples.rows(), samples.rows());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
//***********************************************************
|
||||
//***********************************************************
|
||||
//***********************************************************
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "geometry/Distance.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Distance(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, const EMetric metric)
|
||||
{
|
||||
if (!HaveSameSize(a, b)) { return 0; }
|
||||
switch (metric)
|
||||
{
|
||||
case EMetric::Riemann: return DistanceRiemann(a, b);
|
||||
case EMetric::Euclidian: return DistanceEuclidian(a, b);
|
||||
case EMetric::LogEuclidian: return DistanceLogEuclidian(a, b);
|
||||
case EMetric::LogDet: return DistanceLogDet(a, b);
|
||||
case EMetric::Kullback: return DistanceKullbackSym(a, b);
|
||||
case EMetric::Wasserstein: return DistanceWasserstein(a, b);
|
||||
case EMetric::Identity:
|
||||
default: return 1.0;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceRiemann(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b)
|
||||
{
|
||||
const Eigen::GeneralizedSelfAdjointEigenSolver<Eigen::MatrixXd> es(a, b);
|
||||
const Eigen::ArrayXd result = es.eigenvalues();
|
||||
return sqrt(result.log().square().sum());
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b) { return (b - a).norm(); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceLogEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b) { return DistanceEuclidian(a.log(), b.log()); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceLogDet(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b)
|
||||
{
|
||||
return sqrt(log((0.5 * (a + b)).determinant()) - 0.5 * log(a.determinant() * b.determinant()));
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceKullback(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b)
|
||||
{
|
||||
return 0.5 * ((b.inverse() * a).trace() - a.rows() + log(b.determinant() / a.determinant()));
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceKullbackSym(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b) { return DistanceKullback(a, b) + DistanceKullback(b, a); }
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double DistanceWasserstein(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b)
|
||||
{
|
||||
const Eigen::MatrixXd sB = b.sqrt();
|
||||
return sqrt((a + b - 2 * (sB * a * sB).sqrt()).trace());
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "geometry/Featurization.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
#include "geometry/Basics.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
#ifndef M_SQRT2
|
||||
#define M_SQRT2 1.4142135623730950488016887242097
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Featurization(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, const bool tangent, const Eigen::MatrixXd& ref)
|
||||
{
|
||||
if (tangent) { return TangentSpace(in, out, ref); }
|
||||
return SqueezeUpperTriangle(in, out, true);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool UnFeaturization(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, const bool tangent, const Eigen::MatrixXd& ref)
|
||||
{
|
||||
if (tangent) { return UnTangentSpace(in, out, ref); }
|
||||
return UnSqueezeUpperTriangle(in, out, true);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool SqueezeUpperTriangle(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, const bool rowMajor)
|
||||
{
|
||||
if (!IsSquare(in)) { return false; } // Verification
|
||||
const size_t n = in.rows(); // Number of Features => N
|
||||
out.resize(n * (n + 1) / 2); // Resize
|
||||
|
||||
size_t idx = 0; // Row Index => idx
|
||||
// Row Major or Diagonal Method
|
||||
if (rowMajor) { for (size_t i = 0; i < n; ++i) { for (size_t j = i; j < n; ++j) { out[idx++] = in(i, j); } } }
|
||||
else { for (size_t i = 0; i < n; ++i) { for (size_t j = i; j < n; ++j) { out[idx++] = in(j, j - i); } } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool UnSqueezeUpperTriangle(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, const bool rowMajor)
|
||||
{
|
||||
const size_t nR = in.size(), // Size of Row => Nr
|
||||
n = int((sqrt(1 + 8 * nR) - 1) / 2); // Number of Features => N
|
||||
if (n == 0) { return false; } // Verification
|
||||
out.setZero(n, n); // Init
|
||||
|
||||
size_t idx = 0; // Row Index => idx
|
||||
// Row Major or Diagonal Method
|
||||
if (rowMajor) { for (size_t i = 0; i < n; ++i) { for (size_t j = i; j < n; ++j) { out(j, i) = out(i, j) = in[idx++]; } } }
|
||||
else { for (size_t i = 0; i < n; ++i) { for (size_t j = i; j < n; ++j) { out(j - i, j) = out(j, j - i) = in[idx++]; } } }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool TangentSpace(const Eigen::MatrixXd& in, Eigen::RowVectorXd& out, const Eigen::MatrixXd& ref)
|
||||
{
|
||||
if (!IsSquare(in)) { return false; } // Verification
|
||||
const size_t n = in.rows(); // Number of Features => N
|
||||
|
||||
const Eigen::MatrixXd sC = (ref.size() == 0) ? Eigen::MatrixXd::Identity(n, n) : Eigen::MatrixXd(ref.sqrt()),
|
||||
isC = sC.inverse(), // Inverse Square root of ref => isC
|
||||
mJ = (isC * in * isC).log(), // Transformation Matrix => mJ
|
||||
mCoeffs = M_SQRT2 * Eigen::MatrixXd(Eigen::MatrixXd::Ones(n, n).triangularView<Eigen::StrictlyUpper>())
|
||||
+ Eigen::MatrixXd::Identity(n, n);
|
||||
|
||||
Eigen::RowVectorXd vJ, vCoeffs;
|
||||
if (!SqueezeUpperTriangle(mJ, vJ, true)) { return false; } // Get upper triangle of J => vJ
|
||||
if (!SqueezeUpperTriangle(mCoeffs, vCoeffs, true)) { return false; } // ... of Coefs => vCoeffs
|
||||
out = vCoeffs.cwiseProduct(vJ); // element-wise multiplication
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool UnTangentSpace(const Eigen::RowVectorXd& in, Eigen::MatrixXd& out, const Eigen::MatrixXd& ref)
|
||||
{
|
||||
const size_t n = out.rows(); // Number of Features => N
|
||||
if (!UnSqueezeUpperTriangle(in, out)) { return false; }
|
||||
|
||||
const Eigen::MatrixXd sC = (ref.size() == 0) ? Eigen::MatrixXd::Identity(n, n) : Eigen::MatrixXd(ref.sqrt()),
|
||||
coeffs = Eigen::MatrixXd(out.triangularView<Eigen::StrictlyUpper>()) / M_SQRT2;
|
||||
|
||||
out = sC * (Eigen::MatrixXd(out.diagonal().asDiagonal()) + coeffs + coeffs.transpose()).exp() * sC;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "geometry/Geodesic.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Geodesic(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, const EMetric metric, const double alpha)
|
||||
{
|
||||
if (!HaveSameSize(a, b)) { return false; } // Verification same size
|
||||
if (!IsSquare(a)) { return false; } // Verification square matrix
|
||||
if (!InRange(alpha, 0, 1)) { return false; } // Verification alpha in [0;1]
|
||||
switch (metric) // Switch metric
|
||||
{
|
||||
case EMetric::Riemann: return GeodesicRiemann(a, b, g, alpha);
|
||||
case EMetric::Euclidian: return GeodesicEuclidian(a, b, g, alpha);
|
||||
case EMetric::LogEuclidian: return GeodesicLogEuclidian(a, b, g, alpha);
|
||||
case EMetric::Identity:
|
||||
default: return GeodesicIdentity(a, b, g, alpha);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool GeodesicRiemann(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, const double alpha)
|
||||
{
|
||||
const Eigen::MatrixXd sA = a.sqrt(), isA = sA.inverse();
|
||||
g = sA * (isA * b * isA).pow(alpha) * sA;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool GeodesicEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, const double alpha)
|
||||
{
|
||||
g = (1 - alpha) * a + alpha * b;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool GeodesicLogEuclidian(const Eigen::MatrixXd& a, const Eigen::MatrixXd& b, Eigen::MatrixXd& g, const double alpha)
|
||||
{
|
||||
g = ((1 - alpha) * a.log() + alpha * b.log()).exp();
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool GeodesicIdentity(const Eigen::MatrixXd& a, const Eigen::MatrixXd& /*b*/, Eigen::MatrixXd& g, const double /*alpha*/)
|
||||
{
|
||||
g = Eigen::MatrixXd::Identity(a.rows(), a.rows());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,227 @@
|
||||
#include "geometry/Metrics.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Geodesic.hpp"
|
||||
#include "geometry/Distance.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//static const double EPSILON = 0.000000001; // 10^{-9}
|
||||
static const double EPSILON = 0.0001; // 10^{-4}
|
||||
static const size_t ITER_MAX = 50;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Mean(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean, const EMetric metric)
|
||||
{
|
||||
if (covs.empty()) { return false; } // If no matrix in vector
|
||||
if (covs.size() == 1) // If just one matrix in vector
|
||||
{
|
||||
mean = covs[0];
|
||||
return true;
|
||||
}
|
||||
if (!HaveSameSize(covs))
|
||||
{
|
||||
std::cout << "Matrices haven't same size." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Force Square Matrix for non Euclidian and non Identity metric
|
||||
if (!IsSquare(covs[0]) && (metric != EMetric::Euclidian && metric != EMetric::Identity))
|
||||
{
|
||||
std::cout << "Non Square Matrix is invalid with " << toString(metric) << " metric." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (metric) // Switch method
|
||||
{
|
||||
case EMetric::Riemann: return MeanRiemann(covs, mean);
|
||||
case EMetric::Euclidian: return MeanEuclidian(covs, mean);
|
||||
case EMetric::LogEuclidian: return MeanLogEuclidian(covs, mean);
|
||||
case EMetric::LogDet: return MeanLogDet(covs, mean);
|
||||
case EMetric::Kullback: return MeanKullback(covs, mean);
|
||||
case EMetric::ALE: return MeanALE(covs, mean);
|
||||
case EMetric::Harmonic: return MeanHarmonic(covs, mean);
|
||||
case EMetric::Wasserstein: return MeanWasserstein(covs, mean);
|
||||
case EMetric::Identity:
|
||||
default: return MeanIdentity(covs, mean);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool AJDPham(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& ajd, double /*epsilon*/, const int /*maxIter*/)
|
||||
{
|
||||
MeanIdentity(covs, ajd);
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanRiemann(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
size_t i = 0; // Index of Covariance Matrix => i
|
||||
double nu = 1.0, // Coefficient change => nu
|
||||
tau = std::numeric_limits<double>::max(), // Coefficient change criterion => tau
|
||||
crit = std::numeric_limits<double>::max(); // Current change => crit
|
||||
if (!MeanEuclidian(covs, mean)) { return false; } // Initial Mean
|
||||
|
||||
while (i < ITER_MAX && EPSILON < crit && EPSILON < nu) // Stopping criterion
|
||||
{
|
||||
i++; // Iteration Criterion
|
||||
const Eigen::MatrixXd sC = mean.sqrt(), isC = sC.inverse(); // Square root & Inverse Square root of Mean => sC & isC
|
||||
Eigen::MatrixXd mJ = Eigen::MatrixXd::Zero(n, n); // Change => J
|
||||
for (const auto& cov : covs) { mJ += (isC * cov * isC).log(); } // Sum of log(isC*Ci*isC)
|
||||
mJ /= double(k); // Normalization
|
||||
crit = mJ.norm(); // Current change criterion
|
||||
mean = sC * (nu * mJ).exp() * sC; // Update Mean => M = sC * exp(nu*J) * sC
|
||||
|
||||
const double h = nu * crit; // Update Coefficient change
|
||||
if (h < tau)
|
||||
{
|
||||
nu *= 0.95;
|
||||
tau = h;
|
||||
}
|
||||
else { nu *= 0.5; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanEuclidian(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
mean = Eigen::MatrixXd::Zero(n, n); // Initial Mean
|
||||
for (const auto& cov : covs) { mean += cov; } // Sum of Ci
|
||||
mean /= double(k); // Normalization
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanLogEuclidian(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
mean = Eigen::MatrixXd::Zero(n, n); // Initial Mean
|
||||
for (const auto& cov : covs) { mean += cov.log(); } // Sum of log(Ci)
|
||||
mean = (mean / double(k)).exp(); // Normalization
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanLogDet(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
size_t i = 0; // Index of Covariance Matrix => i
|
||||
double crit = std::numeric_limits<double>::max(); // Current change => crit
|
||||
if (!MeanEuclidian(covs, mean)) { return false; } // Initial Mean
|
||||
|
||||
while (i < ITER_MAX && EPSILON < crit) // Stopping criterion
|
||||
{
|
||||
i++; // Iteration Criterion
|
||||
Eigen::MatrixXd mJ = Eigen::MatrixXd::Zero(n, n); // Change => J
|
||||
|
||||
for (const auto& cov : covs) { mJ += (0.5 * (cov + mean)).inverse(); } // Sum of ((Ci+M)/2)^{-1}
|
||||
mJ = (mJ / double(k)).inverse(); // Normalization
|
||||
crit = (mJ - mean).norm(); // Current change criterion
|
||||
mean = mJ; // Update mean
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanKullback(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
Eigen::MatrixXd m1, m2;
|
||||
if (!MeanEuclidian(covs, m1)) { return false; }
|
||||
if (!MeanHarmonic(covs, m2)) { return false; }
|
||||
if (!GeodesicRiemann(m1, m2, mean, 0.5)) { return false; }
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanWasserstein(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
size_t i = 0; // Index of Covariance Matrix => i
|
||||
double crit = std::numeric_limits<double>::max(); // Current change => crit
|
||||
|
||||
if (!MeanEuclidian(covs, mean)) { return false; } // Initial Mean
|
||||
Eigen::MatrixXd sC = mean.sqrt(); // Square root of Mean => sC
|
||||
|
||||
while (i < ITER_MAX && EPSILON < crit) // Stopping criterion
|
||||
{
|
||||
i++; // Iteration Criterion
|
||||
Eigen::MatrixXd mJ = Eigen::MatrixXd::Zero(n, n); // Change => J
|
||||
|
||||
for (const auto& cov : covs) { mJ += (sC * cov * sC).sqrt(); } // Sum of sqrt(sC*Ci*sC)
|
||||
mJ /= double(k); // Normalization
|
||||
|
||||
const Eigen::MatrixXd sJ = mJ.sqrt(); // Square root of change => sJ
|
||||
crit = (sJ - sC).norm(); // Current change criterion
|
||||
sC = sJ; // Update sC
|
||||
}
|
||||
mean = sC * sC; // Un-square root
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanALE(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
size_t i = 0; // Index of Covariance Matrix => i
|
||||
double crit = std::numeric_limits<double>::max(); // Change criterion => crit
|
||||
if (!AJDPham(covs, mean)) { return false; } // Initial Mean
|
||||
Eigen::MatrixXd mJ; // Change
|
||||
|
||||
while (i < ITER_MAX && EPSILON < crit) // Stopping criterion
|
||||
{
|
||||
i++; // Iteration Criterion
|
||||
mJ = Eigen::MatrixXd::Zero(n, n); // Change => J
|
||||
|
||||
for (const auto& cov : covs) { mJ += (mean.transpose() * cov * mean).log(); } // Sum of log(C^T*Ci*C)
|
||||
mJ /= double(k); // Normalization
|
||||
|
||||
Eigen::MatrixXd update = mJ.exp().diagonal().asDiagonal(); // Update Form => U
|
||||
mean = mean * update.sqrt().inverse(); // Update Mean M = M * U^{-1/2}
|
||||
|
||||
crit = DistanceRiemann(Eigen::MatrixXd::Identity(n, n), update);
|
||||
}
|
||||
|
||||
mJ = Eigen::MatrixXd::Zero(n, n); // Last Change => J
|
||||
for (const auto& cov : covs) { mJ += (mean.transpose() * cov * mean).log(); } // Sum of log(C^T*Ci*C)
|
||||
mJ /= double(k); // Normalization
|
||||
|
||||
Eigen::MatrixXd mA = mean.inverse();
|
||||
mean = mA.transpose() * mJ.exp() * mA;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanHarmonic(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
const size_t k = covs.size(), n = covs[0].rows(); // Number of Matrix & Features => K & N
|
||||
mean = Eigen::MatrixXd::Zero(n, n); // Initial Mean
|
||||
for (const auto& cov : covs) { mean += cov.inverse(); } // Sum of Inverse
|
||||
mean = (mean / double(k)).inverse(); // Normalization and inverse
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MeanIdentity(const std::vector<Eigen::MatrixXd>& covs, Eigen::MatrixXd& mean)
|
||||
{
|
||||
mean = Eigen::MatrixXd::Identity(covs[0].rows(), covs[0].cols());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,151 @@
|
||||
#include "geometry/Median.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Featurization.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
double Median(const Eigen::MatrixXd& m)
|
||||
{
|
||||
const std::vector<double> v(m.data(), m.data() + m.rows() * m.cols());
|
||||
return Median(v);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool Median(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter, const EMetric& metric)
|
||||
{
|
||||
if (matrices.empty()) { return false; } // If no matrix in vector
|
||||
if (matrices.size() == 1) // If just one matrix in vector
|
||||
{
|
||||
median = matrices[0];
|
||||
return true;
|
||||
}
|
||||
if (!HaveSameSize(matrices)) // If different sizes
|
||||
{
|
||||
std::cout << "Matrices have different sizes." << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!IsSquare(matrices[0]) && metric == EMetric::Riemann) // If non square for Riemann metric
|
||||
{
|
||||
std::cout << "Non Square Matrix is invalid with " << toString(metric) << " metric." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (metric)
|
||||
{
|
||||
case EMetric::Riemann: return MedianRiemann(matrices, median, epsilon, maxIter);
|
||||
case EMetric::Euclidian: return MedianEuclidian(matrices, median, epsilon, maxIter);
|
||||
case EMetric::Identity: return MedianIdentity(matrices, median);
|
||||
case EMetric::LogEuclidian:
|
||||
case EMetric::LogDet:
|
||||
case EMetric::Kullback:
|
||||
case EMetric::ALE:
|
||||
case EMetric::Harmonic:
|
||||
case EMetric::Wasserstein:
|
||||
std::cout << toString(metric) << " metric not implemented." << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianEuclidian(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter)
|
||||
{
|
||||
if (matrices.empty() || matrices[0].size() == 0) { return false; }
|
||||
const size_t n = matrices.size(); // Number of sample
|
||||
|
||||
// Initial Median is the median of each channel in all matrix of dataset
|
||||
median = matrices[0]; // to copy size
|
||||
for (size_t i = 0; i < size_t(median.size()); ++i)
|
||||
{
|
||||
std::vector<double> tmp;
|
||||
tmp.reserve(n); // Reserve to optimize (a little) the pushback memory access.
|
||||
for (const auto& cov : matrices) { tmp.push_back(cov.data()[i]); } // Stack value number i of all matrix
|
||||
median.data()[i] = Median(tmp);
|
||||
}
|
||||
|
||||
size_t iter = 0; // number of iteration
|
||||
double gain = epsilon; // Gain since last compute
|
||||
while (iter < maxIter && gain >= epsilon)
|
||||
{
|
||||
Eigen::MatrixXd prev = median; // Keep old median
|
||||
median.setZero(); // Reset median
|
||||
double sumCoefs = 0; // Sum of Coefficient
|
||||
for (const auto& cov : matrices)
|
||||
{
|
||||
//Eigen::MatrixXd difference = cov - prev;
|
||||
//double coef = sqrt(difference.cwiseProduct(difference).sum());
|
||||
if (cov.isApprox(prev)) { continue; } // In this case, Median is exactly this current matrix so we don't consider this matrix
|
||||
double coef = (cov - prev).norm();
|
||||
// Personnal hack and security
|
||||
coef = 1.0 / coef;
|
||||
sumCoefs += coef; // Sum for normalization
|
||||
median += coef * cov; // Add to the new median
|
||||
}
|
||||
if (sumCoefs > 0.0) { median /= sumCoefs; } // Normalize
|
||||
|
||||
gain = (median - prev).norm() / median.norm(); // It's the Frobenius norm
|
||||
iter++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianRiemann(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median, const double epsilon, const size_t maxIter)
|
||||
{
|
||||
if (matrices.empty() || !IsSquare(matrices[0])) { return false; }
|
||||
const size_t n = matrices.size(); // Number of sample
|
||||
const size_t nf = matrices[0].rows() * (matrices[0].rows() + 1) / 2; // Number of Features in tangent space
|
||||
size_t iter = 0; // number of iteration
|
||||
if (!MeanEuclidian(matrices, median)) { return false; } // Initialize Median
|
||||
|
||||
double gain = epsilon; // Gain since last compute
|
||||
std::vector<Eigen::MatrixXd> mats;
|
||||
mats.reserve(n);
|
||||
for (const auto& m : matrices) { mats.push_back(m); }
|
||||
while (iter < maxIter)
|
||||
{
|
||||
// Compute Tangent space of all matrices & sum of euclidian distance of each transposed matrix
|
||||
std::vector<Eigen::RowVectorXd> ts(n);
|
||||
double sum = 0.0;
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
if (!TangentSpace(mats[i], ts[i], median)) { return false; }
|
||||
sum += sqrt(ts[i].cwiseAbs2().sum());
|
||||
}
|
||||
if (std::abs((sum - gain) / gain) < epsilon) { break; } // std::abs call fabs to keep type
|
||||
|
||||
// Arithmetic median in tangent space
|
||||
std::vector<std::vector<double>> transposeTs(nf, std::vector<double>(n));
|
||||
Eigen::RowVectorXd featureMedian(nf);
|
||||
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < nf; ++j) { transposeTs[j][i] = ts[i][j]; } }
|
||||
for (size_t j = 0; j < nf; ++j) { featureMedian[j] = Median(transposeTs[j]); }
|
||||
|
||||
// back to the manifold
|
||||
Eigen::MatrixXd tmp;
|
||||
if (!UnTangentSpace(featureMedian, tmp, median)) { return false; }
|
||||
gain = sum; // Update gain
|
||||
median = tmp; // Update Median
|
||||
iter++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool MedianIdentity(const std::vector<Eigen::MatrixXd>& matrices, Eigen::MatrixXd& median)
|
||||
{
|
||||
median = Eigen::MatrixXd::Identity(matrices[0].rows(), matrices[0].cols());
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,243 @@
|
||||
#include "geometry/Misc.hpp"
|
||||
#include "geometry/Featurization.hpp"
|
||||
|
||||
#include <boost/math/special_functions/gamma.hpp>
|
||||
#include <numeric> // std::iota
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
/// <summary> Get the sign of the specified value. </summary>
|
||||
/// <param name="x"> The value. </param>
|
||||
/// <returns> <c>1</c> if <c>x > 0</c>, <c>0</c> if <c>x == 0</c>, <c>-1</c> if <c>x < 0</c>. </returns>
|
||||
template <typename T>
|
||||
int sgn(T x) { return (T(0) < x) - (x < T(0)); }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
/// <summary> Structure used for iota function for range of double. </summary>
|
||||
struct SDoubleIota
|
||||
{
|
||||
explicit SDoubleIota(const double init = 0.0, const double inc = 1.0) : v(init), inc(inc) {}
|
||||
|
||||
operator double() const { return v; } // don't add explicit qualifier for iota functions (were template cast is used)
|
||||
SDoubleIota& operator++()
|
||||
{
|
||||
v += inc;
|
||||
return *this;
|
||||
}
|
||||
double v;
|
||||
double inc;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
/// <summary> Structure used for iota function for range of round index. </summary>
|
||||
struct SRoundIndex
|
||||
{
|
||||
explicit SRoundIndex(const double init = 0.0, const double inc = 1.0) : v(init), inc(inc) {}
|
||||
|
||||
operator size_t() const { return size_t(std::round(v)); } // don't add explicit qualifier for iota functions (were template cast is used)
|
||||
SRoundIndex& operator++()
|
||||
{
|
||||
v += inc;
|
||||
return *this;
|
||||
}
|
||||
double v;
|
||||
double inc;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::vector<double> doubleRange(const double begin, const double end, const double step, const bool closed)
|
||||
{
|
||||
std::vector<double> res;
|
||||
if (end < begin) { return res; }
|
||||
const double size = (end - begin) / step;
|
||||
// check if the end is inclued in range for size, we ceil for no modulo values and add the last value to the range
|
||||
res.resize(size_t(std::ceil((closed && std::trunc(size) == size) ? size + 1 : size)));
|
||||
std::iota(res.begin(), res.end(), SDoubleIota(begin, step));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RoundIndexRange(const double begin, const double end, const double step, const bool closed, const bool unique)
|
||||
{
|
||||
std::vector<size_t> res;
|
||||
if (end < begin) { return res; }
|
||||
const double size = (end - begin) / step;
|
||||
// check if the end is inclued in range for size, we ceil for no modulo values and add the last value to the range
|
||||
res.resize(size_t(std::ceil((closed && std::trunc(size) == size) ? size + 1 : size)));
|
||||
std::iota(res.begin(), res.end(), SRoundIndex(begin, step));
|
||||
|
||||
if (unique)
|
||||
{
|
||||
const auto last = std::unique(res.begin(), res.end()); // Remove duplicate values (but after last, we have undefined value)
|
||||
res.erase(last, res.end()); // Resize Vector (we erase after last)
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> BinHist(const std::vector<double>& dataset, const size_t n)
|
||||
{
|
||||
std::vector<size_t> res(n, 0);
|
||||
const double max = *std::max_element(dataset.begin(), dataset.end());
|
||||
if (max == 0) { return res; } // if max is 0, coef can't be compute
|
||||
const double coef = n / max;
|
||||
for (const auto& data : dataset)
|
||||
{
|
||||
const size_t bin = size_t(std::floor(data * coef));
|
||||
if (bin < n) { res[bin]++; }
|
||||
else if (bin == n) { res[n - 1]++; } // if this data is equal to max
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
bool FitDistribution(const std::vector<double>& values, double& mu, double& sigma, const std::vector<double>& betas, const double minQuant,
|
||||
const double maxQuant, const double minClean, const double maxDropout, const double stepBound, const double stepScale)
|
||||
{
|
||||
if (values.empty() || betas.empty() || minQuant < 0 || minQuant > 1 || maxQuant < 0 || maxQuant > 1 || minClean < 0 || maxDropout < 0
|
||||
|| stepBound < 0.0001 || stepBound > 0.1 || stepScale < 0.0001 || stepScale > 0.1) { return false; }
|
||||
|
||||
//========== Scales ==========
|
||||
const size_t nBeta = betas.size();
|
||||
// Scales is a vector for each beta as :
|
||||
// scale = beta/(2*gamma(1/beta)) with gamma the function as gamma(n) = (n-1)! for all integer greater than 0
|
||||
std::vector<double> scales;
|
||||
scales.reserve(nBeta);
|
||||
std::transform(betas.begin(), betas.end(), std::back_inserter(scales), [](const double beta) -> double { return beta / (2 * tgamma(1 / beta)); });
|
||||
|
||||
//========== zBounds ==========
|
||||
// zBounds is a vector of lower and upper bounds for each beta as : sign(quants-1/2) * gammaincinv(sign(quants-1/2) * (2*quants-1), 1/beta)^(1/beta);
|
||||
// with gammaincinv the Inverse incomplete gamma function, here quants are the quantiles limit (by default [0.022 0.6])
|
||||
std::vector<std::vector<double>> zBounds(nBeta);
|
||||
const int signMin = sgn(minQuant - 0.5), signMax = sgn(maxQuant - 0.5);
|
||||
const double coefMin = signMin * (2 * minQuant - 1), coefMax = signMax * (2 * maxQuant - 1);
|
||||
|
||||
for (size_t i = 0; i < nBeta; ++i)
|
||||
{
|
||||
if (betas[i] == 0) { zBounds[i] = { 0, 0 }; }
|
||||
else
|
||||
{
|
||||
const double beta = 1 / betas[i];
|
||||
zBounds[i] = {
|
||||
signMin * pow(boost::math::gamma_p_inv(beta, coefMin), beta),
|
||||
signMax * pow(boost::math::gamma_p_inv(beta, coefMax), beta)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//========== Sort Values ==========
|
||||
// We sort values to access quantiles directly
|
||||
const size_t n = values.size();
|
||||
std::vector<double> newValues = values;
|
||||
std::sort(newValues.begin(), newValues.end());
|
||||
|
||||
//========== Compute Index range ==========
|
||||
// Width are the limit if all data is clean or artifacted. It's usefull for the for loop limit and step for each width possible
|
||||
// Bounds are the range of begining value used to compute mu and sigma. It's usefull for the for loop limit and step for first index of value to take
|
||||
// We create Vector for widths and bounds to precompute all round and avoid duplicate indexes in widths or bounds
|
||||
std::vector<size_t> widths = RoundIndexRange(n * (maxQuant - minQuant) * minClean, n * (maxQuant - minQuant), n * stepScale, true, false);
|
||||
std::reverse(widths.begin(), widths.end());
|
||||
const std::vector<size_t> bounds = RoundIndexRange(n * minQuant, n * (minQuant + maxDropout), n * stepBound, true, false);
|
||||
const size_t maxWidth = std::max(widths.front(), widths.back()); // to prevent if widths is in descending or ascending order
|
||||
const size_t nBound = bounds.size();
|
||||
|
||||
//========== Compute Grid (with index range) ==========
|
||||
// Create the Biggest table of data with width in column and bound in row
|
||||
std::vector<std::vector<double>> grid(nBound);
|
||||
std::vector<double> firsts(nBound);
|
||||
for (size_t i = 0; i < nBound; ++i)
|
||||
{
|
||||
grid[i].reserve(maxWidth);
|
||||
const auto first = newValues.begin() + bounds[i];
|
||||
std::copy_n(first, maxWidth, std::back_inserter(grid[i]));
|
||||
firsts[i] = grid[i][0];
|
||||
for (auto& e : grid[i]) { e -= firsts[i]; } // Substract first value on all element
|
||||
}
|
||||
|
||||
//========== Width Loop ==========
|
||||
double bestKl = std::numeric_limits<double>::max();
|
||||
size_t bestBeta = 0, bestId = 0, bestWidth = 0;
|
||||
// for each interval width...
|
||||
for (const auto& w : widths)
|
||||
{
|
||||
const size_t nbins = size_t(std::round(3 * log2(1 + (double(w) / 2))));
|
||||
|
||||
//========== Compute Histogramm ==========
|
||||
std::vector<std::vector<double>> hist(nBound);
|
||||
for (size_t i = 0; i < nBound; ++i)
|
||||
{
|
||||
hist[i].reserve(nbins);
|
||||
std::vector<size_t> tmp = BinHist(std::vector<double>(grid[i].begin(), grid[i].begin() + w), nbins);
|
||||
std::transform(tmp.begin(), tmp.end(), std::back_inserter(hist[i]), [](const size_t e) -> double { return log(e + 0.01); });
|
||||
}
|
||||
|
||||
//========== Beta Loop ==========
|
||||
for (size_t b = 0; b < nBeta; ++b)
|
||||
{
|
||||
//========== Compute Probability ==========
|
||||
std::vector<double> prob(nbins);
|
||||
double sumprob = 0.0;
|
||||
for (size_t i = 0; i < nbins; ++i)
|
||||
{
|
||||
prob[i] = std::exp(-std::pow(std::abs(zBounds[b][0] + (((i + 0.5) / nbins) * (zBounds[b][1] - zBounds[b][0]))), betas[b])) * scales[b];
|
||||
sumprob += prob[i];
|
||||
}
|
||||
if (sumprob != 0) { for (auto& p : prob) { p /= sumprob; } }
|
||||
|
||||
//========== Compute the Kullback-Leibler divergences ==========
|
||||
//kl = sum(prob * (log(prob) - hist)) + log(w));
|
||||
std::vector<double> kl(nBound, log(w));
|
||||
for (size_t i = 0; i < nBound; ++i) { for (size_t j = 0; j < nbins; ++j) { kl[i] += prob[j] * (log(prob[j]) - hist[i][j]); } }
|
||||
|
||||
// Update Parameters
|
||||
auto minIt = std::min_element(kl.begin(), kl.end());
|
||||
if (*minIt < bestKl)
|
||||
{
|
||||
bestKl = *minIt;
|
||||
bestBeta = b;
|
||||
bestId = minIt - kl.begin();
|
||||
bestWidth = w - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double alpha = grid[bestId][bestWidth] / (zBounds[bestBeta][1] - zBounds[bestBeta][0]);
|
||||
double beta = betas[bestBeta];
|
||||
|
||||
mu = firsts[bestId] - zBounds[bestBeta][0] * alpha;
|
||||
sigma = sqrt(alpha * alpha * std::tgamma(3 / beta) / std::tgamma(1 / beta));
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
void sortedEigenVector(const Eigen::MatrixXd& matrix, Eigen::MatrixXd& vectors, std::vector<double>& values, const EMetric /*metric*/)
|
||||
{
|
||||
// Compute Eigen Vector/Values
|
||||
const Eigen::EigenSolver<Eigen::MatrixXd> es(matrix);
|
||||
const Eigen::MatrixXd tmpVec = es.eigenvectors().real(); // It's complex by default but all imaginary part are 0
|
||||
const Eigen::MatrixXd tmpVal = es.eigenvalues().real(); // It's complex by default but all imaginary part are 0
|
||||
values = std::vector<double>(tmpVal.data(), tmpVal.data() + tmpVal.size());
|
||||
|
||||
// Get order of eigen values.
|
||||
std::vector<size_t> idx(values.size());
|
||||
std::iota(idx.begin(), idx.end(), 0);
|
||||
std::stable_sort(idx.begin(), idx.end(), [&values](const size_t i1, const size_t i2) { return values[i1] < values[i2]; });
|
||||
// Sort Eigen Values
|
||||
std::stable_sort(values.begin(), values.end());
|
||||
|
||||
// Sort Eigen Vector
|
||||
vectors = tmpVec; // copy matrix to set size easily
|
||||
for (size_t i = 0; i < size_t(tmpVec.cols()); ++i) { vectors.col(i) = tmpVec.col(idx[i]); }
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,278 @@
|
||||
#include "geometry/artifacts/CASR.hpp"
|
||||
|
||||
#include "geometry/Misc.hpp"
|
||||
#include "geometry/Median.hpp"
|
||||
#include "geometry/Covariance.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/classifier/IMatrixClassifier.hpp"
|
||||
|
||||
#include <boost/math/special_functions/detail/igamma_inverse.hpp>
|
||||
#include <unsupported/Eigen/MatrixFunctions>
|
||||
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CASR::train(const std::vector<Eigen::MatrixXd>& dataset, const double rejectionLimit)
|
||||
{
|
||||
if (dataset.empty() || dataset[0].size() == 0) { return false; }
|
||||
const size_t n = dataset.size(); // Number of samples
|
||||
m_nChannel = dataset[0].rows(); // Number of channels
|
||||
|
||||
//========== Compute the covariance matrix ==========
|
||||
std::vector<Eigen::MatrixXd> covs(n);
|
||||
//for (size_t i = 0; i < n; ++i) { if (!CovarianceMatrixLWF(dataset[i], covs[i])) { return false; } } // We assume data is centered
|
||||
for (size_t i = 0; i < n; ++i) { if (!CovarianceMatrix(dataset[i], covs[i], EEstimator::LWF, EStandardization::Center)) { return false; } }
|
||||
|
||||
//========== Compute Square Root of Median ==========
|
||||
if (!Median(covs, m_median)) { return false; } // Geometric median independant of metric
|
||||
m_median = m_median.sqrt();
|
||||
|
||||
//========== Compute Eigen vectors ==========
|
||||
Eigen::MatrixXd eigVector;
|
||||
std::vector<double> eigValues;
|
||||
sortedEigenVector(m_median, eigVector, eigValues, m_metric); //Actually only Euclidian metric is implemented
|
||||
|
||||
//========== Compute the ponderate dataset ==========
|
||||
std::vector<Eigen::MatrixXd> newDataset;
|
||||
newDataset.reserve(n);
|
||||
for (const auto& m : dataset) { newDataset.push_back((m.transpose() * eigVector)); } // Multiply by eigen vector (we transpose to have channels in column
|
||||
for (auto& m : newDataset) { m = m.cwiseProduct(m); } // Square new signal
|
||||
|
||||
//========== Compute the "fit" distribution ==========
|
||||
// Compute the RMS of each channel for each sample
|
||||
std::vector<std::vector<double>> rms(m_nChannel, std::vector<double>(n));
|
||||
for (size_t i = 0; i < n; ++i) { for (size_t j = 0; j < m_nChannel; ++j) { rms[j][i] = sqrt(newDataset[i].col(j).mean()); } }
|
||||
|
||||
// Compute the "fit" distribution
|
||||
std::vector<double> mu(m_nChannel, 0.0), sigma(m_nChannel, 0.0);
|
||||
for (size_t i = 0; i < m_nChannel; ++i) { FitDistribution(rms[i], mu[i], sigma[i]); }
|
||||
|
||||
// Compute the threshold Matrix
|
||||
m_threshold = Eigen::MatrixXd::Zero(m_nChannel, m_nChannel);
|
||||
for (size_t i = 0; i < m_nChannel; ++i) { m_threshold(i, i) = mu[i] + rejectionLimit * sigma[i]; }
|
||||
m_threshold *= eigVector.transpose();
|
||||
|
||||
// Initialize Reconstruction matrix and trivial
|
||||
m_r = Eigen::MatrixXd::Identity(m_nChannel, m_nChannel);
|
||||
m_trivial = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CASR::process(const Eigen::MatrixXd& in, Eigen::MatrixXd& out)
|
||||
{
|
||||
// Check if input data is compatible with training data and if we don't limit so much the reconstruction
|
||||
out = in;
|
||||
if (size_t(out.rows()) != m_nChannel) { return false; }
|
||||
const size_t begin = size_t((1.0 - m_maxChannel) * double(m_nChannel)); // We define the number of channels to non reconstruct
|
||||
if (begin == m_nChannel) { return true; }
|
||||
if (m_r.size() == 0) { m_r = Eigen::MatrixXd::Identity(m_nChannel, m_nChannel); }
|
||||
|
||||
// Compute Covariance matrix
|
||||
Eigen::MatrixXd cov;
|
||||
if (!CovarianceMatrix(in, cov, EEstimator::LWF, EStandardization::Center)) { return false; }
|
||||
if (m_cov.size() == 0) { m_cov = cov; } // if first time
|
||||
else { if (!Mean({ m_cov, cov }, m_cov, m_metric)) { return false; } } // else mean of the both
|
||||
|
||||
// Compute Eigen vector & values
|
||||
Eigen::MatrixXd eigVector;
|
||||
std::vector<double> eigValues;
|
||||
sortedEigenVector(m_cov, eigVector, eigValues, m_metric);
|
||||
|
||||
// Check if eigen values is over threshold computed during train (ponderated by eigen vector)
|
||||
Eigen::MatrixXd threshold = (m_threshold * eigVector).cwiseAbs2();
|
||||
bool trivial = true;
|
||||
std::vector<bool> keep(m_nChannel, true);
|
||||
for (size_t i = begin; i < m_nChannel; ++i)
|
||||
{
|
||||
if (eigValues[i] >= threshold.col(i).sum())
|
||||
{
|
||||
keep[i] = false;
|
||||
trivial = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if All channels are clean
|
||||
if (trivial) { m_r = Eigen::MatrixXd::Identity(m_nChannel, m_nChannel); }
|
||||
else // if not...
|
||||
{
|
||||
// Compute the reconstruction matrix with bad channels
|
||||
Eigen::MatrixXd tmp = eigVector.transpose() * m_median;
|
||||
for (size_t i = begin; i < m_nChannel; ++i) { if (!keep[i]) { tmp.row(i).setZero(); } }
|
||||
const Eigen::MatrixXd newR = m_median * tmp.completeOrthogonalDecomposition().pseudoInverse() * eigVector.transpose();
|
||||
|
||||
if (!m_trivial)
|
||||
{
|
||||
// Compute blend values for the samples
|
||||
const size_t nSample = in.cols();
|
||||
std::vector<double> blend(nSample);
|
||||
std::iota(blend.begin(), blend.end(), 1); // Range 1 to nSample (inclued)
|
||||
for (auto& b : blend) { b = (1 - cos(M_PI * (b / double(nSample)))) / 2.0; }
|
||||
|
||||
// Apply reconstruction ponderate by the blend (we considere the old reconstruction matrix for the second part)
|
||||
Eigen::MatrixXd t1 = newR * in;
|
||||
Eigen::MatrixXd t2 = m_r * in;
|
||||
for (size_t i = 0; i < nSample; ++i) { out.col(i) = (blend[i] * t1.col(i)) + ((1 - blend[i]) * t2.col(i)); }
|
||||
}
|
||||
m_r = newR; // Update the reconstruction matrix
|
||||
}
|
||||
m_trivial = trivial;
|
||||
return true;
|
||||
}
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
bool CASR::setMatrices(const Eigen::MatrixXd& median, const Eigen::MatrixXd& threshold, const Eigen::MatrixXd& reconstruct,
|
||||
const Eigen::MatrixXd& covariance)
|
||||
{
|
||||
if (!IsSquare(median) || !HaveSameSize(median, threshold)
|
||||
|| (reconstruct.size() != 0 && !HaveSameSize(median, reconstruct))
|
||||
|| (covariance.size() != 0 && !HaveSameSize(median, covariance)))
|
||||
{
|
||||
std::cout << "All matrices must be square with same size (or empty for reconstruct and covariance matrix" << std::endl;
|
||||
return false;
|
||||
}
|
||||
m_nChannel = median.rows();
|
||||
m_median = median;
|
||||
m_threshold = threshold;
|
||||
m_r = reconstruct.size() != 0 ? reconstruct : Eigen::MatrixXd::Identity(m_nChannel, m_nChannel);
|
||||
m_cov = covariance;
|
||||
m_trivial = true;
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CASR::saveXML(const std::string& filename) const
|
||||
{
|
||||
tinyxml2::XMLDocument doc;
|
||||
// Create Root
|
||||
tinyxml2::XMLNode* root = doc.NewElement("ASR"); // Create root node
|
||||
doc.InsertFirstChild(root); // Add root to XML
|
||||
|
||||
tinyxml2::XMLElement* data = doc.NewElement("ASR-data"); // Create data node
|
||||
data->SetAttribute("metric", toString(m_metric).c_str()); // Set attribute metric
|
||||
data->SetAttribute("nChannel", int(m_nChannel)); // Set attribute nCHannel
|
||||
data->SetAttribute("maxChannel", int(m_maxChannel)); // Set attribute nCHannel
|
||||
data->SetAttribute("trivial", m_trivial); // Set attribute nCHannel
|
||||
|
||||
tinyxml2::XMLElement* median = doc.NewElement("Median"); // Create Median node
|
||||
if (!IMatrixClassifier::saveMatrix(median, m_median)) { return false; } // Save Median Matrix
|
||||
data->InsertEndChild(median); // Add Median node to data node
|
||||
|
||||
tinyxml2::XMLElement* threshold = doc.NewElement("Threshold"); // Create Median node
|
||||
if (!IMatrixClassifier::saveMatrix(threshold, m_threshold)) { return false; } // Save Median Matrix
|
||||
data->InsertEndChild(threshold); // Add Median node to data node
|
||||
|
||||
tinyxml2::XMLElement* r = doc.NewElement("R"); // Create Median node
|
||||
if (!IMatrixClassifier::saveMatrix(r, m_r)) { return false; } // Save Median Matrix
|
||||
data->InsertEndChild(r); // Add Median node to data node
|
||||
|
||||
tinyxml2::XMLElement* cov = doc.NewElement("Cov"); // Create Median node
|
||||
if (!IMatrixClassifier::saveMatrix(cov, m_cov)) { return false; } // Save Median Matrix
|
||||
data->InsertEndChild(cov); // Add Median node to data node
|
||||
|
||||
root->InsertEndChild(data); // Add data to root
|
||||
return doc.SaveFile(filename.c_str()) == 0; // save XML (if != 0 it means error)
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CASR::loadXML(const std::string& filename)
|
||||
{
|
||||
// Load File
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
if (xmlDoc.LoadFile(filename.c_str()) != 0) { return false; } // Check File Exist and Loading
|
||||
|
||||
// Load Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.FirstChild(); // Get Root Node
|
||||
if (root == nullptr) { return false; } // Check Root Node Exist
|
||||
|
||||
// Load Data
|
||||
tinyxml2::XMLElement* data = root->FirstChildElement("ASR-data"); // Get Data Node
|
||||
if (data == nullptr) { return false; } // Check Root Node Exist
|
||||
m_metric = StringToMetric(std::string(data->Attribute("metric")));
|
||||
m_nChannel = data->IntAttribute("nChannel");
|
||||
m_maxChannel = data->IntAttribute("maxChannel");
|
||||
m_trivial = data->BoolAttribute("trivial");
|
||||
|
||||
tinyxml2::XMLElement* element = data->FirstChildElement("Median"); // Get Median Node
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
if (!IMatrixClassifier::loadMatrix(element, m_median)) { return false; } // Load Median Matrix
|
||||
|
||||
element = data->FirstChildElement("Threshold"); // Get Threshold Node
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
if (!IMatrixClassifier::loadMatrix(element, m_threshold)) { return false; } // Load Threshold Matrix
|
||||
|
||||
element = data->FirstChildElement("R"); // Get R Node
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
if (!IMatrixClassifier::loadMatrix(element, m_r)) { return false; } // Load R Matrix
|
||||
|
||||
element = data->FirstChildElement("Cov"); // Get Cov Node
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
if (!IMatrixClassifier::loadMatrix(element, m_cov)) { return false; } // Load Cov Matrix
|
||||
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CASR::isEqual(const CASR& obj, const double precision) const
|
||||
{
|
||||
return m_metric == obj.m_metric && m_nChannel == obj.m_nChannel
|
||||
&& abs(m_maxChannel - obj.m_maxChannel) < precision && m_trivial == obj.m_trivial
|
||||
&& AreEquals(m_median, obj.m_median, precision) && AreEquals(m_threshold, obj.m_threshold, precision)
|
||||
&& AreEquals(m_r, obj.m_r, precision) && AreEquals(m_cov, obj.m_cov, precision);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CASR::copy(const CASR& obj)
|
||||
{
|
||||
m_metric = obj.m_metric;
|
||||
m_nChannel = obj.m_nChannel;
|
||||
m_maxChannel = obj.m_maxChannel;
|
||||
m_trivial = obj.m_trivial;
|
||||
m_median = obj.m_median;
|
||||
m_threshold = obj.m_threshold;
|
||||
m_r = obj.m_r;
|
||||
m_cov = obj.m_cov;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CASR::print() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Metric : " << toString(m_metric) << std::endl;
|
||||
if (m_nChannel == 0) { ss << "Training not done" << std::endl; }
|
||||
else
|
||||
{
|
||||
ss << "Training done." << std::endl;
|
||||
ss << size_t(m_maxChannel * double(m_nChannel)) << "/" << m_nChannel << " channels can be reconstruted." << std::endl;
|
||||
ss << "Median matrix is : " << std::endl << m_median << std::endl;
|
||||
ss << "Threshold matrix is : " << std::endl << m_threshold << std::endl;
|
||||
if (m_cov.size() == 0) { ss << "No process launched yet." << std::endl; }
|
||||
else
|
||||
{
|
||||
ss << "Last sample " << (m_trivial ? "was" : "wasn't") << " trivial." << std::endl;
|
||||
ss << "Last Reconstruction Matrix : " << std::endl << m_r << std::endl;
|
||||
ss << "Last Covariance Matrix : " << std::endl << m_cov << std::endl;
|
||||
}
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "geometry/classifier/CBias.hpp"
|
||||
#include "geometry/classifier/IMatrixClassifier.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Geodesic.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::computeBias(const std::vector<std::vector<Eigen::MatrixXd>>& dataset, const EMetric metric) { return computeBias(Vector2DTo1D(dataset), metric); }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::computeBias(const std::vector<Eigen::MatrixXd>& dataset, const EMetric metric)
|
||||
{
|
||||
if (!Mean(dataset, m_bias, metric)) { return false; } // Compute Bias reference
|
||||
m_biasIS = m_bias.sqrt().inverse(); // Inverse Square root of Bias matrix => isR
|
||||
m_n = 0;
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::applyBias(const std::vector<std::vector<Eigen::MatrixXd>>& in, std::vector<std::vector<Eigen::MatrixXd>>& out)
|
||||
{
|
||||
const size_t n = in.size();
|
||||
out.resize(n);
|
||||
for (size_t i = 0; i < n; ++i) { applyBias(in[i], out[i]); }
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::applyBias(const std::vector<Eigen::MatrixXd>& in, std::vector<Eigen::MatrixXd>& out)
|
||||
{
|
||||
const size_t n = in.size();
|
||||
out.resize(n);
|
||||
for (size_t i = 0; i < n; ++i) { applyBias(in[i], out[i]); }
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::applyBias(const Eigen::MatrixXd& in, Eigen::MatrixXd& out) { out = m_biasIS * in * m_biasIS.transpose(); }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::updateBias(const Eigen::MatrixXd& sample, const EMetric metric)
|
||||
{
|
||||
m_n++; // Update number of classify
|
||||
if (m_n == 1) { m_bias = sample; } // At the first pass we reinitialize the Bias
|
||||
else { Geodesic(m_bias, sample, m_bias, metric, 1.0 / m_n); }
|
||||
m_biasIS = m_bias.sqrt().inverse(); // Inverse Square root of Bias matrix => isR
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::setBias(const Eigen::MatrixXd& bias)
|
||||
{
|
||||
m_bias = bias;
|
||||
m_biasIS = m_bias.sqrt().inverse();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::saveXML(const std::string& filename) const
|
||||
{
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
// Create Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.NewElement("Bias"); // Create root node
|
||||
xmlDoc.InsertFirstChild(root); // Add root to XML
|
||||
|
||||
tinyxml2::XMLElement* data = xmlDoc.NewElement("Bias-data"); // Create data node
|
||||
if (!saveAdditional(xmlDoc, data)) { return false; } // Save Optionnal Informations
|
||||
|
||||
root->InsertEndChild(data); // Add data to root
|
||||
return xmlDoc.SaveFile(filename.c_str()) == 0; // save XML (if != 0 it means error)
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::loadXML(const std::string& filename)
|
||||
{
|
||||
// Load File
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
if (xmlDoc.LoadFile(filename.c_str()) != 0) { return false; } // Check File Exist and Loading
|
||||
|
||||
// Load Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.FirstChild(); // Get Root Node
|
||||
if (root == nullptr) { return false; } // Check Root Node Exist
|
||||
|
||||
// Load Data
|
||||
tinyxml2::XMLElement* data = root->FirstChildElement("Bias-data"); // Get Data Node
|
||||
if (!loadAdditional(data)) { return false; } // Load Optionnal Informations
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
tinyxml2::XMLElement* bias = doc.NewElement("Bias"); // Create Bias node
|
||||
bias->SetAttribute("n", int(m_n)); // Set attribute class number of trials
|
||||
if (!IMatrixClassifier::saveMatrix(bias, m_bias)) { return false; } // Save class
|
||||
data->InsertEndChild(bias); // Add class node to data node
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::loadAdditional(tinyxml2::XMLElement* data)
|
||||
{
|
||||
tinyxml2::XMLElement* bias = data->FirstChildElement("Bias"); // Get LDA Weight Node
|
||||
m_n = bias->IntAttribute("n"); // Get the number of Trials for this class
|
||||
if (!IMatrixClassifier::loadMatrix(bias, m_bias)) { return false; } // Load Reference Matrix
|
||||
m_biasIS = m_bias.sqrt().inverse();
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CBias::isEqual(const CBias& obj, const double precision) const { return AreEquals(m_bias, obj.m_bias, precision) && m_n == obj.m_n; }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CBias::copy(const CBias& obj)
|
||||
{
|
||||
m_bias = obj.m_bias;
|
||||
m_biasIS = obj.m_biasIS;
|
||||
m_n = obj.m_n;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CBias::print() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Number of Classification : " << m_n << std::endl;
|
||||
ss << "Bias Matrix : ";
|
||||
if (m_bias.size() != 0) { ss << std::endl << m_bias.format(MATRIX_FORMAT) << std::endl; }
|
||||
else { ss << "Not Computed" << std::endl; }
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#include "geometry/classifier/CMatrixClassifierFgMDM.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
CMatrixClassifierFgMDM::~CMatrixClassifierFgMDM()
|
||||
{
|
||||
for (auto& v : m_dataset) { v.clear(); }
|
||||
m_dataset.clear();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDM::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
m_dataset = dataset;
|
||||
return train();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDM::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
if (!CMatrixClassifierFgMDMRT::classify(sample, classId, distance, probability, EAdaptations::None)) { return false; }
|
||||
|
||||
// Adaptation
|
||||
if (adaptation == EAdaptations::None) { return true; }
|
||||
// Get class id for adaptation and increase number of trials, expected if supervised, predicted if unsupervised
|
||||
const size_t id = adaptation == EAdaptations::Supervised ? realClassId : classId;
|
||||
if (id >= m_nbClass) { return false; } // Check id (if supervised and bad input)
|
||||
m_nbTrials[id]++; // Update number of trials for the class id
|
||||
m_dataset[id].push_back(sample); // Update the dataset
|
||||
|
||||
// Retrain
|
||||
return train();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
#include "geometry/classifier/CMatrixClassifierFgMDMRT.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Featurization.hpp"
|
||||
#include "geometry/Classification.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
if (dataset.empty()) { return false; }
|
||||
if (!Mean(Vector2DTo1D(dataset), m_ref, EMetric::Riemann)) { return false; } // Compute Reference matrix
|
||||
|
||||
// Transform to the Tangent Space
|
||||
const size_t nbClass = dataset.size();
|
||||
std::vector<std::vector<Eigen::RowVectorXd>> tsSample(nbClass);
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
const size_t nbTrials = dataset[k].size();
|
||||
tsSample[k].resize(nbTrials);
|
||||
for (size_t i = 0; i < nbTrials; ++i) { if (!TangentSpace(dataset[k][i], tsSample[k][i], m_ref)) { return false; } }
|
||||
}
|
||||
|
||||
// Compute FgDA Weight
|
||||
if (!FgDACompute(tsSample, m_weight)) { return false; }
|
||||
|
||||
// Convert dataset
|
||||
std::vector<std::vector<Eigen::MatrixXd>> newDataset(nbClass);
|
||||
std::vector<std::vector<Eigen::RowVectorXd>> filtered(nbClass);
|
||||
for (size_t k = 0; k < nbClass; ++k)
|
||||
{
|
||||
const size_t nbTrials = dataset[k].size();
|
||||
newDataset[k].resize(nbTrials);
|
||||
filtered[k].resize(nbTrials);
|
||||
for (size_t i = 0; i < nbTrials; ++i)
|
||||
{
|
||||
if (!FgDAApply(tsSample[k][i], filtered[k][i], m_weight)) { return false; } // Apply Filter
|
||||
if (!UnTangentSpace(filtered[k][i], newDataset[k][i], m_ref)) { return false; } // Return to Matrix Space
|
||||
}
|
||||
}
|
||||
|
||||
return CMatrixClassifierMDM::train(newDataset); // Train MDM
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
Eigen::RowVectorXd tsSample, filtered;
|
||||
Eigen::MatrixXd newSample;
|
||||
|
||||
if (!TangentSpace(sample, tsSample, m_ref)) { return false; } // Transform to the Tangent Space
|
||||
if (!FgDAApply(tsSample, filtered, m_weight)) { return false; } // Apply Filter
|
||||
if (!UnTangentSpace(filtered, newSample, m_ref)) { return false; } // Return to Matrix Space
|
||||
return CMatrixClassifierMDM::classify(newSample, classId, distance, probability, adaptation, realClassId);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::isEqual(const CMatrixClassifierFgMDMRT& obj, const double precision) const
|
||||
{
|
||||
if (!CMatrixClassifierMDM::isEqual(obj, precision)) { return false; } // Compare base members
|
||||
if (!AreEquals(m_ref, obj.m_ref, precision)) { return false; } // Compare Reference
|
||||
if (!AreEquals(m_weight, obj.m_weight, precision)) { return false; } // Compare Weight
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierFgMDMRT::copy(const CMatrixClassifierFgMDMRT& obj)
|
||||
{
|
||||
CMatrixClassifierMDM::copy(obj);
|
||||
m_ref = obj.m_ref;
|
||||
m_weight = obj.m_weight;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
// Save Reference
|
||||
tinyxml2::XMLElement* reference = doc.NewElement("Reference"); // Create Reference node
|
||||
if (!saveMatrix(reference, m_ref)) { return false; } // Save class
|
||||
data->InsertEndChild(reference); // Add class node to data node
|
||||
|
||||
// Save Weight
|
||||
tinyxml2::XMLElement* weight = doc.NewElement("Weight"); // Create LDA Weight node
|
||||
if (!saveMatrix(weight, m_weight)) { return false; } // Save class
|
||||
data->InsertEndChild(weight); // Add class node to data node
|
||||
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRT::loadAdditional(tinyxml2::XMLElement* data)
|
||||
{
|
||||
// Load Reference
|
||||
tinyxml2::XMLElement* ref = data->FirstChildElement("Reference"); // Get Reference Node
|
||||
if (!loadMatrix(ref, m_ref)) { return false; } // Load Reference Matrix
|
||||
|
||||
// Load Weight
|
||||
tinyxml2::XMLElement* weight = data->FirstChildElement("Weight"); // Get LDA Weight Node
|
||||
return loadMatrix(weight, m_weight); // Load LDA Weight Matrix
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CMatrixClassifierFgMDMRT::printAdditional() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Reference matrix : " << std::endl << m_ref.format(MATRIX_FORMAT) << std::endl; // Reference
|
||||
ss << "Weight matrix : " << std::endl << m_weight.format(MATRIX_FORMAT) << std::endl; // Print Weight
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
#include "geometry/classifier/CMatrixClassifierFgMDMRTRebias.hpp"
|
||||
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Covariance.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//**********************
|
||||
//***** Classifier *****
|
||||
//**********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
if (!m_bias.computeBias(dataset, m_metric)) { return false; }
|
||||
std::vector<std::vector<Eigen::MatrixXd>> newDataset;
|
||||
m_bias.applyBias(dataset, newDataset);
|
||||
if (!CMatrixClassifierFgMDMRT::train(newDataset)) { return false; } // Train FgMDM
|
||||
const Eigen::MatrixXd identity = Eigen::MatrixXd::Identity(m_ref.rows(), m_ref.cols()); // Identity matrix
|
||||
if (AreEquals(m_ref, identity)) { m_ref = identity; } // Normally it's always the case with Identity matrix we simplify future operation
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
if (!IsSquare(sample)) { return false; } // Verification if it's a square matrix
|
||||
Eigen::MatrixXd newSample;
|
||||
m_bias.applyBias(sample, newSample);
|
||||
m_bias.updateBias(sample, m_metric);
|
||||
return CMatrixClassifierFgMDMRT::classify(newSample, classId, distance, probability, adaptation, realClassId);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
if (!CMatrixClassifierFgMDMRT::saveAdditional(doc, data)) { return false; }
|
||||
if (!m_bias.saveAdditional(doc, data)) { return false; }
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::loadAdditional(tinyxml2::XMLElement* data)
|
||||
{
|
||||
if (!CMatrixClassifierFgMDMRT::loadAdditional(data)) { return false; }
|
||||
if (!m_bias.loadAdditional(data)) { return false; }
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierFgMDMRTRebias::isEqual(const CMatrixClassifierFgMDMRTRebias& obj, const double precision) const
|
||||
{
|
||||
return CMatrixClassifierFgMDMRT::isEqual(obj, precision) && m_bias == obj.m_bias;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierFgMDMRTRebias::copy(const CMatrixClassifierFgMDMRTRebias& obj)
|
||||
{
|
||||
CMatrixClassifierFgMDMRT::copy(obj);
|
||||
m_bias = obj.m_bias;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CMatrixClassifierFgMDMRTRebias::printAdditional() const
|
||||
{
|
||||
std::stringstream ss = CMatrixClassifierFgMDMRT::printAdditional();
|
||||
ss << m_bias;
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
#include "geometry/classifier/CMatrixClassifierMDM.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Distance.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include "geometry/Geodesic.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//***********************
|
||||
//***** Constructor *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
CMatrixClassifierMDM::CMatrixClassifierMDM(const size_t nbClass, const EMetric metric)
|
||||
{
|
||||
CMatrixClassifierMDM::setClassCount(nbClass);
|
||||
m_metric = metric;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
CMatrixClassifierMDM::~CMatrixClassifierMDM()
|
||||
{
|
||||
m_means.clear();
|
||||
m_nbTrials.clear();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//**********************
|
||||
//***** Classifier *****
|
||||
//**********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierMDM::setClassCount(const size_t nbClass)
|
||||
{
|
||||
if (m_nbClass != nbClass || m_means.size() != nbClass || m_nbTrials.size() != nbClass)
|
||||
{
|
||||
IMatrixClassifier::setClassCount(nbClass);
|
||||
m_means.resize(m_nbClass);
|
||||
m_nbTrials.resize(nbClass);
|
||||
}
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
if (dataset.empty()) { return false; }
|
||||
setClassCount(dataset.size()); // Change the number of classes if needed
|
||||
for (size_t k = 0; k < m_nbClass; ++k) // for each class
|
||||
{
|
||||
if (!Mean(dataset[k], m_means[k], m_metric)) { return false; } // Compute the mean of each class
|
||||
m_nbTrials[k] = dataset[k].size();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
if (!IsSquare(sample)) { return false; } // Verification if it's a square matrix
|
||||
double distMin = std::numeric_limits<double>::max(); // Init of distance min
|
||||
|
||||
// Compute Distances
|
||||
distance.resize(m_nbClass);
|
||||
for (size_t k = 0; k < m_nbClass; ++k)
|
||||
{
|
||||
distance[k] = Distance(sample, m_means[k], m_metric);
|
||||
if (distMin > distance[k])
|
||||
{
|
||||
classId = k;
|
||||
distMin = distance[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Compute Probabilities (personnal method)
|
||||
probability.resize(m_nbClass);
|
||||
double sumProbability = 0.0;
|
||||
for (size_t k = 0; k < m_nbClass; ++k)
|
||||
{
|
||||
probability[k] = distMin / distance[k];
|
||||
sumProbability += probability[k];
|
||||
}
|
||||
|
||||
for (auto& p : probability) { p /= sumProbability; }
|
||||
|
||||
// Adaptation
|
||||
if (adaptation == EAdaptations::None) { return true; }
|
||||
// Get class id for adaptation and increase number of trials, expected if supervised, predicted if unsupervised
|
||||
const size_t id = adaptation == EAdaptations::Supervised ? realClassId : classId;
|
||||
if (id >= m_nbClass) { return false; } // Check id (if supervised and bad input)
|
||||
m_nbTrials[id]++; // Update number of trials for the class id
|
||||
return Geodesic(m_means[id], sample, m_means[id], m_metric, 1.0 / m_nbTrials[id]);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::saveClasses(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
for (size_t k = 0; k < m_nbClass; ++k) // for each class
|
||||
{
|
||||
tinyxml2::XMLElement* element = doc.NewElement("Class"); // Create class node
|
||||
element->SetAttribute("class-id", int(k)); // Set attribute class id (0 to K)
|
||||
element->SetAttribute("nb-trials", int(m_nbTrials[k])); // Set attribute class number of trials
|
||||
if (!saveMatrix(element, m_means[k])) { return false; } // Save class Matrix Reference
|
||||
data->InsertEndChild(element); // Add class node to data node
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::loadClasses(tinyxml2::XMLElement* data)
|
||||
{
|
||||
tinyxml2::XMLElement* element = data->FirstChildElement("Class"); // Get First Class Node
|
||||
for (size_t k = 0; k < m_nbClass; ++k) // for each class
|
||||
{
|
||||
if (element == nullptr) { return false; } // Check if Node Exist
|
||||
const size_t idx = element->IntAttribute("class-id"); // Get Id (normally idx == k)
|
||||
if (idx != k) { return false; } // Check Id
|
||||
m_nbTrials[k] = element->IntAttribute("nb-trials"); // Get the number of Trials for this class
|
||||
if (!loadMatrix(element, m_means[k])) { return false; } // Load Class Matrix
|
||||
element = element->NextSiblingElement("Class"); // Next Class
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CMatrixClassifierMDM::printClasses() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < m_nbClass; ++i)
|
||||
{
|
||||
ss << "Mean of class " << i << " (" << m_nbTrials[i] << " trials): ";
|
||||
if (m_means[i].size() != 0) { ss << std::endl << m_means[i].format(MATRIX_FORMAT) << std::endl; }
|
||||
else { ss << "Not Computed" << std::endl; }
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDM::isEqual(const CMatrixClassifierMDM& obj, const double precision) const
|
||||
{
|
||||
if (!IMatrixClassifier::isEqual(obj)) { return false; }
|
||||
if (m_nbClass != obj.getClassCount()) { return false; }
|
||||
for (size_t i = 0; i < m_nbClass; ++i)
|
||||
{
|
||||
if (!AreEquals(m_means[i], obj.m_means[i], precision)) { return false; }
|
||||
if (m_nbTrials[i] != obj.m_nbTrials[i]) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierMDM::copy(const CMatrixClassifierMDM& obj)
|
||||
{
|
||||
IMatrixClassifier::copy(obj);
|
||||
setClassCount(m_nbClass);
|
||||
for (size_t i = 0; i < m_nbClass; ++i)
|
||||
{
|
||||
m_means[i] = obj.m_means[i];
|
||||
m_nbTrials[i] = obj.m_nbTrials[i];
|
||||
}
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
#include "geometry/classifier/CMatrixClassifierMDMRebias.hpp"
|
||||
#include "geometry/Mean.hpp"
|
||||
#include "geometry/Basics.hpp"
|
||||
#include <unsupported/Eigen/MatrixFunctions> // SQRT of Matrix
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//**********************
|
||||
//***** Classifier *****
|
||||
//**********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::train(const std::vector<std::vector<Eigen::MatrixXd>>& dataset)
|
||||
{
|
||||
if (!m_bias.computeBias(dataset, m_metric)) { return false; }
|
||||
std::vector<std::vector<Eigen::MatrixXd>> newDataset;
|
||||
m_bias.applyBias(dataset, newDataset);
|
||||
return CMatrixClassifierMDM::train(newDataset); // Train MDM
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::classify(const Eigen::MatrixXd& sample, size_t& classId, std::vector<double>& distance,
|
||||
std::vector<double>& probability, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
if (!IsSquare(sample)) { return false; } // Verification if it's a square matrix
|
||||
Eigen::MatrixXd newSample;
|
||||
m_bias.applyBias(sample, newSample);
|
||||
m_bias.updateBias(sample, m_metric);
|
||||
return CMatrixClassifierMDM::classify(newSample, classId, distance, probability, adaptation, realClassId);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::saveAdditional(tinyxml2::XMLDocument& doc, tinyxml2::XMLElement* data) const
|
||||
{
|
||||
if (!CMatrixClassifierMDM::saveAdditional(doc, data)) { return false; }
|
||||
if (!m_bias.saveAdditional(doc, data)) { return false; }
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::loadAdditional(tinyxml2::XMLElement* data)
|
||||
{
|
||||
if (!CMatrixClassifierMDM::loadAdditional(data)) { return false; }
|
||||
if (!m_bias.loadAdditional(data)) { return false; }
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool CMatrixClassifierMDMRebias::isEqual(const CMatrixClassifierMDMRebias& obj, const double precision) const
|
||||
{
|
||||
return CMatrixClassifierMDM::isEqual(obj, precision) && m_bias == obj.m_bias;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void CMatrixClassifierMDMRebias::copy(const CMatrixClassifierMDMRebias& obj)
|
||||
{
|
||||
CMatrixClassifierMDM::copy(obj);
|
||||
m_bias = obj.m_bias;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
std::stringstream CMatrixClassifierMDMRebias::printAdditional() const
|
||||
{
|
||||
std::stringstream ss = CMatrixClassifierMDM::printAdditional();
|
||||
ss << m_bias;
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
#include "geometry/classifier/IMatrixClassifier.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
//***********************
|
||||
//***** Constructor *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
IMatrixClassifier::IMatrixClassifier(const size_t nbClass, const EMetric metric)
|
||||
{
|
||||
IMatrixClassifier::setClassCount(nbClass);
|
||||
m_metric = metric;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void IMatrixClassifier::setClassCount(const size_t nbClass) { m_nbClass = nbClass; }
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::classify(const Eigen::MatrixXd& sample, size_t& classId, const EAdaptations adaptation, const size_t& realClassId)
|
||||
{
|
||||
std::vector<double> distance, probability;
|
||||
return classify(sample, classId, distance, probability, adaptation, realClassId);
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//***********************
|
||||
//***** XML Manager *****
|
||||
//***********************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::saveXML(const std::string& filename) const
|
||||
{
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
// Create Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.NewElement("Classifier"); // Create root node
|
||||
xmlDoc.InsertFirstChild(root); // Add root to XML
|
||||
|
||||
tinyxml2::XMLElement* data = xmlDoc.NewElement("Classifier-data"); // Create data node
|
||||
if (!saveHeader(data)) { return false; } // Save Header attribute
|
||||
if (!saveAdditional(xmlDoc, data)) { return false; } // Save Optionnal Informations
|
||||
if (!saveClasses(xmlDoc, data)) { return false; } // Save Classes
|
||||
|
||||
root->InsertEndChild(data); // Add data to root
|
||||
return xmlDoc.SaveFile(filename.c_str()) == 0; // save XML (if != 0 it means error)
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::loadXML(const std::string& filename)
|
||||
{
|
||||
// Load File
|
||||
tinyxml2::XMLDocument xmlDoc;
|
||||
if (xmlDoc.LoadFile(filename.c_str()) != 0) { return false; } // Check File Exist and Loading
|
||||
|
||||
// Load Root
|
||||
tinyxml2::XMLNode* root = xmlDoc.FirstChild(); // Get Root Node
|
||||
if (root == nullptr) { return false; } // Check Root Node Exist
|
||||
|
||||
// Load Data
|
||||
tinyxml2::XMLElement* data = root->FirstChildElement("Classifier-data"); // Get Data Node
|
||||
if (data == nullptr) { return false; } // Check Root Node Exist
|
||||
if (!loadHeader(data)) { return false; } // Load Header attribute
|
||||
if (!loadAdditional(data)) { return false; } // Load Optionnal Informations
|
||||
if (!loadClasses(data)) { return false; } // Load Classes
|
||||
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::convertMatrixToXMLFormat(const Eigen::MatrixXd& in, std::stringstream& out)
|
||||
{
|
||||
out << in.format(MATRIX_FORMAT);
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::convertXMLFormatToMatrix(std::stringstream& in, Eigen::MatrixXd& out, const size_t rows, const size_t cols)
|
||||
{
|
||||
out = Eigen::MatrixXd::Identity(rows, cols); // Init With Identity Matrix (in case of)
|
||||
for (size_t i = 0; i < rows; ++i) // Fill Matrix
|
||||
{
|
||||
for (size_t j = 0; j < cols; ++j) { in >> out(i, j); }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::saveMatrix(tinyxml2::XMLElement* element, const Eigen::MatrixXd& matrix)
|
||||
{
|
||||
element->SetAttribute("size", int(matrix.rows())); // Set Matrix size NxN
|
||||
std::stringstream ss;
|
||||
convertMatrixToXMLFormat(matrix, ss);
|
||||
element->SetText(ss.str().c_str()); // Write Means Value
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::loadMatrix(tinyxml2::XMLElement* element, Eigen::MatrixXd& matrix)
|
||||
{
|
||||
const size_t size = element->IntAttribute("size"); // Get number of row/col
|
||||
if (size == 0) { return true; }
|
||||
std::stringstream ss(element->GetText()); // String stream to parse Matrix value
|
||||
convertXMLFormatToMatrix(ss, matrix, size, size);
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*****************************
|
||||
//***** Override Operator *****
|
||||
//*****************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::isEqual(const IMatrixClassifier& obj, const double /*precision*/) const
|
||||
{
|
||||
return m_metric == obj.m_metric && m_nbClass == obj.getClassCount();
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
void IMatrixClassifier::copy(const IMatrixClassifier& obj)
|
||||
{
|
||||
m_metric = obj.m_metric;
|
||||
setClassCount(obj.getClassCount());
|
||||
}
|
||||
/// -------------------------------------------------------------------------------------------------
|
||||
|
||||
/// -------------------------------------------------------------------------------------------------
|
||||
std::stringstream IMatrixClassifier::print() const { return std::stringstream(printHeader().str() + printAdditional().str() + printClasses().str()); }
|
||||
/// -------------------------------------------------------------------------------------------------
|
||||
|
||||
/// -------------------------------------------------------------------------------------------------
|
||||
std::stringstream IMatrixClassifier::printHeader() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << getType() << " Classifier" << std::endl;
|
||||
ss << "Metric : " << toString(m_metric) << std::endl;
|
||||
ss << "Number of Classes : " << m_nbClass << std::endl;
|
||||
return ss;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
//*******************************************
|
||||
//***** XML Manager (Private Functions) *****
|
||||
//*******************************************
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::saveHeader(tinyxml2::XMLElement* data) const
|
||||
{
|
||||
data->SetAttribute("type", getType().c_str()); // Set attribute classifier type
|
||||
data->SetAttribute("class-count", int(m_nbClass)); // Set attribute class count
|
||||
data->SetAttribute("metric", toString(m_metric).c_str()); // Set attribute metric
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
bool IMatrixClassifier::loadHeader(tinyxml2::XMLElement* data)
|
||||
{
|
||||
if (data == nullptr) { return false; } // Check if Node Exist
|
||||
const std::string classifierType = data->Attribute("type"); // Get type
|
||||
if (classifierType != getType()) { return false; } // Check Type
|
||||
setClassCount(data->IntAttribute("class-count")); // Update Number of classes
|
||||
m_metric = StringToMetric(data->Attribute("metric")); // Update Metric
|
||||
return true;
|
||||
}
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Geometry
|
||||
@@ -0,0 +1,50 @@
|
||||
PROJECT(test-geometry)
|
||||
|
||||
FILE(GLOB_RECURSE TESTS_SRC_FILES *.cpp *.hpp)
|
||||
|
||||
ADD_EXECUTABLE(${PROJECT_NAME} ${TESTS_SRC_FILES})
|
||||
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER ${TESTS_FOLDER}) # Place project in folder unit-test (for some IDE)
|
||||
|
||||
# Modify library prefixes and suffixes to comply to Windows or Linux naming
|
||||
IF(WIN32)
|
||||
SET(CMAKE_FIND_LIBRARY_PREFIXES "")
|
||||
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".dll")
|
||||
ELSEIF(APPLE)
|
||||
SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
|
||||
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".a")
|
||||
ELSE()
|
||||
SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
|
||||
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so" ".a")
|
||||
ENDIF()
|
||||
|
||||
|
||||
FIND_PATH(PATH_GTEST ${CMAKE_FIND_LIBRARY_PREFIXES}gtest PATHS ${LIST_DEPENDENCIES_PATH} PATH_SUFFIXES gtest)
|
||||
SET(GTEST_ROOT ${PATH_GTEST}/${CMAKE_FIND_LIBRARY_PREFIXES}gtest)
|
||||
FIND_PACKAGE(GTest REQUIRED)
|
||||
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${GTEST_BOTH_LIBRARIES})
|
||||
INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS})
|
||||
|
||||
# OpenViBE Module
|
||||
INCLUDE("FindModuleGeometry")
|
||||
|
||||
# OpenViBE Third Party
|
||||
INCLUDE("FindThirdPartyEigen")
|
||||
INCLUDE("FindThirdPartyBoost")
|
||||
|
||||
# ---------------------------------
|
||||
# Target macros
|
||||
# Defines target operating system, architecture and compiler
|
||||
# ---------------------------------
|
||||
SET_BUILD_PLATFORM()
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
ADD_TEST(NAME test_Geometry COMMAND ${PROJECT_NAME})
|
||||
|
||||
OV_INSTALL_LAUNCH_SCRIPT(SCRIPT_PREFIX "${PROJECT_NAME}" EXECUTABLE_NAME "${PROJECT_NAME}")
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,36 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// ReSharper disable CppUnusedIncludeDirective
|
||||
#include "test_Basics.hpp"
|
||||
#include "test_Covariance.hpp"
|
||||
#include "test_Mean.hpp"
|
||||
#include "test_Median.hpp"
|
||||
#include "test_Misc.hpp"
|
||||
#include "test_Distance.hpp"
|
||||
#include "test_Geodesics.hpp"
|
||||
#include "test_Featurization.hpp"
|
||||
#include "test_Classifier.hpp"
|
||||
#include "test_MatrixClassifier.hpp"
|
||||
#include "test_ASR.hpp"
|
||||
// ReSharper restore CppUnusedIncludeDirective
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Code coverage tips (this functions are used only if tests failed)
|
||||
const size_t dumS = 0;
|
||||
const double dumD = 0;
|
||||
const Eigen::MatrixXd dumM = Eigen::MatrixXd::Identity(2, 2);
|
||||
const std::vector<int> dumV = { 0, 0 };
|
||||
const Geometry::CMatrixClassifierMDM dumC;
|
||||
ErrorMsg("", dumS, dumS);
|
||||
ErrorMsg("", dumD, dumD);
|
||||
ErrorMsg("", dumM, dumM);
|
||||
ErrorMsg("", dumV, dumV);
|
||||
ErrorMsg("", dumC, dumC);
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
catch (std::exception&) { return 1; }
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file misc.hpp
|
||||
/// \brief Some constants and functions for google tests
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 0.1.
|
||||
/// \date 26/10/2018.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
/// \remarks
|
||||
/// - For this test I compare the results with the <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> library (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>) or <a href="http://scikit-learn.org">sklearn</a> if pyRiemman just redirect the function.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
#include <geometry/classifier/IMatrixClassifier.hpp>
|
||||
#include <geometry/artifacts/CASR.hpp>
|
||||
|
||||
const std::string SEP = "\n====================\n";
|
||||
|
||||
//*********************************************************************************
|
||||
//********** Comparison of values with epsilon tolerance for google test **********
|
||||
//*********************************************************************************
|
||||
/// <summary> Check if two doubles are almost equal. </summary>
|
||||
/// <param name="x"> The first value. </param>
|
||||
/// <param name="y"> The second value. </param>
|
||||
/// <param name="epsilon"> (Optional) The epsilon tolerance. </param>
|
||||
/// <returns> True if almost equal, false if not. </returns>
|
||||
inline bool isAlmostEqual(const double x, const double y, const double epsilon = 0.0001) { return std::abs(x - y) < epsilon; }
|
||||
|
||||
/// <summary> Check if sum of two vectors are almost equal. </summary>
|
||||
/// <typeparam name="T"> Generic numeric type parameter. </typeparam>
|
||||
/// \copydetails isAlmostEqual(const double, const double, const double)
|
||||
template <typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
|
||||
bool isAlmostEqual(const std::vector<T>& x, const std::vector<T>& y, const double epsilon = 0.0001)
|
||||
{
|
||||
double xsum = 0.0, ysum = 0.0;
|
||||
for (const auto& n : x) { xsum += n; }
|
||||
for (const auto& n : y) { ysum += n; }
|
||||
return (x.size() == y.size() && isAlmostEqual(xsum, ysum, epsilon));
|
||||
}
|
||||
|
||||
/// <summary> Check if sum of two matrix are almost equal. </summary>
|
||||
/// \copydetails isAlmostEqual(const double, const double, const double)
|
||||
inline bool isAlmostEqual(const Eigen::MatrixXd& x, const Eigen::MatrixXd& y, const double epsilon = 0.0001)
|
||||
{
|
||||
return x.size() == y.size() && isAlmostEqual(x.cwiseAbs().sum(), y.cwiseAbs().sum(), epsilon);
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
//********** Error Message Standardization for googltest **********
|
||||
//*****************************************************************
|
||||
/// <summary> Error message for size_t. </summary>
|
||||
/// <param name="name"> The name of the test. </param>
|
||||
/// <param name="ref"> The reference value. </param>
|
||||
/// <param name="calc"> The calculate value. </param>
|
||||
/// <returns> Error message. </returns>
|
||||
inline std::string ErrorMsg(const std::string& name, const size_t ref, const size_t calc)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << SEP << name << " : Reference : " << ref << ", \tCompute : " << calc << SEP;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary> Error message for doubles. </summary>
|
||||
/// \copydetails ErrorMsg(const std::string&, const size_t, const size_t)
|
||||
inline std::string ErrorMsg(const std::string& name, const double ref, const double calc)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << SEP << name << " : Reference : " << ref << ", \tCompute : " << calc << SEP;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary> Error message for numeric vector. </summary>
|
||||
/// <typeparam name="T"> Generic numeric type parameter. </typeparam>
|
||||
/// \copydetails ErrorMsg(const std::string&, const size_t, const size_t)
|
||||
template <typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value, T>::type>
|
||||
std::string ErrorMsg(const std::string& name, const std::vector<T>& ref, const std::vector<T>& calc)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << SEP << name << " : " << std::endl << " Reference : \t[";
|
||||
for (const T& t : ref) { ss << t << ", "; }
|
||||
if (!ref.empty()) { ss.seekp(ss.str().length() - 2); }
|
||||
ss << "] " << std::endl << " Compute : \t[";
|
||||
for (const T& t : calc) { ss << t << ", "; }
|
||||
if (!ref.empty()) { ss.seekp(ss.str().length() - 2); }
|
||||
ss << "] " << SEP;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary> Error message for matrix. </summary>
|
||||
/// \copydetails ErrorMsg(const std::string&, const size_t, const size_t)
|
||||
inline std::string ErrorMsg(const std::string& name, const Eigen::MatrixXd& ref, const Eigen::MatrixXd& calc)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << SEP << name << " : " << std::endl << "********** Reference **********\n" << ref << std::endl << "********** Compute **********\n" << calc << SEP;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary> Error message for matrix Classifier. </summary>
|
||||
/// \copydetails ErrorMsg(const std::string&, const size_t, const size_t)
|
||||
inline std::string ErrorMsg(const std::string& name, const Geometry::IMatrixClassifier& ref, const Geometry::IMatrixClassifier& calc)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << SEP << name << " : " << std::endl << "********** Reference **********\n" << ref << std::endl << "********** Compute **********\n" << calc << SEP;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary> Error message for ASR. </summary>
|
||||
/// \copydetails ErrorMsg(const std::string&, const size_t, const size_t)
|
||||
inline std::string ErrorMsg(const std::string& name, const Geometry::CASR& ref, const Geometry::CASR& calc)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << SEP << name << " : " << std::endl << "********** Reference **********\n" << ref << std::endl << "********** Compute **********\n" << calc << SEP;
|
||||
return ss.str();
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_ASR.hpp
|
||||
/// \brief Tests for Artifact Subspace Reconstruction.
|
||||
/// \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 We use the EEglab Matlab plugin to compare result for validation
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "init.hpp"
|
||||
#include "misc.hpp"
|
||||
|
||||
#include <geometry/artifacts/CASR.hpp>
|
||||
#include <geometry/Basics.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_ASR : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<Eigen::MatrixXd> m_dataset;
|
||||
|
||||
void SetUp() override { m_dataset = Geometry::Vector2DTo1D(InitDataset::Dataset()); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_ASR, Train_Euclidian)
|
||||
{
|
||||
const Geometry::CASR ref = InitASR::Euclidian::Reference();
|
||||
const Geometry::CASR calc(Geometry::EMetric::Euclidian, m_dataset);
|
||||
EXPECT_TRUE(calc == ref) << ErrorMsg("Train ASR in Euclidian metric", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_ASR, Train_Riemann)
|
||||
{
|
||||
std::cout << "Riemannian Eigen Value isn't implemented, so result is same as Euclidian metric." << std::endl;
|
||||
const Geometry::CASR ref = InitASR::Riemann::Reference();
|
||||
const Geometry::CASR calc(Geometry::EMetric::Riemann, m_dataset);
|
||||
EXPECT_TRUE(calc == ref) << ErrorMsg("Train ASR in Riemann metric", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_ASR, Process)
|
||||
{
|
||||
m_dataset = InitDataset::FirstClassDataset();
|
||||
Geometry::CASR calc(Geometry::EMetric::Euclidian, m_dataset);
|
||||
|
||||
std::vector<Eigen::MatrixXd> testset = InitDataset::SecondClassDataset();
|
||||
std::vector<Eigen::MatrixXd> result(testset.size());
|
||||
for (size_t i = 0; i < testset.size(); ++i)
|
||||
{
|
||||
testset[i] *= 2;
|
||||
EXPECT_TRUE(calc.process(testset[i], result[i])) << "ASR Process fail for sample " + std::to_string(i) + ".\n";
|
||||
}
|
||||
for (size_t i = 1; i < testset.size(); ++i)
|
||||
{
|
||||
EXPECT_FALSE(isAlmostEqual(result[i], testset[i])) << "the sample " + std::to_string(i) + " wasn't reconstructed.\n";
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_ASR, Save)
|
||||
{
|
||||
Geometry::CASR calc;
|
||||
const Geometry::CASR ref = InitASR::Euclidian::Reference();
|
||||
EXPECT_TRUE(ref.saveXML("test_ASR_Save.xml")) << "Error during Saving : " << std::endl << ref << std::endl;
|
||||
EXPECT_TRUE(calc.loadXML("test_ASR_Save.xml")) << "Error during Loading : " << std::endl << calc << std::endl;
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("ASR Save", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,150 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Basics.hpp
|
||||
/// \brief Tests for Basic functions of Module.
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 1.0.
|
||||
/// \date 09/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "init.hpp"
|
||||
#include "misc.hpp"
|
||||
|
||||
#include <geometry/Basics.hpp>
|
||||
#include <geometry/Metrics.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Basics : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<std::vector<Eigen::MatrixXd>> m_dataSet;
|
||||
|
||||
void SetUp() override { m_dataSet = InitDataset::Dataset(); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Basics, MatrixStandardization)
|
||||
{
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calcC, refC = InitBasics::Center::Reference();
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calcS, refS = InitBasics::StandardScaler::Reference();
|
||||
calcC.resize(m_dataSet.size());
|
||||
calcS.resize(m_dataSet.size());
|
||||
for (size_t k = 0; k < m_dataSet.size(); ++k)
|
||||
{
|
||||
calcC[k].resize(m_dataSet[k].size());
|
||||
calcS[k].resize(m_dataSet[k].size());
|
||||
for (size_t i = 0; i < m_dataSet[k].size(); ++i)
|
||||
{
|
||||
EXPECT_TRUE(MatrixStandardization(m_dataSet[k][i], calcC[k][i], Geometry::EStandardization::Center)) << "Error During Centerization" << std::endl;
|
||||
EXPECT_TRUE(MatrixStandardization(m_dataSet[k][i], calcS[k][i], Geometry::EStandardization::StandardScale)) << "Error During Standard Scaler" << std::endl;
|
||||
const std::string title = "Matrix Center Sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
EXPECT_TRUE(isAlmostEqual(refC[k][i], calcC[k][i])) << ErrorMsg(title, refC[k][i], calcC[k][i]);
|
||||
EXPECT_TRUE(isAlmostEqual(refS[k][i], calcS[k][i])) << ErrorMsg(title, refS[k][i], calcS[k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Basics, GetElements)
|
||||
{
|
||||
Eigen::RowVectorXd ref(3);
|
||||
const std::vector<size_t> idx{ 0, 4, 7 };
|
||||
ref << -3, -6, -1;
|
||||
const Eigen::RowVectorXd calc = Geometry::GetElements(m_dataSet[0][0].row(0), idx); // row = -3, -4, -5, -4, -6, -1, -4, -1, -3, -1
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("GetElements", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Basics, ARange)
|
||||
{
|
||||
const std::vector<size_t> ref{ 1, 3, 5, 7, 9 },
|
||||
calc = Geometry::ARange(size_t(1), size_t(10), size_t(2));
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("ARange", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Basics, Vector2DTo1D)
|
||||
{
|
||||
std::vector<Eigen::MatrixXd> calc = Geometry::Vector2DTo1D(m_dataSet);
|
||||
bool equal = true;
|
||||
size_t idx = 0;
|
||||
for (auto& set : m_dataSet) { for (const auto& data : set) { if (!isAlmostEqual(data, calc[idx++])) { equal = false; } } }
|
||||
EXPECT_TRUE(equal) << "Vector2DTo1D fail";
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Basics, Metrics)
|
||||
{
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::Riemann) == "Riemann");
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::Euclidian) == "Euclidian");
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::LogEuclidian) == "Log Euclidian");
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::LogDet) == "Log Determinant");
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::Kullback) == "Kullback");
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::ALE) == "AJD-based log-Euclidean");
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::Harmonic) == "Harmonic");
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::Wasserstein) == "Wasserstein");
|
||||
EXPECT_TRUE(toString(Geometry::EMetric::Identity) == "Identity");
|
||||
EXPECT_TRUE(Geometry::StringToMetric("Riemann") == Geometry::EMetric::Riemann);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("Euclidian") == Geometry::EMetric::Euclidian);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("Log Euclidian") == Geometry::EMetric::LogEuclidian);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("Log Determinant") == Geometry::EMetric::LogDet);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("Kullback") == Geometry::EMetric::Kullback);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("AJD-based log-Euclidean") == Geometry::EMetric::ALE);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("Harmonic") == Geometry::EMetric::Harmonic);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("Wasserstein") == Geometry::EMetric::Wasserstein);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("Identity") == Geometry::EMetric::Identity);
|
||||
EXPECT_TRUE(Geometry::StringToMetric("") == Geometry::EMetric::Identity);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Basics, Validation)
|
||||
{
|
||||
const Eigen::MatrixXd m1 = Eigen::MatrixXd::Zero(2, 2),
|
||||
m2 = Eigen::MatrixXd::Zero(1, 2),
|
||||
m3;
|
||||
std::vector<Eigen::MatrixXd> v;
|
||||
|
||||
EXPECT_TRUE(Geometry::InRange(1, 0, 2)); // 0 <= 1 <= 2 ?
|
||||
EXPECT_FALSE(Geometry::InRange(2, 0, 1)); // 0 <= 2 <= 1 ?
|
||||
|
||||
EXPECT_FALSE(Geometry::AreNotEmpty(v)); // Empty Vector
|
||||
v.push_back(m3);
|
||||
EXPECT_FALSE(Geometry::AreNotEmpty(v)); // Vector with one empty matix
|
||||
v.push_back(m1);
|
||||
EXPECT_FALSE(Geometry::AreNotEmpty(v)); // Vector With one empty matrix and one non empty matrix
|
||||
v.clear();
|
||||
v.push_back(m1);
|
||||
v.push_back(m2);
|
||||
EXPECT_TRUE(Geometry::AreNotEmpty(v)); // Vector With two non empty matrix
|
||||
|
||||
EXPECT_TRUE(Geometry::HaveSameSize(m1, m1)); // Same matrix
|
||||
EXPECT_FALSE(Geometry::HaveSameSize(m3, m3)); // Same but empty
|
||||
EXPECT_FALSE(Geometry::HaveSameSize(m1, m2)); // DIfferents
|
||||
EXPECT_FALSE(Geometry::HaveSameSize(m1, m3)); // One empty
|
||||
|
||||
EXPECT_FALSE(Geometry::HaveSameSize(v)); // Two different
|
||||
EXPECT_FALSE(Geometry::AreSquare(v)); // One square
|
||||
v.clear();
|
||||
v.push_back(m1);
|
||||
v.push_back(m1);
|
||||
EXPECT_TRUE(Geometry::HaveSameSize(v) && Geometry::AreSquare(v)); // Same matrix
|
||||
|
||||
Geometry::MatrixPrint(m1); // Only to check
|
||||
Geometry::MatrixPrint(m3); // Only to check
|
||||
|
||||
std::vector<std::string> vs = Geometry::Split("0,1,2,3.a\n", ",");
|
||||
EXPECT_TRUE(vs.size() == 4 && vs[0] == "0" && vs[1] == "1" && vs[2] == "2" && vs[3] == "3.a") << vs.size() << " " << vs[0] << " " << vs[1] << " " << vs[2] << " " << vs[3] << std::endl;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,53 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Classifier.hpp
|
||||
/// \brief Tests for Classifier Functions.
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 1.0.
|
||||
/// \date 09/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "misc.hpp"
|
||||
#include "init.hpp"
|
||||
|
||||
#include <geometry/Classification.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Classifier : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<std::vector<Eigen::RowVectorXd>> m_dataSet;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
const std::vector<Eigen::RowVectorXd> tmp = InitFeaturization::TangentSpace::Reference();
|
||||
m_dataSet = Geometry::Vector1DTo2D(tmp, { NB_TRIALS1, NB_TRIALS2 });
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Classifier, LSQR)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitClassif::LSQR::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Geometry::LSQR(m_dataSet, calc);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("LSQR", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Classifier, FgDACompute)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitClassif::FgDACompute::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Geometry::FgDACompute(m_dataSet, calc);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("FgDA", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,158 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Covariance.hpp
|
||||
/// \brief Tests for Covariance Matrix Functions.
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 1.0.
|
||||
/// \date 09/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "misc.hpp"
|
||||
#include "init.hpp"
|
||||
|
||||
#include <geometry/Covariance.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Covariances : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<std::vector<Eigen::MatrixXd>> m_dataSet;
|
||||
|
||||
void SetUp() override { m_dataSet = InitDataset::Dataset(); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Covariances, Covariance_Matrix_COR)
|
||||
{
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calc, ref = InitCovariance::COR::Dataset();
|
||||
calc.resize(m_dataSet.size());
|
||||
for (size_t k = 0; k < m_dataSet.size(); ++k)
|
||||
{
|
||||
calc[k].resize(m_dataSet[k].size());
|
||||
for (size_t i = 0; i < m_dataSet[k].size(); ++i)
|
||||
{
|
||||
CovarianceMatrix(m_dataSet[k][i], calc[k][i], Geometry::EEstimator::COR, Geometry::EStandardization::None);
|
||||
const std::string title = "Covariance Matrix COR Sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
EXPECT_TRUE(isAlmostEqual(ref[k][i], calc[k][i])) << ErrorMsg(title, ref[k][i], calc[k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Covariances, Covariance_Matrix_COV)
|
||||
{
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calc, ref = InitCovariance::COV::Dataset();
|
||||
calc.resize(m_dataSet.size());
|
||||
for (size_t k = 0; k < m_dataSet.size(); ++k)
|
||||
{
|
||||
calc[k].resize(m_dataSet[k].size());
|
||||
for (size_t i = 0; i < m_dataSet[k].size(); ++i)
|
||||
{
|
||||
CovarianceMatrix(m_dataSet[k][i], calc[k][i], Geometry::EEstimator::COV, Geometry::EStandardization::None);
|
||||
const std::string title = "Covariance Matrix COV Sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
EXPECT_TRUE(isAlmostEqual(ref[k][i], calc[k][i])) << ErrorMsg(title, ref[k][i], calc[k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Covariances, Covariance_Matrix_LWF)
|
||||
{
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calc, ref = InitCovariance::LWF::Reference();
|
||||
calc.resize(m_dataSet.size());
|
||||
for (size_t k = 0; k < m_dataSet.size(); ++k)
|
||||
{
|
||||
calc[k].resize(m_dataSet[k].size());
|
||||
for (size_t i = 0; i < m_dataSet[k].size(); ++i)
|
||||
{
|
||||
CovarianceMatrix(m_dataSet[k][i], calc[k][i], Geometry::EEstimator::LWF, Geometry::EStandardization::Center);
|
||||
const std::string title = "Covariance Matrix LWF Sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
EXPECT_TRUE(isAlmostEqual(ref[k][i], calc[k][i])) << ErrorMsg(title, ref[k][i], calc[k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Covariances, Covariance_Matrix_MCD)
|
||||
{
|
||||
std::cout << "Not implemented" << std::endl;
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calc;
|
||||
//std::vector<std::vector<Eigen::MatrixXd>> ref = InitCovariance::MCD::Reference();
|
||||
calc.resize(m_dataSet.size());
|
||||
for (size_t k = 0; k < m_dataSet.size(); ++k)
|
||||
{
|
||||
calc[k].resize(m_dataSet[k].size());
|
||||
for (size_t i = 0; i < m_dataSet[k].size(); ++i)
|
||||
{
|
||||
CovarianceMatrix(m_dataSet[k][i], calc[k][i], Geometry::EEstimator::MCD, Geometry::EStandardization::Center);
|
||||
//const std::string title = "Covariance Matrix MCD Sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
//EXPECT_TRUE(isAlmostEqual(ref[k][i], calc[k][i])) << ErrorMsg(title, ref[k][i], calc[k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Covariances, Covariance_Matrix_OAS)
|
||||
{
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calc, ref = InitCovariance::OAS::Reference();
|
||||
calc.resize(m_dataSet.size());
|
||||
for (size_t k = 0; k < m_dataSet.size(); ++k)
|
||||
{
|
||||
calc[k].resize(m_dataSet[k].size());
|
||||
for (size_t i = 0; i < m_dataSet[k].size(); ++i)
|
||||
{
|
||||
CovarianceMatrix(m_dataSet[k][i], calc[k][i], Geometry::EEstimator::OAS, Geometry::EStandardization::Center);
|
||||
const std::string title = "Covariance Matrix OAS Sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
EXPECT_TRUE(isAlmostEqual(ref[k][i], calc[k][i])) << ErrorMsg(title, ref[k][i], calc[k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Covariances, Covariance_Matrix_SCM)
|
||||
{
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calc, ref = InitCovariance::SCM::Reference();
|
||||
calc.resize(m_dataSet.size());
|
||||
for (size_t k = 0; k < m_dataSet.size(); ++k)
|
||||
{
|
||||
calc[k].resize(m_dataSet[k].size());
|
||||
for (size_t i = 0; i < m_dataSet[k].size(); ++i)
|
||||
{
|
||||
CovarianceMatrix(m_dataSet[k][i], calc[k][i], Geometry::EEstimator::SCM, Geometry::EStandardization::None);
|
||||
const std::string title = "Covariance Matrix SCM Sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
EXPECT_TRUE(isAlmostEqual(ref[k][i], calc[k][i])) << ErrorMsg(title, ref[k][i], calc[k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Covariances, Covariance_Matrix_IDE)
|
||||
{
|
||||
std::vector<std::vector<Eigen::MatrixXd>> calc;
|
||||
const Eigen::MatrixXd ref = Eigen::MatrixXd::Identity(NB_CHAN, NB_CHAN);
|
||||
calc.resize(m_dataSet.size());
|
||||
for (size_t k = 0; k < m_dataSet.size(); ++k)
|
||||
{
|
||||
calc[k].resize(m_dataSet[k].size());
|
||||
for (size_t i = 0; i < m_dataSet[k].size(); ++i)
|
||||
{
|
||||
CovarianceMatrix(m_dataSet[k][i], calc[k][i], Geometry::EEstimator::IDE, Geometry::EStandardization::None);
|
||||
const std::string title = "Covariance Matrix IDE Sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc[k][i])) << ErrorMsg(title, ref, calc[k][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,119 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Distance.hpp
|
||||
/// \brief Tests for Distance Functions.
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 1.0.
|
||||
/// \date 09/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "misc.hpp"
|
||||
#include "init.hpp"
|
||||
|
||||
#include <geometry/Distance.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Distances : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<Eigen::MatrixXd> m_dataSet;
|
||||
|
||||
void SetUp() override { m_dataSet = Geometry::Vector2DTo1D(InitCovariance::LWF::Reference()); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Distances, Euclidian)
|
||||
{
|
||||
const std::vector<double> ref = InitDistance::Euclidian::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Euclidian::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
const double calc = Distance(mean, m_dataSet[i], Geometry::EMetric::Euclidian);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Distance Euclidian Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Distances, LogEuclidian)
|
||||
{
|
||||
const std::vector<double> ref = InitDistance::LogEuclidian::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::LogEuclidian::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
const double calc = Distance(mean, m_dataSet[i], Geometry::EMetric::LogEuclidian);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Distance LogEuclidian Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Distances, Riemann)
|
||||
{
|
||||
const std::vector<double> ref = InitDistance::Riemann::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Riemann::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
const double calc = Distance(mean, m_dataSet[i], Geometry::EMetric::Riemann);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Distance Riemann Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Distances, LogDet)
|
||||
{
|
||||
const std::vector<double> ref = InitDistance::LogDeterminant::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::LogDeterminant::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
const double calc = Distance(mean, m_dataSet[i], Geometry::EMetric::LogDet);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Distance LogDet Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Distances, Kullback)
|
||||
{
|
||||
const std::vector<double> ref = InitDistance::Kullback::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Kullback::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
const double calc = Distance(mean, m_dataSet[i], Geometry::EMetric::Kullback);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Distance Kullback Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Distances, Wasserstein)
|
||||
{
|
||||
const std::vector<double> ref = InitDistance::Wasserstein::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Wasserstein::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
const double calc = Distance(mean, m_dataSet[i], Geometry::EMetric::Wasserstein);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Distance Wasserstein Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Distances, Identity)
|
||||
{
|
||||
const Eigen::MatrixXd mean = InitMeans::Wasserstein::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
const double calc = Distance(mean, m_dataSet[i], Geometry::EMetric::Identity);
|
||||
EXPECT_TRUE(isAlmostEqual(1, calc)) << ErrorMsg("Distance Wasserstein Sample [" + std::to_string(i) + "]", 1, calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,91 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Featurization.hpp
|
||||
/// \brief Tests for Matrix Featurization Functions.
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 1.0.
|
||||
/// \date 09/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "misc.hpp"
|
||||
#include "init.hpp"
|
||||
|
||||
#include <geometry/Featurization.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Featurization : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<Eigen::MatrixXd> m_dataSet;
|
||||
|
||||
void SetUp() override { m_dataSet = Geometry::Vector2DTo1D(InitCovariance::LWF::Reference()); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Featurization, TangentSpace)
|
||||
{
|
||||
const std::vector<Eigen::RowVectorXd> ref = InitFeaturization::TangentSpace::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Riemann::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
Eigen::RowVectorXd calc;
|
||||
EXPECT_TRUE(Geometry::Featurization(m_dataSet[i], calc, true, mean)) << "Error During Processing";
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("TangentSpace Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Featurization, UnTangentSpace)
|
||||
{
|
||||
const std::vector<Eigen::RowVectorXd> ref = InitFeaturization::TangentSpace::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Riemann::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
Eigen::MatrixXd calc;
|
||||
EXPECT_TRUE(Geometry::UnFeaturization(ref[i], calc, true, mean)) << "Error During Processing";
|
||||
EXPECT_TRUE(isAlmostEqual(m_dataSet[i], calc)) << ErrorMsg("UnTangentSpace Sample [" + std::to_string(i) + "]", m_dataSet[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Featurization, Squeeze)
|
||||
{
|
||||
const std::vector<Eigen::RowVectorXd> ref = InitFeaturization::Squeeze::Reference();
|
||||
const std::vector<Eigen::RowVectorXd> refDiag = InitFeaturization::SqueezeDiag::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Riemann::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
Eigen::RowVectorXd calc;
|
||||
EXPECT_TRUE(Geometry::Featurization(m_dataSet[i], calc, false, mean)) << "Error During Processing";
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Squeeze Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
EXPECT_TRUE(Geometry::SqueezeUpperTriangle(m_dataSet[i], calc, false)) << "Error During Processing";
|
||||
EXPECT_TRUE(isAlmostEqual(refDiag[i], calc)) << ErrorMsg("Squeeze Sample [" + std::to_string(i) + "]", refDiag[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Featurization, UnSqueeze)
|
||||
{
|
||||
const std::vector<Eigen::RowVectorXd> ref = InitFeaturization::Squeeze::Reference();
|
||||
const std::vector<Eigen::RowVectorXd> refDiag = InitFeaturization::SqueezeDiag::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Riemann::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
Eigen::MatrixXd calc;
|
||||
EXPECT_TRUE(Geometry::UnFeaturization(ref[i], calc, false, mean)) << "Error During Processing";
|
||||
EXPECT_TRUE(isAlmostEqual(m_dataSet[i], calc)) << ErrorMsg("UnSqueeze Sample [" + std::to_string(i) + "]", m_dataSet[i], calc);
|
||||
EXPECT_TRUE(Geometry::UnSqueezeUpperTriangle(refDiag[i], calc, false)) << "Error During Processing";
|
||||
EXPECT_TRUE(isAlmostEqual(m_dataSet[i], calc)) << ErrorMsg("UnSqueeze Sample [" + std::to_string(i) + "]", m_dataSet[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,84 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Geodesics.hpp
|
||||
/// \brief Tests for Geodesic Functions.
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 1.0.
|
||||
/// \date 09/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "misc.hpp"
|
||||
#include "init.hpp"
|
||||
|
||||
#include <geometry/Geodesic.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Geodesic : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<Eigen::MatrixXd> m_dataSet;
|
||||
|
||||
void SetUp() override { m_dataSet = Geometry::Vector2DTo1D(InitCovariance::LWF::Reference()); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Geodesic, Euclidian)
|
||||
{
|
||||
const std::vector<Eigen::MatrixXd> ref = InitGeodesics::Euclidian::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Euclidian::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
Eigen::MatrixXd calc;
|
||||
Geodesic(mean, m_dataSet[i], calc, Geometry::EMetric::Euclidian, 0.5);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Geodesic Euclidian Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Geodesic, LogEuclidian)
|
||||
{
|
||||
const std::vector<Eigen::MatrixXd> ref = InitGeodesics::LogEuclidian::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::LogEuclidian::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
Eigen::MatrixXd calc;
|
||||
Geodesic(mean, m_dataSet[i], calc, Geometry::EMetric::LogEuclidian, 0.5);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Geodesic LogEuclidian Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Geodesic, Riemann)
|
||||
{
|
||||
const std::vector<Eigen::MatrixXd> ref = InitGeodesics::Riemann::Reference();
|
||||
const Eigen::MatrixXd mean = InitMeans::Riemann::Reference();
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
Eigen::MatrixXd calc;
|
||||
Geodesic(mean, m_dataSet[i], calc, Geometry::EMetric::Riemann, 0.5);
|
||||
EXPECT_TRUE(isAlmostEqual(ref[i], calc)) << ErrorMsg("Geodesic Riemann Sample [" + std::to_string(i) + "]", ref[i], calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Geodesic, Identity)
|
||||
{
|
||||
const Eigen::MatrixXd mean = InitMeans::Riemann::Reference(), ref = Eigen::MatrixXd::Identity(NB_CHAN, NB_CHAN);
|
||||
for (size_t i = 0; i < m_dataSet.size(); ++i)
|
||||
{
|
||||
Eigen::MatrixXd calc;
|
||||
Geodesic(mean, m_dataSet[i], calc, Geometry::EMetric::Identity, 0.5);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Geodesic Identity Sample [" + std::to_string(i) + "]", ref, calc);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,296 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_MatrixClassifier.hpp
|
||||
/// \brief Tests for Matrix Classifiers.
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 1.0.
|
||||
/// \date 09/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
/// \remarks
|
||||
/// - For this tests I compare the results with the <a href="https://github.com/alexandrebarachant/pyRiemann">pyRiemann</a> library (<a href="https://github.com/alexandrebarachant/pyRiemann/blob/master/LICENSE">License</a>) or <a href="http://scikit-learn.org">sklearn</a> if pyRiemman just redirect the function.
|
||||
/// - For the adaptation Classification tests I compare the results with the <a href="https://github.com/alexandrebarachant/covariancetoolbox">covariancetoolbox</a> Matlab library (<a href="https://github.com/alexandrebarachant/covariancetoolbox/blob/master/COPYING">License</a>).
|
||||
/// - The Matlab toolbox is older and Riemannian mean estimation is diff�rent the test are adapted to switch between the two library
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "misc.hpp"
|
||||
#include "init.hpp"
|
||||
|
||||
#include <geometry/classifier/CMatrixClassifierMDM.hpp>
|
||||
#include <geometry/classifier/CMatrixClassifierMDMRebias.hpp>
|
||||
#include <geometry/classifier/CMatrixClassifierFgMDM.hpp>
|
||||
#include <geometry/classifier/CMatrixClassifierFgMDMRT.hpp>
|
||||
#include <geometry/classifier/CMatrixClassifierFgMDMRTRebias.hpp>
|
||||
|
||||
static const std::vector<std::vector<double>> EMPTY_DIST;
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
static void TestClassify(Geometry::IMatrixClassifier& calc, const std::vector<std::vector<Eigen::MatrixXd>>& dataset, const std::vector<size_t>& prediction,
|
||||
const std::vector<std::vector<double>>& predictionDistance, const Geometry::EAdaptations& adapt)
|
||||
{
|
||||
Eigen::MatrixXd result = Eigen::MatrixXd::Zero(NB_CLASS, NB_CLASS);
|
||||
size_t idx = 0;
|
||||
for (size_t k = 0; k < dataset.size(); ++k)
|
||||
{
|
||||
for (size_t i = 0; i < dataset[k].size(); ++i)
|
||||
{
|
||||
const std::string text = "sample [" + std::to_string(k) + "][" + std::to_string(i) + "]";
|
||||
size_t classid = 0;
|
||||
std::vector<double> distance, probability;
|
||||
EXPECT_TRUE(calc.classify(dataset[k][i], classid, distance, probability, adapt, k)) << "Error during Classify " << text;
|
||||
if (idx < prediction.size()) { EXPECT_TRUE(prediction[idx] == classid) << ErrorMsg("Prediction " + text, prediction[idx], classid); }
|
||||
if (idx < predictionDistance.size())
|
||||
{
|
||||
EXPECT_TRUE(isAlmostEqual(predictionDistance[idx], distance)) << ErrorMsg("Prediction Distance " + text, predictionDistance[idx], distance);
|
||||
}
|
||||
idx++;
|
||||
result(k, classid)++;
|
||||
}
|
||||
}
|
||||
std::cout << "***** Classifier : *****" << std::endl << calc << std::endl << "***** Result : *****" << std::endl << result << std::endl;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_MatrixClassifier : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<std::vector<Eigen::MatrixXd>> m_dataSet;
|
||||
|
||||
void SetUp() override { m_dataSet = InitCovariance::LWF::Reference(); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Train)
|
||||
{
|
||||
const Geometry::CMatrixClassifierMDM ref = InitMatrixClassif::MDM::Reference();
|
||||
Geometry::CMatrixClassifierMDM calc;
|
||||
EXPECT_TRUE(calc.train(m_dataSet)) << "Error during Training : " << std::endl << calc << std::endl;
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Train", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Classifify)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDM calc = InitMatrixClassif::MDM::ReferenceMatlab();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::MDM::Prediction(), InitMatrixClassif::MDM::PredictionDistance(), Geometry::EAdaptations::None);
|
||||
const Geometry::CMatrixClassifierMDM ref = InitMatrixClassif::MDM::ReferenceMatlab(); // No Change
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Classify Change without adaptation mode", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Classifify_Adapt_Supervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDM calc = InitMatrixClassif::MDM::ReferenceMatlab();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::MDM::PredictionSupervised(), InitMatrixClassif::MDM::PredictionDistanceSupervised(),
|
||||
Geometry::EAdaptations::Supervised);
|
||||
const Geometry::CMatrixClassifierMDM ref = InitMatrixClassif::MDM::AfterSupervised();
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Adapt Classify after Supervised adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Classifify_Adapt_Unsupervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDM calc = InitMatrixClassif::MDM::ReferenceMatlab();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::MDM::PredictionUnSupervised(), InitMatrixClassif::MDM::PredictionDistanceUnSupervised(),
|
||||
Geometry::EAdaptations::Unsupervised);
|
||||
const Geometry::CMatrixClassifierMDM ref = InitMatrixClassif::MDM::AfterUnSupervised();
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Adapt Classify after Unsupervised adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Save)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDM calc;
|
||||
const Geometry::CMatrixClassifierMDM ref = InitMatrixClassif::MDM::Reference();
|
||||
EXPECT_TRUE(ref.saveXML("test_MDM_Save.xml")) << "Error during Saving : " << std::endl << ref << std::endl;
|
||||
EXPECT_TRUE(calc.loadXML("test_MDM_Save.xml")) << "Error during Loading : " << std::endl << calc << std::endl;
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Save", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDMRT_Train)
|
||||
{
|
||||
const Geometry::CMatrixClassifierFgMDMRT ref = InitMatrixClassif::FgMDMRT::Reference();
|
||||
Geometry::CMatrixClassifierFgMDMRT calc;
|
||||
EXPECT_TRUE(calc.train(m_dataSet)) << "Error during Training : " << std::endl << calc << std::endl;
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Train", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDMRT_Classifify)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRT calc = InitMatrixClassif::FgMDMRT::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::FgMDMRT::Prediction(), InitMatrixClassif::FgMDMRT::PredictionDistance(), Geometry::EAdaptations::None);
|
||||
const Geometry::CMatrixClassifierFgMDMRT ref = InitMatrixClassif::FgMDMRT::Reference();
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Classify Change without adaptation mode", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDMRT_Classifify_Adapt_Supervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRT calc = InitMatrixClassif::FgMDMRT::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::FgMDMRT::PredictionSupervised(), EMPTY_DIST, Geometry::EAdaptations::Supervised);
|
||||
//const Geometry::CMatrixClassifierFgMDMRT ref = InitMatrixClassif::FgMDMRT::AfterSupervised();
|
||||
//EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Adapt Classify after Supervised RT adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDMRT_Classifify_Adapt_Unsupervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRT calc(InitMatrixClassif::FgMDMRT::Reference());
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::FgMDMRT::PredictionUnSupervised(), EMPTY_DIST, Geometry::EAdaptations::Unsupervised);
|
||||
//const Geometry::CMatrixClassifierFgMDMRT ref = InitMatrixClassif::FgMDMRT::AfterUnSupervised();
|
||||
//EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Adapt Classify after Unsupervised RT adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDMRT_Save)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRT calc;
|
||||
const Geometry::CMatrixClassifierFgMDMRT ref = InitMatrixClassif::FgMDMRT::Reference();
|
||||
EXPECT_TRUE(ref.saveXML("test_FgMDM_Save.xml")) << "Error during Saving : " << std::endl << ref << std::endl;
|
||||
EXPECT_TRUE(calc.loadXML("test_FgMDM_Save.xml")) << "Error during Loading : " << std::endl << calc << std::endl;
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Save", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDM_Classifify_Adapt_Supervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDM calc = InitMatrixClassif::FgMDM::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::FgMDM::PredictionSupervised(), EMPTY_DIST, Geometry::EAdaptations::Supervised);
|
||||
//const Geometry::CMatrixClassifierFgMDM ref = InitMatrixClassif::FgMDM::AfterSupervised();
|
||||
//EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Adapt Classify after Supervised adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDM_Classifify_Adapt_Unsupervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDM calc = InitMatrixClassif::FgMDM::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::FgMDM::PredictionUnSupervised(), EMPTY_DIST, Geometry::EAdaptations::Unsupervised);
|
||||
//const Geometry::CMatrixClassifierFgMDM ref = InitMatrixClassif::FgMDM::AfterUnSupervised();
|
||||
//EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Adapt Classify after Unsupervised adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Rebias_Train)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDMRebias calc;
|
||||
EXPECT_TRUE(calc.train(m_dataSet)) << "Error during Training : " << std::endl << calc << std::endl;
|
||||
//const Geometry::CMatrixClassifierMDMRebias ref = InitMatrixClassif::MDMRebias::Reference();
|
||||
//EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Rebias Train", ref, calc); // The mean method is different in matlab toolbox and python toolbox
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Rebias_Classifify)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDMRebias calc = InitMatrixClassif::MDMRebias::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::MDMRebias::Prediction(), InitMatrixClassif::MDMRebias::PredictionDistance(), Geometry::EAdaptations::None);
|
||||
const Geometry::CMatrixClassifierMDMRebias ref = InitMatrixClassif::MDMRebias::After(); // No Class change but Rebias yes
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Rebias Classify Change without adaptation mode", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Rebias_Classifify_Adapt_Supervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDMRebias calc = InitMatrixClassif::MDMRebias::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::MDMRebias::PredictionSupervised(), InitMatrixClassif::MDMRebias::PredictionDistanceSupervised(),
|
||||
Geometry::EAdaptations::Supervised);
|
||||
const Geometry::CMatrixClassifierMDMRebias ref = InitMatrixClassif::MDMRebias::AfterSupervised();
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Rebias Adapt Classify after Supervised adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Rebias_Classifify_Adapt_Unsupervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDMRebias calc = InitMatrixClassif::MDMRebias::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::MDMRebias::PredictionUnSupervised(), InitMatrixClassif::MDMRebias::PredictionDistanceUnSupervised(),
|
||||
Geometry::EAdaptations::Unsupervised);
|
||||
const Geometry::CMatrixClassifierMDMRebias ref = InitMatrixClassif::MDMRebias::AfterUnSupervised();
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Rebias Adapt Classify after Unsupervised adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, MDM_Rebias_Save)
|
||||
{
|
||||
Geometry::CMatrixClassifierMDMRebias calc;
|
||||
const Geometry::CMatrixClassifierMDMRebias ref = InitMatrixClassif::MDMRebias::Reference();
|
||||
EXPECT_TRUE(ref.saveXML("test_MDM_Rebias_Save.xml")) << "Error during Saving : " << std::endl << ref << std::endl;
|
||||
EXPECT_TRUE(calc.loadXML("test_MDM_Rebias_Save.xml")) << "Error during Loading : " << std::endl << calc << std::endl;
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("MDM Rebias Save", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDM_RT_Rebias_Train)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRTRebias calc;
|
||||
EXPECT_TRUE(calc.train(m_dataSet)) << "Error during Training : " << std::endl << calc << std::endl;
|
||||
const Geometry::CMatrixClassifierFgMDMRTRebias ref = InitMatrixClassif::FgMDMRTRebias::Reference();
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Rebias Train", ref, calc); // The mean method is different in matlab toolbox and python toolbox
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDM_RT_Rebias_Save)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRTRebias calc;
|
||||
const Geometry::CMatrixClassifierFgMDMRTRebias ref = InitMatrixClassif::FgMDMRTRebias::Reference();
|
||||
EXPECT_TRUE(ref.saveXML("test_FgMDM_Rebias_Save.xml")) << "Error during Saving : " << std::endl << ref << std::endl;
|
||||
EXPECT_TRUE(calc.loadXML("test_FgMDM_Rebias_Save.xml")) << "Error during Loading : " << std::endl << calc << std::endl;
|
||||
EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Rebias Save", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDM_RT_Rebias_Classifify)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRTRebias calc = InitMatrixClassif::FgMDMRTRebias::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::FgMDMRTRebias::Prediction(), EMPTY_DIST, Geometry::EAdaptations::None);
|
||||
//const Geometry::CMatrixClassifierFgMDMRTRebias ref = InitMatrixClassif::FgMDMRTRebias::After();
|
||||
//EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Rebias Classify Change without adaptation mode", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDM_RT_Rebias_Classifify_Adapt_Supervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRTRebias calc = InitMatrixClassif::FgMDMRTRebias::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::FgMDMRTRebias::PredictionSupervised(), EMPTY_DIST, Geometry::EAdaptations::Supervised);
|
||||
//const Geometry::CMatrixClassifierFgMDMRTRebias ref = InitMatrixClassif::FgMDMRTRebias::AfterSupervised();
|
||||
//EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Rebias Adapt Classify after Supervised adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_MatrixClassifier, FgMDM_RT_Rebias_Classifify_Adapt_Unsupervised)
|
||||
{
|
||||
Geometry::CMatrixClassifierFgMDMRTRebias calc = InitMatrixClassif::FgMDMRTRebias::Reference();
|
||||
TestClassify(calc, m_dataSet, InitMatrixClassif::FgMDMRTRebias::PredictionUnSupervised(), EMPTY_DIST, Geometry::EAdaptations::Unsupervised);
|
||||
//const Geometry::CMatrixClassifierFgMDMRTRebias ref = InitMatrixClassif::FgMDMRTRebias::AfterUnSupervised();
|
||||
//EXPECT_TRUE(ref == calc) << ErrorMsg("FgMDM Rebias Adapt Classify after Unsupervised adaptation", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,135 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Mean.hpp
|
||||
/// \brief Tests for Mean Functions.
|
||||
/// \author Thibaut Monseigne (Inria).
|
||||
/// \version 1.0.
|
||||
/// \date 09/01/2019.
|
||||
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "misc.hpp"
|
||||
#include "init.hpp"
|
||||
|
||||
#include <geometry/Mean.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Means : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<Eigen::MatrixXd> m_dataSet;
|
||||
|
||||
void SetUp() override { m_dataSet = Geometry::Vector2DTo1D(InitCovariance::LWF::Reference()); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, BadInput)
|
||||
{
|
||||
std::vector<Eigen::MatrixXd> bad;
|
||||
Eigen::MatrixXd calc;
|
||||
EXPECT_FALSE(Mean(bad, calc, Geometry::EMetric::Riemann));
|
||||
bad.emplace_back(Eigen::MatrixXd::Zero(1, 2));
|
||||
bad.emplace_back(Eigen::MatrixXd::Zero(1, 2));
|
||||
EXPECT_FALSE(Mean(bad, calc, Geometry::EMetric::Riemann));
|
||||
bad.emplace_back(Eigen::MatrixXd::Zero(2, 2));
|
||||
EXPECT_FALSE(Mean(bad, calc, Geometry::EMetric::Riemann));
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, Euclidian)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitMeans::Euclidian::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::Euclidian);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix Euclidian", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, LogEuclidian)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitMeans::LogEuclidian::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::LogEuclidian);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix LogEuclidian", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, Riemann)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitMeans::Riemann::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::Riemann);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix Riemann", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, LogDet)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitMeans::LogDeterminant::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::LogDet);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix LogDet", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, Kullback)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitMeans::Kullback::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::Kullback);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix Kullback", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, Wasserstein)
|
||||
{
|
||||
std::cout << "Precision Error" << std::endl;
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::Wasserstein);
|
||||
//const Eigen::MatrixXd ref = InitMeans::Wasserstein::Reference();
|
||||
//EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix Wasserstein", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, ALE)
|
||||
{
|
||||
std::cout << "Not implemented" << std::endl;
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::ALE);
|
||||
//const Eigen::MatrixXd ref = InitMeans::ALE::Reference();
|
||||
//EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix ALE", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, Harmonic)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitMeans::Harmonic::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::Harmonic);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix Harmonic", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Means, Identity)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitMeans::Identity::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
Mean(m_dataSet, calc, Geometry::EMetric::Identity);
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Mean Matrix Identity", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,86 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Median.hpp
|
||||
/// \brief Tests for Median 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 We use the EEglab Matlab plugin to compare result for validation
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "init.hpp"
|
||||
#include "misc.hpp"
|
||||
|
||||
#include <geometry/Basics.hpp>
|
||||
#include <geometry/Median.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Median : public testing::Test
|
||||
{
|
||||
protected:
|
||||
std::vector<Eigen::MatrixXd> m_dataSet;
|
||||
|
||||
void SetUp() override { m_dataSet = Geometry::Vector2DTo1D(InitCovariance::LWF::Reference()); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Median, Simple_Median)
|
||||
{
|
||||
std::vector<double> v{ 5, 6, 4, 3, 2, 6, 7, 9, 3 };
|
||||
double calc = Geometry::Median(v);
|
||||
EXPECT_EQ(calc, 5);
|
||||
|
||||
v.pop_back();
|
||||
calc = Geometry::Median(v);
|
||||
EXPECT_EQ(calc, 5.5);
|
||||
|
||||
Eigen::MatrixXd m(3, 3);
|
||||
m << 5, 6, 4, 3, 2, 6, 7, 9, 3;
|
||||
calc = Geometry::Median(m);
|
||||
EXPECT_EQ(calc, 5);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Median, Euclidian)
|
||||
{
|
||||
Eigen::MatrixXd calc;
|
||||
Eigen::MatrixXd ref(3, 3);
|
||||
ref << 1.749537973777478, 0.002960131606861, 0.020507254841909,
|
||||
0.002960131606861, 1.754563395557952, 0.043042786354499,
|
||||
0.020507254841909, 0.043042786354499, 1.057672472691352;
|
||||
EXPECT_TRUE(Geometry::Median(m_dataSet, calc, 0.0001, 50, Geometry::EMetric::Euclidian)) << "Error During Median Computing";
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Euclidian Median of Dataset", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Median, Riemann)
|
||||
{
|
||||
Eigen::MatrixXd calc;
|
||||
Eigen::MatrixXd ref(3, 3);
|
||||
ref << 1.851330747504982, 0.002002346316770, 0.022122030618131,
|
||||
0.002002346316770, 1.644242996651016, 0.033655563302757,
|
||||
0.022122030618131, 0.033655563302757, 0.851184143800763;
|
||||
EXPECT_TRUE(Geometry::Median(m_dataSet, calc, 0.0001, 50, Geometry::EMetric::Riemann)) << "Error During Median Computes";
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Riemann Median of Dataset", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Median, Identity)
|
||||
{
|
||||
const Eigen::MatrixXd ref = InitMeans::Identity::Reference();
|
||||
Eigen::MatrixXd calc;
|
||||
EXPECT_TRUE(Geometry::Median(m_dataSet, calc, 0.0001, 50, Geometry::EMetric::Identity)) << "Error During Median Computes";
|
||||
EXPECT_TRUE(isAlmostEqual(ref, calc)) << ErrorMsg("Identity Median of Dataset", ref, calc);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,158 @@
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
///
|
||||
/// \file test_Misc.hpp
|
||||
/// \brief Tests for Misc Functions of module.
|
||||
/// \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 We use the EEglab Matlab plugin to compare result for validation
|
||||
///
|
||||
///-------------------------------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "init.hpp"
|
||||
#include "misc.hpp"
|
||||
|
||||
#include <geometry/Misc.hpp>
|
||||
#include <geometry/Basics.hpp>
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
class Tests_Misc : public testing::Test
|
||||
{
|
||||
//protected:
|
||||
// std::vector<Eigen::MatrixXd> m_dataSet;
|
||||
//
|
||||
// void SetUp() override { m_dataSet = Vector2DTo1D(InitCovariance::LWF::Reference()); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Misc, Double_Range)
|
||||
{
|
||||
const std::vector<double> calc1 = Geometry::doubleRange(0, 10, 2), calc2 = Geometry::doubleRange(0, 10, 2, false),
|
||||
calc3 = Geometry::doubleRange(0.15, 3.05, 0.5), calc4 = Geometry::doubleRange(0.15, 3.05, 0.5, false),
|
||||
ref1 = { 0, 2, 4, 6, 8, 10 }, ref2 = { 0, 2, 4, 6, 8 },
|
||||
ref3 = { 0.15, 0.65, 1.15, 1.65, 2.15, 2.65 }, ref4 = { 0.15, 0.65, 1.15, 1.65, 2.15, 2.65 };
|
||||
|
||||
EXPECT_TRUE(isAlmostEqual(ref1, calc1)) << ErrorMsg("Double closed Range with integer value", ref1, calc1);
|
||||
EXPECT_TRUE(isAlmostEqual(ref2, calc2)) << ErrorMsg("Double opened Range with integer value", ref2, calc2);
|
||||
EXPECT_TRUE(isAlmostEqual(ref3, calc3)) << ErrorMsg("Double closed Range with double value", ref3, calc3);
|
||||
EXPECT_TRUE(isAlmostEqual(ref4, calc4)) << ErrorMsg("Double opened Range with double value", ref4, calc4);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Misc, Round_Index_Range)
|
||||
{
|
||||
const std::vector<size_t> calc1 = Geometry::RoundIndexRange(0, 10, 2), calc2 = Geometry::RoundIndexRange(0, 10, 2, false),
|
||||
calc3 = Geometry::RoundIndexRange(0.15, 3.15, 0.2), calc4 = Geometry::RoundIndexRange(0.15, 3.05, 0.2, false, false),
|
||||
ref1 = { 0, 2, 4, 6, 8, 10 }, ref2 = { 0, 2, 4, 6, 8 },
|
||||
ref3 = { 0, 1, 2, 3 }, ref4 = { 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3 };
|
||||
|
||||
EXPECT_TRUE(isAlmostEqual(ref1, calc1)) << ErrorMsg("Round Index closed Range with integer value", ref1, calc1);
|
||||
EXPECT_TRUE(isAlmostEqual(ref2, calc2)) << ErrorMsg("Round Index opened Range with integer value", ref2, calc2);
|
||||
EXPECT_TRUE(isAlmostEqual(ref3, calc3)) << ErrorMsg("Round Index closed Range with double value", ref3, calc3);
|
||||
EXPECT_TRUE(isAlmostEqual(ref4, calc4)) << ErrorMsg("Round Index opened Range with double value", ref4, calc4);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Misc, Bin_Histogramm)
|
||||
{
|
||||
//========== Create Dataset ==========
|
||||
const std::vector<Eigen::MatrixXd> matrices = Geometry::Vector2DTo1D(InitDataset::Dataset());
|
||||
std::vector<std::vector<double>> dataset(NB_CHAN);
|
||||
// Transform Dataset to vector per channel
|
||||
for (size_t i = 0; i < NB_CHAN; ++i) { dataset[i].reserve(NB_SAMPLE * matrices.size()); }
|
||||
for (const auto& m : matrices) { for (size_t i = 0; i < NB_CHAN; ++i) { for (size_t j = 0; j < NB_SAMPLE; ++j) { dataset[i].push_back(m(i, j)); } } }
|
||||
|
||||
// Sort and remove first (to begin by 0)
|
||||
for (auto& d : dataset)
|
||||
{
|
||||
std::sort(d.begin(), d.end());
|
||||
const auto first = d[0];
|
||||
for (auto& e : d) { e -= first; }
|
||||
}
|
||||
|
||||
//========== Create Ref ==========
|
||||
const std::vector<std::vector<size_t>> ref =
|
||||
{
|
||||
{ 12, 10, 0, 15, 15, 0, 6, 12, 0, 11, 11, 0, 11, 14, 3 },
|
||||
{ 17, 0, 26, 0, 0, 18, 0, 28, 0, 0, 15, 0, 7, 0, 9 },
|
||||
{ 36, 0, 0, 34, 0, 0, 0, 0, 0, 15, 0, 0, 20, 0, 15 }
|
||||
};
|
||||
|
||||
//========== Test ==========
|
||||
for (size_t i = 0; i < NB_CHAN; ++i)
|
||||
{
|
||||
const std::vector<size_t> hist = Geometry::BinHist(dataset[i], 15);
|
||||
EXPECT_TRUE(isAlmostEqual(hist, ref[i])) << ErrorMsg("Bin Histogramm", hist, ref[i]);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Misc, Fit_Distribution)
|
||||
{
|
||||
const std::vector<Eigen::MatrixXd> matrices = Geometry::Vector2DTo1D(InitDataset::Dataset());
|
||||
std::vector<std::vector<double>> dataset(NB_CHAN);
|
||||
// Transform Dataset to vector per channel
|
||||
for (size_t i = 0; i < NB_CHAN; ++i) { dataset[i].reserve(NB_SAMPLE * matrices.size()); }
|
||||
for (const auto& m : matrices) { for (size_t i = 0; i < NB_CHAN; ++i) { for (size_t j = 0; j < NB_SAMPLE; ++j) { dataset[i].push_back(m(i, j)); } } }
|
||||
|
||||
// Begin Fit Distribution
|
||||
std::vector<double> mu(NB_CHAN), sigma(NB_CHAN);
|
||||
const std::vector<double> refMu = { -0.840258269642149, - 2.10169835819046, 0.898301641809541 },
|
||||
refSigma = { 2.76541902273525, 0.435493584265319, 0.435493584265319 };
|
||||
|
||||
for (size_t i = 0; i < NB_CHAN; ++i)
|
||||
{
|
||||
Geometry::FitDistribution(dataset[i], mu[i], sigma[i]);
|
||||
EXPECT_TRUE(isAlmostEqual(mu[i], refMu[i])) << ErrorMsg("Fit Distribution Mu", mu[i], refMu[i]);
|
||||
EXPECT_TRUE(isAlmostEqual(sigma[i], refSigma[i])) << ErrorMsg("Fit Distribution Sigma", sigma[i], refSigma[i]);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Misc, Sorted_Eigen_Vector_Euclidian)
|
||||
{
|
||||
std::vector<Eigen::MatrixXd> matrices = Geometry::Vector2DTo1D(InitCovariance::LWF::Reference());
|
||||
const size_t n = matrices.size();
|
||||
std::vector<Eigen::MatrixXd> vectors = InitEigenVector::Euclidian::Vectors();
|
||||
std::vector<std::vector<double>> values = InitEigenVector::Euclidian::Values();
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
Eigen::MatrixXd vec;
|
||||
std::vector<double> val;
|
||||
Geometry::sortedEigenVector(matrices[i], vec, val, Geometry::EMetric::Euclidian);
|
||||
EXPECT_TRUE(isAlmostEqual(vectors[i], vec)) << ErrorMsg("Eigen Vector sample " + std::to_string(i) + " : ", vectors[i], vec);
|
||||
EXPECT_TRUE(isAlmostEqual(values[i], val)) << ErrorMsg("Eigen Value sample " + std::to_string(i) + " : ", values[i], val);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
TEST_F(Tests_Misc, Sorted_Eigen_Vector_Riemann)
|
||||
{
|
||||
std::cout << "Not implemented" << std::endl;
|
||||
std::vector<Eigen::MatrixXd> matrices = Geometry::Vector2DTo1D(InitCovariance::LWF::Reference());
|
||||
const size_t n = matrices.size();
|
||||
//std::vector<Eigen::MatrixXd> vectors = InitEigenVector::Riemann::Vectors();
|
||||
//std::vector<std::vector<double>> values = InitEigenVector::Riemann::Values();
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
Eigen::MatrixXd vec;
|
||||
std::vector<double> val;
|
||||
Geometry::sortedEigenVector(matrices[i], vec, val, Geometry::EMetric::Riemann);
|
||||
//EXPECT_TRUE(isAlmostEqual(vectors[i], vec)) << ErrorMsg("Eigen Vector sample " + std::to_string(i) + " : ", vectors[i], vec);
|
||||
//EXPECT_TRUE(isAlmostEqual(values[i], val)) << ErrorMsg("Eigen Value sample " + std::to_string(i) + " : ", values[i], val);
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
Reference in New Issue
Block a user