diff --git a/src/mlpack/methods/gmm/CMakeLists.txt b/src/mlpack/methods/gmm/CMakeLists.txt index de08439a10..a9305cc654 100644 --- a/src/mlpack/methods/gmm/CMakeLists.txt +++ b/src/mlpack/methods/gmm/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES no_constraint.hpp positive_definite_constraint.hpp diagonal_constraint.hpp + eigenvalue_ratio_constraint.hpp ) # Add directory name to sources. diff --git a/src/mlpack/methods/gmm/eigenvalue_ratio_constraint.hpp b/src/mlpack/methods/gmm/eigenvalue_ratio_constraint.hpp new file mode 100644 index 0000000000..813617025c --- /dev/null +++ b/src/mlpack/methods/gmm/eigenvalue_ratio_constraint.hpp @@ -0,0 +1,79 @@ +/** + * @file eigenvalue_ratio_constraint.hpp + * @author Ryan Curtin + * + * Constrain a covariance matrix to have a certain ratio of eigenvalues. + */ +#ifndef __MLPACK_METHODS_GMM_EIGENVALUE_RATIO_CONSTRAINT_HPP +#define __MLPACK_METHODS_GMM_EIGENVALUE_RATIO_CONSTRAINT_HPP + +#include + +namespace mlpack { +namespace gmm { + +/** + * Given a vector of eigenvalue ratios, ensure that the covariance matrix always + * has those eigenvalue ratios. When you create this object, make sure that the + * vector of ratios that you pass does not go out of scope, because this object + * holds a reference to that vector instead of copying it. + */ +class EigenvalueRatioConstraint +{ + public: + /** + * Create the EigenvalueRatioConstraint object with the given vector of + * eigenvalue ratios. These ratios are with respect to the first eigenvalue, + * which is the largest eigenvalue, so the first element of the vector should + * be 1. In addition, all other elements should be less than or equal to 1. + */ + EigenvalueRatioConstraint(const arma::vec& ratios) : + ratios(ratios) + { + // Check validity of ratios. + if (std::abs(ratios[0] - 1.0) > 1e-20) + Log::Fatal << "EigenvalueRatioConstraint::EigenvalueRatioConstraint(): " + << "first element of ratio vector is not 1.0!" << std::endl; + + for (size_t i = 1; i < ratios.n_elem; ++i) + { + if (ratios[i] > 1.0) + Log::Fatal << "EigenvalueRatioConstraint::EigenvalueRatioConstraint(): " + << "element " << i << " of ratio vector is greater than 1.0!" + << std::endl; + if (ratios[i] < 0.0) + Log::Warn << "EigenvalueRatioConstraint::EigenvalueRatioConstraint(): " + << "element " << i << " of ratio vectors is negative and will " + << "probably cause the covariance to be non-invertible..." + << std::endl; + } + + /** + * Apply the eigenvalue ratio constraint to the given covariance matrix. + */ + void ApplyConstraint(arma::mat& covariance) const + { + // Eigendecompose the matrix. + arma::vec eigenvalues; + arma::mat eigenvectors; + arma::eig_sym(eigenvalues, eigenvectors, covariance); + + // Change the eigenvalues to what we are forcing them to be. There + // shouldn't be any negative eigenvalues anyway, so it doesn't matter if we + // are suddenly forcing them to be positive. If the first eigenvalue is + // negative, well, there are going to be some problems later... + eigenvalues = (eigenvalues[0] * ratios); + + // Reassemble the matrix. + covariance = eigenvectors * arma::diagmat(eigenvalues) * eigenvectors.t(); + } + + private: + //! Ratios for eigenvalues. + const arma::vec& ratios; +}; + +}; // namespace gmm +}; // namespace mlpack + +#endif