include/maths/common/CLinearAlgebraEigen.h (539 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the following additional limitation. Functionality enabled by the * files subject to the Elastic License 2.0 may only be used in production when * invoked by an Elasticsearch process with a license key installed that permits * use of machine learning features. You may not use this file except in * compliance with the Elastic License 2.0 and the foregoing additional * limitation. */ #ifndef INCLUDED_ml_maths_common_CLinearAlgebraEigen_h #define INCLUDED_ml_maths_common_CLinearAlgebraEigen_h #include <core/CMemoryUsage.h> #include <core/CPersistUtils.h> #include <core/CSmallVectorFwd.h> #include <core/CStatePersistInserter.h> #include <core/CStateRestoreTraverser.h> #include <core/RestoreMacros.h> #include <maths/common/CChecksum.h> #include <maths/common/CLinearAlgebra.h> #include <maths/common/CLinearAlgebraFwd.h> #include <Eigen/Cholesky> #include <Eigen/Core> #include <Eigen/LU> #include <Eigen/QR> #include <Eigen/SVD> #include <Eigen/SparseCore> #include <algorithm> #include <iterator> #include <type_traits> namespace Eigen { #define LESS_OR_GREATER(l, r) \ if (l < r) { \ return true; \ } else if (r > l) { \ return false; \ } //! Less than on Eigen sparse matrix. template<typename SCALAR, int FLAGS, typename STORAGE_INDEX> bool operator<(const SparseMatrix<SCALAR, FLAGS, STORAGE_INDEX>& lhs, const SparseMatrix<SCALAR, FLAGS, STORAGE_INDEX>& rhs) { LESS_OR_GREATER(lhs.rows(), rhs.rows()) LESS_OR_GREATER(lhs.cols(), rhs.cols()) for (STORAGE_INDEX i = 0; i < lhs.rows(); ++i) { for (STORAGE_INDEX j = 0; j < lhs.cols(); ++j) { LESS_OR_GREATER(lhs(i, j), rhs(i, j)) } } return false; } //! Less than on Eigen sparse vector. template<typename SCALAR, int FLAGS, typename STORAGE_INDEX> bool operator<(const SparseVector<SCALAR, FLAGS, STORAGE_INDEX>& lhs, const SparseVector<SCALAR, FLAGS, STORAGE_INDEX>& rhs) { LESS_OR_GREATER(lhs.size(), rhs.size()) for (STORAGE_INDEX i = 0; i < lhs.size(); ++i) { LESS_OR_GREATER(lhs(i), rhs(i)) } return false; } //! Less than on Eigen dense matrix. template<typename SCALAR, int ROWS, int COLS, int OPTIONS, int MAX_ROWS, int MAX_COLS> bool operator<(const Matrix<SCALAR, ROWS, COLS, OPTIONS, MAX_ROWS, MAX_COLS>& lhs, const Matrix<SCALAR, ROWS, COLS, OPTIONS, MAX_ROWS, MAX_COLS>& rhs) { LESS_OR_GREATER(lhs.rows(), rhs.rows()) LESS_OR_GREATER(lhs.cols(), rhs.cols()) return std::lexicographical_compare(lhs.data(), lhs.data() + lhs.size(), rhs.data(), rhs.data() + rhs.size()); } //! Less than on an Eigen memory mapped matrix. template<typename PLAIN_OBJECT_TYPE, int OPTIONS, typename STRIDE_TYPE> bool operator<(const Map<PLAIN_OBJECT_TYPE, OPTIONS, STRIDE_TYPE>& lhs, const Map<PLAIN_OBJECT_TYPE, OPTIONS, STRIDE_TYPE>& rhs) { LESS_OR_GREATER(lhs.rows(), rhs.rows()) LESS_OR_GREATER(lhs.cols(), rhs.cols()) return std::lexicographical_compare(lhs.data(), lhs.data() + lhs.size(), rhs.data(), rhs.data() + rhs.size()); } #undef LESS_OR_GREATER //! Free swap picked up by std:: algorithms etc. template<typename SCALAR, int FLAGS, typename STORAGE_INDEX> void swap(SparseVector<SCALAR, FLAGS, STORAGE_INDEX>& lhs, SparseVector<SCALAR, FLAGS, STORAGE_INDEX>& rhs) { lhs.swap(rhs); } //! Free swap picked up by std:: algorithms etc. template<typename SCALAR, int ROWS, int COLS, int OPTIONS, int MAX_ROWS, int MAX_COLS> void swap(Matrix<SCALAR, ROWS, COLS, OPTIONS, MAX_ROWS, MAX_COLS>& lhs, Matrix<SCALAR, ROWS, COLS, OPTIONS, MAX_ROWS, MAX_COLS>& rhs) { lhs.swap(rhs); } } namespace ml { namespace maths { namespace common { template<typename VECTOR, typename ANNOTATION> class CAnnotatedVector; //! Rename to follow our conventions and add to ml::maths. template<typename SCALAR, int FLAGS = 0> using CSparseMatrix = Eigen::SparseMatrix<SCALAR, FLAGS, std::ptrdiff_t>; //! The type of an element of a sparse matrix in coordinate form. template<typename SCALAR> using CSparseMatrixElement = Eigen::Triplet<SCALAR>; //! Rename to follow our conventions and add to ml::maths. template<typename SCALAR, int FLAGS = Eigen::RowMajorBit> using CSparseVector = Eigen::SparseVector<SCALAR, FLAGS, std::ptrdiff_t>; //! The type of an element of a sparse vector in coordinate form. template<typename SCALAR> using CSparseVectorCoordinate = Eigen::Triplet<SCALAR>; //! Create a tuple with which to initialize a sparse matrix. template<typename SCALAR> inline CSparseMatrixElement<SCALAR> matrixElement(std::ptrdiff_t row, std::ptrdiff_t column, SCALAR value) { return CSparseMatrixElement<SCALAR>(row, column, value); } //! Create a tuple with which to initialize a sparse column vector. template<typename SCALAR> inline CSparseVectorCoordinate<SCALAR> vectorCoordinate(std::ptrdiff_t row, SCALAR value) { return CSparseVectorCoordinate<SCALAR>(row, 0, value); } //! \brief Adapts Eigen::SparseVector::InnerIterator for use with STL. template<typename SCALAR, int FLAGS = Eigen::RowMajorBit> class CSparseVectorIndexIterator { public: using iterator_category = std::input_iterator_tag; using value_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t; using pointer = void; using reference = void; public: CSparseVectorIndexIterator(const CSparseVector<SCALAR, FLAGS>& vector, std::size_t index) : m_Vector(&vector), m_Base(vector, index) {} bool operator==(const CSparseVectorIndexIterator& rhs) const { return m_Vector == rhs.m_Vector && m_Base.row() == rhs.m_Base.row() && m_Base.col() == rhs.m_Base.col(); } bool operator!=(const CSparseVectorIndexIterator& rhs) const { return (*this == rhs) == false; } std::ptrdiff_t operator*() const { return std::max(m_Base.row(), m_Base.col()); } CSparseVectorIndexIterator& operator++() { ++m_Base; return *this; } CSparseVectorIndexIterator operator++(int) { CSparseVectorIndexIterator result(*this); ++m_Base; return result; } private: using TIterator = typename CSparseVector<SCALAR, FLAGS>::InnerIterator; private: CSparseVector<SCALAR, FLAGS>* m_Vector; TIterator m_Base; }; //! Get an iterator over the indices of \p vector. template<typename SCALAR, int FLAGS> CSparseVectorIndexIterator<SCALAR, FLAGS> beginIndices(const CSparseVector<SCALAR, FLAGS>& vector) { return CSparseVectorIndexIterator<SCALAR, FLAGS>(vector, 0); } //! Get the end iterator of the indices of \p vector. template<typename SCALAR, int FLAGS> CSparseVectorIndexIterator<SCALAR, FLAGS> endIndices(const CSparseVector<SCALAR, FLAGS>& vector) { return CSparseVectorIndexIterator<SCALAR, FLAGS>(vector, vector.data().size()); } //! \brief Decorates an Eigen matrix with some useful methods. template<typename SCALAR> class CDenseMatrix : public Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic> { public: using TBase = Eigen::Matrix<SCALAR, Eigen::Dynamic, Eigen::Dynamic>; using TIndexType = typename TBase::Index; public: //! Forwarding constructor. template<typename... ARGS> CDenseMatrix(ARGS&&... args) : TBase(std::forward<ARGS>(args)...) {} //! \name Copy and Move Semantics //@{ CDenseMatrix(const CDenseMatrix& other) = default; CDenseMatrix(CDenseMatrix&& other) = default; CDenseMatrix& operator=(const CDenseMatrix& other) = default; CDenseMatrix& operator=(CDenseMatrix&& other) = default; template<typename EXPR> CDenseMatrix& operator=(const EXPR& expr) { static_cast<TBase&>(*this) = expr; return *this; } // @} //! Debug the memory usage of this object. void debugMemoryUsage(const core::CMemoryUsage::TMemoryUsagePtr& mem) const { mem->setName("CDenseMatrix"); mem->addItem("components", this->memoryUsage()); } //! Get the memory used by this object. std::size_t memoryUsage() const { return sizeof(SCALAR) * this->size(); } //! Get a checksum of this object. std::uint64_t checksum(std::uint64_t seed = 0) const { for (std::ptrdiff_t i = 0; i < this->size(); ++i) { seed = CChecksum::calculate(seed, this->coeff(i)); } return seed; } }; //! Free efficient swap for ADLU. template<typename SCALAR> void swap(CDenseMatrix<SCALAR>& lhs, CDenseMatrix<SCALAR>& rhs) { lhs.swap(rhs); } //! \brief Gets a constant dense square matrix with specified dimension or with //! specified numbers of rows and columns. template<typename SCALAR> struct SConstant<CDenseMatrix<SCALAR>> { static CDenseMatrix<SCALAR> get(std::ptrdiff_t dimension, SCALAR constant) { return get(dimension, dimension, constant); } static CDenseMatrix<SCALAR> get(std::ptrdiff_t rows, std::ptrdiff_t cols, SCALAR constant) { return CDenseMatrix<SCALAR>::Constant(rows, cols, constant); } }; //! \brief Gets the identity dense square matrix with specified dimension. template<typename SCALAR> struct SIdentity<CDenseMatrix<SCALAR>> { static CDenseMatrix<SCALAR> get(std::ptrdiff_t dimension) { return CDenseMatrix<SCALAR>::Identity(dimension, dimension); } }; //! \brief Decorates an Eigen column vector with some useful methods. template<typename SCALAR> class CDenseVector : public Eigen::Matrix<SCALAR, Eigen::Dynamic, 1> { public: using TBase = Eigen::Matrix<SCALAR, Eigen::Dynamic, 1>; using TIndexType = typename TBase::Index; public: static const std::string DENSE_VECTOR_TAG; public: //! Forwarding constructor. template<typename... ARGS> CDenseVector(ARGS&&... args) : TBase(std::forward<ARGS>(args)...) {} //! \name Copy and Move Semantics //@{ CDenseVector(const CDenseVector& other) = default; CDenseVector(CDenseVector&& other) noexcept = default; CDenseVector& operator=(const CDenseVector& other) = default; CDenseVector& operator=(CDenseVector&& other) noexcept = default; template<typename EXPR> CDenseVector& operator=(const EXPR& expr) { static_cast<TBase&>(*this) = expr; return *this; } // @} //! Debug the memory usage of this object. void debugMemoryUsage(const core::CMemoryUsage::TMemoryUsagePtr& mem) const { mem->setName("CDenseVector"); mem->addItem("components", this->memoryUsage()); } //! Get the memory used by this object. std::size_t memoryUsage() const { return sizeof(SCALAR) * this->size(); } //! Get a checksum of this object. std::uint64_t checksum(std::uint64_t seed = 0) const { for (std::ptrdiff_t i = 0; i < this->size(); ++i) { seed = CChecksum::calculate(seed, this->coeff(i)); } return seed; } //! Persist by passing information to \p inserter. void acceptPersistInserter(core::CStatePersistInserter& inserter) const { inserter.insertValue(DENSE_VECTOR_TAG, core::CPersistUtils::toString( this->to<std::vector<SCALAR>>())); } //! Populate the object from serialized data. bool acceptRestoreTraverser(core::CStateRestoreTraverser& traverser) { std::vector<SCALAR> tempVector; if (core::CPersistUtils::restore(DENSE_VECTOR_TAG, tempVector, traverser) == false) { LOG_ERROR(<< "Failed to restore " << DENSE_VECTOR_TAG << ", got " << traverser.value()); return false; } *this = fromStdVector(tempVector); return true; } //! Convert to a std::vector. //! //! It is assumed that COLLECTION supports reserve and push_back. template<typename COLLECTION> COLLECTION to() const { COLLECTION result; result.reserve(this->size()); for (int i = 0; i < this->size(); ++i) { result.push_back(this->coeff(i)); } return result; } //! Convert from a std::vector. static CDenseVector<SCALAR> fromStdVector(const std::vector<SCALAR>& vector) { CDenseVector<SCALAR> result(vector.size()); for (std::size_t i = 0; i < vector.size(); ++i) { result(i) = vector[i]; } return result; } //! Convert from a core::CSmallVector. template<std::size_t N> static CDenseVector<SCALAR> fromSmallVector(const core::CSmallVector<SCALAR, N>& vector) { CDenseVector<SCALAR> result(vector.size()); for (std::size_t i = 0; i < vector.size(); ++i) { result(i) = vector[i]; } return result; } }; template<typename SCALAR> const std::string CDenseVector<SCALAR>::DENSE_VECTOR_TAG{"dense_vector"}; //! Free efficient swap for ADLU. template<typename SCALAR> void swap(CDenseVector<SCALAR>& lhs, CDenseVector<SCALAR>& rhs) { lhs.swap(rhs); } //! \brief Gets a constant dense vector with specified dimension. template<typename SCALAR> struct SConstant<CDenseVector<SCALAR>> { static CDenseVector<SCALAR> get(std::ptrdiff_t dimension, SCALAR constant) { return CDenseVector<SCALAR>::Constant(dimension, constant); } }; //! \brief Decorates an Eigen::Map of a dense matrix with some useful methods //! and changes default copy semantics to shallow copy. //! //! IMPLEMENTATION DECISIONS:\n //! This effectively acts like a std::reference_wrapper of an Eigen::Map for //! an Eigen matrix. In particular, all copying is shallow unlike Eigen::Map //! that acts directly on the referenced memory. This is to match the behaviour //! of CMemoryMappedDenseVector. //! //! \sa CMemoryMappedDenseVector for more information. template<typename SCALAR, Eigen::AlignmentType ALIGNMENT = Eigen::Unaligned> class CMemoryMappedDenseMatrix : public Eigen::Map<typename CDenseMatrix<SCALAR>::TBase, ALIGNMENT> { public: using TBase = Eigen::Map<typename CDenseMatrix<SCALAR>::TBase, ALIGNMENT>; using TIndexType = typename TBase::Index; //! See core::CMemory. static constexpr bool dynamicSizeAlwaysZero() { return true; } public: //! Forwarding constructor. template<typename... ARGS> CMemoryMappedDenseMatrix(ARGS&&... args) : TBase{std::forward<ARGS>(args)...} {} //! \name Copy and Move Semantics //@{ CMemoryMappedDenseMatrix(const CMemoryMappedDenseMatrix& other) : TBase{nullptr, 1, 1} { this->reseat(other); } CMemoryMappedDenseMatrix(CMemoryMappedDenseMatrix&& other) noexcept( std::is_nothrow_constructible<TBase>::value) : TBase{nullptr, 1, 1} { this->reseat(other); } CMemoryMappedDenseMatrix& operator=(const CMemoryMappedDenseMatrix& other) { if (this != &other) { this->reseat(other); } return *this; } CMemoryMappedDenseMatrix& operator=(CMemoryMappedDenseMatrix&& other) noexcept( std::is_nothrow_constructible<TBase>::value) { if (this != &other) { this->reseat(other); } return *this; } //@} //! Assignment from a dense matrix. template<typename OTHER_SCALAR> CMemoryMappedDenseMatrix& operator=(const CDenseMatrix<OTHER_SCALAR>& rhs) { static_cast<TBase&>(*this) = rhs.template cast<SCALAR>(); return *this; } //! Get a checksum of this object. std::uint64_t checksum(std::uint64_t seed = 0) const { for (std::ptrdiff_t i = 0; i < this->rows(); ++i) { for (std::ptrdiff_t j = 0; j < this->rows(); ++j) { seed = CChecksum::calculate(seed, (*this)(i, j)); } } return seed; } private: void reseat(const CMemoryMappedDenseMatrix& other) { TBase* base{static_cast<TBase*>(this)}; new (base) TBase{const_cast<SCALAR*>(other.data()), other.rows(), other.cols()}; } }; //! Free efficient swap for ADLU. template<typename SCALAR, Eigen::AlignmentType ALIGNMENT> void swap(CMemoryMappedDenseMatrix<SCALAR, ALIGNMENT>& lhs, CMemoryMappedDenseMatrix<SCALAR, ALIGNMENT>& rhs) { lhs.swap(rhs); } //! \brief Gets a constant square dense matrix with specified dimension or with //! specified numbers of rows and columns. template<typename SCALAR, Eigen::AlignmentType ALIGNMENT> struct SConstant<CMemoryMappedDenseMatrix<SCALAR, ALIGNMENT>> { static auto get(std::ptrdiff_t dimension, SCALAR constant) -> decltype(SConstant<CDenseMatrix<SCALAR>>::get(dimension, 1)) { return SConstant<CDenseMatrix<SCALAR>>::get(dimension, constant); } static auto get(std::ptrdiff_t rows, std::ptrdiff_t cols, SCALAR constant) -> decltype(SConstant<CDenseMatrix<SCALAR>>::get(rows, cols, constant)) { return SConstant<CDenseMatrix<SCALAR>>::get(rows, cols, constant); } }; //! \brief Gets the identity dense matrix with specified dimension. template<typename SCALAR, Eigen::AlignmentType ALIGNMENT> struct SIdentity<CMemoryMappedDenseMatrix<SCALAR, ALIGNMENT>> { static auto get(std::ptrdiff_t dimension) -> decltype(SIdentity<CDenseMatrix<SCALAR>>::get(dimension)) { return SIdentity<CDenseMatrix<SCALAR>>::get(dimension); } }; //! \brief Decorates an Eigen::Map of a dense vector with some useful methods //! and changes default copy semantics to shallow. //! //! IMPLEMENTATION DECISIONS:\n //! This effectively acts like a std::reference_wrapper of an Eigen::Map for //! an Eigen vector. In particular, all copying is shallow unlike Eigen::Map //! that acts directly on the referenced memory, i.e. //! \code{.cpp} //! double values1[]{1.0, 1.0}; //! double values2[]{2.0, 2.0}; //! //! CMemoryMappedDenseVector<double> mm1{values1, 2}; //! CMemoryMappedDenseVector<double> mm2{values2, 2}; //! //! mm1 = mm2; //! std::cout << mm1(0) << "," << mm1(1) << "," << values1[0] << "," << values1[1] << std::endl; //! //! Eigen::Map<Eigen::VectorXd> map1{values1, 2}; //! Eigen::Map<Eigen::VectorXd> map2{values2, 2}; //! //! map1 = map2; //! std::cout << map1(0) << "," << map1(1) << "," << values1[0] << "," << values1[1] << std::endl; //! \endcode //! //! Outputs:\n //! 2,2,1,1\n //! 2,2,2,2 //! //! This better fits our needs with data frames where we want to reference the //! memory stored in the data frame rows, but never modify it directly through //! this vector type. template<typename SCALAR, Eigen::AlignmentType ALIGNMENT = Eigen::Unaligned> class CMemoryMappedDenseVector : public Eigen::Map<typename CDenseVector<SCALAR>::TBase, ALIGNMENT> { public: using TDenseVector = CDenseVector<SCALAR>; using TBase = Eigen::Map<typename TDenseVector::TBase, ALIGNMENT>; using TIndexType = typename TBase::Index; //! See core::CMemory. static constexpr bool dynamicSizeAlwaysZero() { return true; } public: //! Forwarding constructor. template<typename... ARGS> CMemoryMappedDenseVector(ARGS&&... args) : TBase{std::forward<ARGS>(args)...} {} //! Added because the forwarding constructor above doesn't work with //! annotated vector arguments with Visual Studio 2019 in C++17 mode. template<typename ANNOTATION> CMemoryMappedDenseVector(CAnnotatedVector<TDenseVector, ANNOTATION>&& annotatedDense) : TBase{annotatedDense.data(), annotatedDense.size()} {} //! \name Copy and Move Semantics //@{ CMemoryMappedDenseVector(const CMemoryMappedDenseVector& other) : TBase{nullptr, 1} { this->reseat(other); } CMemoryMappedDenseVector(CMemoryMappedDenseVector&& other) noexcept( std::is_nothrow_constructible<TBase>::value) : TBase{nullptr, 1} { this->reseat(other); } CMemoryMappedDenseVector& operator=(const CMemoryMappedDenseVector& other) { if (this != &other) { this->reseat(other); } return *this; } CMemoryMappedDenseVector& operator=(CMemoryMappedDenseVector&& other) noexcept( std::is_nothrow_constructible<TBase>::value) { if (this != &other) { this->reseat(other); } return *this; } //@} //! Assignment from a dense vector. template<typename OTHER_SCALAR> CMemoryMappedDenseVector& operator=(const CDenseVector<OTHER_SCALAR>& rhs) { static_cast<TBase&>(*this) = rhs.template cast<SCALAR>(); return *this; } //! Get a checksum of this object. std::uint64_t checksum(std::uint64_t seed = 0) const { for (std::ptrdiff_t i = 0; i < this->size(); ++i) { seed = CChecksum::calculate(seed, this->coeff(i)); } return seed; } private: void reseat(const CMemoryMappedDenseVector& other) { TBase* base{static_cast<TBase*>(this)}; new (base) TBase{const_cast<SCALAR*>(other.data()), other.size()}; } }; //! Free efficient swap for ADLU. template<typename SCALAR, Eigen::AlignmentType ALIGNMENT> void swap(CMemoryMappedDenseVector<SCALAR, ALIGNMENT>& lhs, CMemoryMappedDenseVector<SCALAR, ALIGNMENT>& rhs) { lhs.swap(rhs); } //! \brief Gets a constant dense vector with specified dimension. template<typename SCALAR, Eigen::AlignmentType ALIGNMENT> struct SConstant<CMemoryMappedDenseVector<SCALAR, ALIGNMENT>> { static auto get(std::ptrdiff_t dimension, SCALAR constant) -> decltype(SConstant<CDenseVector<SCALAR>>::get(dimension, constant)) { return SConstant<CDenseVector<SCALAR>>::get(dimension, constant); } }; //! \brief Eigen matrix typedef. //! //! DESCRIPTION:\n //! Instantiating many different sizes of Eigen::Matrix really hurts //! our compile times and executable sizes with debug symbols. The //! idea of this class is to limit the maximum size of N for which //! we instantiate different versions. Also, Eigen matrices are always //! used for calculation for which we want to use double precision. template<typename MATRIX> struct SDenseMatrix { using Type = CDenseMatrix<double>; }; //! \brief Use stack matrix for size 2. template<typename T> struct SDenseMatrix<CSymmetricMatrixNxN<T, 2>> { using Type = Eigen::Matrix<double, 2, 2>; }; //! \brief Use stack matrix for size 3. template<typename T> struct SDenseMatrix<CSymmetricMatrixNxN<T, 3>> { using Type = Eigen::Matrix<double, 3, 3>; }; //! \brief Use stack matrix for size 4. template<typename T> struct SDenseMatrix<CSymmetricMatrixNxN<T, 4>> { using Type = Eigen::Matrix<double, 4, 4>; }; //! Get the Eigen matrix for \p matrix. template<typename MATRIX> typename SDenseMatrix<MATRIX>::Type toDenseMatrix(const MATRIX& matrix) { return matrix.template toType<typename SDenseMatrix<MATRIX>::Type>(); } //! Get the dynamic Eigen matrix for \p matrix. template<typename MATRIX> CDenseMatrix<double> toDynamicDenseMatrix(const MATRIX& matrix) { return matrix.template toType<CDenseMatrix<double>>(); } //! \brief Eigen vector typedef. //! //! DESCRIPTION:\n //! See SDenseMatrix. template<typename VECTOR> struct SDenseVector { using Type = CDenseVector<double>; }; //! \brief Use stack vector for size 2. template<typename T> struct SDenseVector<CVectorNx1<T, 2>> { using Type = Eigen::Matrix<double, 2, 1>; }; //! \brief Use stack vector for size 3. template<typename T> struct SDenseVector<CVectorNx1<T, 3>> { using Type = Eigen::Matrix<double, 3, 1>; }; //! \brief Use stack vector for size 4. template<typename T> struct SDenseVector<CVectorNx1<T, 4>> { using Type = Eigen::Matrix<double, 4, 1>; }; //! Get the Eigen vector for \p vector. template<typename VECTOR> typename SDenseVector<VECTOR>::Type toDenseVector(const VECTOR& vector) { return vector.template toType<typename SDenseVector<VECTOR>::Type>(); } //! Get the dynamic Eigen vector for \p vector. template<typename VECTOR> CDenseVector<double> toDynamicDenseVector(const VECTOR& vector) { return vector.template toType<CDenseVector<double>>(); } //! \brief The default type for converting Eigen matrices to our //! internal symmetric matrices. //! //! IMPLEMENTATION DECISIONS:\n //! This type is needed to get Eigen GEMM expressions to play nicely //! with our symmetric matrix type constructors. Also, I think it is //! useful to flag explicitly when a conversion is taking place, the //! fromDenseMatrix function plays this role in code where we want a //! conversion. template<typename MATRIX> class CDenseMatrixInitializer { public: explicit CDenseMatrixInitializer(const MATRIX& type) : m_Type(&type) {} std::size_t rows() const { return m_Type->rows(); } double get(std::size_t i, std::size_t j) const { return (m_Type->template selfadjointView<Eigen::Lower>())(i, j); } private: const MATRIX* m_Type; }; //! Convert an Eigen matrix to a form which can initialize one of our //! symmetric matrix objects. template<typename MATRIX> CDenseMatrixInitializer<MATRIX> fromDenseMatrix(const MATRIX& type) { return CDenseMatrixInitializer<MATRIX>(type); } //! \brief The default type for converting Eigen vectors to our //! internal vectors. //! //! IMPLEMENTATION DECISIONS:\n //! This type is needed to get Eigen GEMM expressions to play nicely //! with our vector type constructors. Also, I think it is useful to //! flag explicitly when a conversion is taking place, the fromDenseVector //! function plays this role in code where we want a conversion. template<typename VECTOR> class CDenseVectorInitializer { public: explicit CDenseVectorInitializer(const VECTOR& type) : m_Type(&type) {} std::size_t dimension() const { return m_Type->size(); } double get(std::size_t i) const { return (*m_Type)(i); } private: const VECTOR* m_Type; }; //! Convert an Eigen vector to a form which can initialize one of our //! vector objects. template<typename VECTOR> CDenseVectorInitializer<VECTOR> fromDenseVector(const VECTOR& type) { return CDenseVectorInitializer<VECTOR>(type); } template<typename T, std::size_t N> template<typename MATRIX> CSymmetricMatrixNxN<T, N>::CSymmetricMatrixNxN(const CDenseMatrixInitializer<MATRIX>& m) { for (std::size_t i = 0u, i_ = 0; i < N; ++i) { for (std::size_t j = 0; j <= i; ++j, ++i_) { TBase::m_LowerTriangle[i_] = m.get(i, j); } } } template<typename T> template<typename MATRIX> CSymmetricMatrix<T>::CSymmetricMatrix(const CDenseMatrixInitializer<MATRIX>& m) { m_D = m.rows(); TBase::m_LowerTriangle.resize(m_D * (m_D + 1) / 2); for (std::size_t i = 0u, i_ = 0; i < m_D; ++i) { for (std::size_t j = 0; j <= i; ++j, ++i_) { TBase::m_LowerTriangle[i_] = m.get(i, j); } } } template<typename T, std::size_t N> template<typename VECTOR> CVectorNx1<T, N>::CVectorNx1(const CDenseVectorInitializer<VECTOR>& v) { for (std::size_t i = 0; i < N; ++i) { TBase::m_X[i] = v.get(i); } } template<typename T> template<typename VECTOR> CVector<T>::CVector(const CDenseVectorInitializer<VECTOR>& v) { TBase::m_X.resize(v.dimension()); for (std::size_t i = 0; i < TBase::m_X.size(); ++i) { TBase::m_X[i] = v.get(i); } } } } } namespace Eigen { template<typename BIN_OP> struct ScalarBinaryOpTraits<ml::core::CFloatStorage, double, BIN_OP> { using ReturnType = double; }; template<typename BIN_OP> struct ScalarBinaryOpTraits<double, ml::core::CFloatStorage, BIN_OP> { using ReturnType = double; }; } #endif // INCLUDED_ml_maths_common_CLinearAlgebraEigen_h