diff --git a/Eigen/LU b/Eigen/LU index f452d8307..c91150035 100644 --- a/Eigen/LU +++ b/Eigen/LU @@ -1,5 +1,5 @@ -#ifndef EIGEN_LU_H -#define EIGEN_LU_H +#ifndef EIGEN_LU_MODULE_H +#define EIGEN_LU_MODULE_H #include "Core" @@ -16,9 +16,10 @@ namespace Eigen { * \endcode */ +#include "src/LU/LU.h" #include "src/LU/Determinant.h" #include "src/LU/Inverse.h" } // namespace Eigen -#endif // EIGEN_LU_H +#endif // EIGEN_LU_MODULE_H diff --git a/Eigen/src/Core/DummyPacketMath.h b/Eigen/src/Core/DummyPacketMath.h index 4abd6e997..313001f2f 100644 --- a/Eigen/src/Core/DummyPacketMath.h +++ b/Eigen/src/Core/DummyPacketMath.h @@ -139,7 +139,7 @@ template struct ei_palign_impl { // by default data are aligned, so there is nothing to be done :) - inline static void run(PacketType& first, const PacketType& second) {} + inline static void run(PacketType&, const PacketType&) {} }; /** \internal update \a first using the concatenation of the \a Offset last elements diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index f15e560cb..c2b12a0a3 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -526,6 +526,7 @@ template class MatrixBase /////////// LU module /////////// + const LU lu() const; const EvalType inverse() const; void computeInverse(EvalType *result) const; Scalar determinant() const; diff --git a/Eigen/src/Core/Swap.h b/Eigen/src/Core/Swap.h index 35590b56f..3b864789e 100644 --- a/Eigen/src/Core/Swap.h +++ b/Eigen/src/Core/Swap.h @@ -42,7 +42,9 @@ template void MatrixBase::swap(const MatrixBase& other) { MatrixBase *_other = const_cast*>(&other); - if(SizeAtCompileTime == Dynamic) + + // disable that path: it makes LU decomposition fail ! I can't see the bug though. + if(false /*SizeAtCompileTime == Dynamic*/) { ei_swap_selector::run(derived(),other.const_cast_derived()); } diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index 76a2f60cf..067ccd0b0 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -95,7 +95,7 @@ void ei_cache_friendly_product( bool _rhsRowMajor, const Scalar* _rhs, int _rhsStride, bool resRowMajor, Scalar* res, int resStride); -template class Inverse; +template class LU; template class QR; template class Cholesky; template class CholeskyWithoutSquareRoot; diff --git a/Eigen/src/Core/util/Macros.h b/Eigen/src/Core/util/Macros.h index eaa5df27f..40db9a0a7 100644 --- a/Eigen/src/Core/util/Macros.h +++ b/Eigen/src/Core/util/Macros.h @@ -135,7 +135,6 @@ typedef typename Eigen::NumTraits::Real RealScalar; \ typedef typename Base::PacketScalar PacketScalar; \ typedef typename Eigen::ei_nested::type Nested; \ typedef typename Eigen::ei_eval::type Eval; \ -typedef typename Eigen::Inverse InverseType; \ enum { RowsAtCompileTime = Eigen::ei_traits::RowsAtCompileTime, \ ColsAtCompileTime = Eigen::ei_traits::ColsAtCompileTime, \ MaxRowsAtCompileTime = Eigen::ei_traits::MaxRowsAtCompileTime, \ diff --git a/Eigen/src/LU/Determinant.h b/Eigen/src/LU/Determinant.h index fdb1673d8..96e400564 100644 --- a/Eigen/src/LU/Determinant.h +++ b/Eigen/src/LU/Determinant.h @@ -63,7 +63,7 @@ const typename Derived::Scalar ei_bruteforce_det(const MatrixBase& m) - ei_bruteforce_det4_helper(m,1,3,0,2) + ei_bruteforce_det4_helper(m,2,3,0,1); default: - assert(false); + ei_internal_assert(false); } } @@ -85,7 +85,7 @@ typename ei_traits::Scalar MatrixBase::determinant() const return derived().diagonal().redux(ei_scalar_product_op()); } else if(rows() <= 4) return ei_bruteforce_det(derived()); - else assert(false); // unimplemented for now + else return lu().determinant(); } #endif // EIGEN_DETERMINANT_H diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h new file mode 100644 index 000000000..aa701f2ce --- /dev/null +++ b/Eigen/src/LU/LU.h @@ -0,0 +1,188 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2006-2008 Benoit Jacob +// +// Eigen is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// Alternatively, you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of +// the License, or (at your option) any later version. +// +// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License and a copy of the GNU General Public License along with +// Eigen. If not, see . + +#ifndef EIGEN_LU_H +#define EIGEN_LU_H + +/** \ingroup LU_Module + * + * \class LU + * + * \brief LU decomposition of a matrix with complete pivoting, and associated features + * + * \param MatrixType the type of the matrix of which we are computing the LU decomposition + * + * This class performs a LU decomposition of any matrix, with complete pivoting: the matrix A + * is decomposed as A = PLUQ where L is unit-lower-triangular, U is upper-triangular, and P and Q + * are permutation matrices. + * + * This decomposition provides the generic approach to solving systems of linear equations, computing + * the rank, invertibility, inverse, and determinant. However for the case when invertibility is + * assumed, we have a specialized variant (see MatrixBase::inverse()) achieving better performance. + * + * \sa MatrixBase::lu(), MatrixBase::determinant(), MatrixBase::rank(), MatrixBase::kernelDim(), + * MatrixBase::kernelBasis(), MatrixBase::solve(), MatrixBase::isInvertible(), + * MatrixBase::inverse(), MatrixBase::computeInverse() + */ +template class LU +{ + public: + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef Matrix IntRowVectorType; + typedef Matrix IntColVectorType; + + LU(const MatrixType& matrix); + + const MatrixType& matrixLU() const + { + return m_lu; + } + + const Part matrixL() const + { + return m_lu; + } + + const Part matrixU() const + { + return m_lu; + } + + const IntColVectorType& permutationP() const + { + return m_p; + } + + const IntRowVectorType& permutationQ() const + { + return m_q; + } + + template + typename ProductReturnType, OtherDerived>::Type::Eval + solve(const MatrixBase &b) const; + + /** + * This method returns the determinant of the matrix of which + * *this is the LU decomposition. It has only linear complexity + * (that is, O(n) where n is the dimension of the square matrix) + * as the LU decomposition has already been computed. + * + * Warning: a determinant can be very big or small, so for matrices + * of large dimension (like a 50-by-50 matrix) there can be a risk of + * overflow/underflow. + */ + typename ei_traits::Scalar determinant() const; + + protected: + MatrixType m_lu; + IntColVectorType m_p; + IntRowVectorType m_q; + int m_det_pq; + Scalar m_biggest_eigenvalue_of_u; + int m_dimension_of_kernel; +}; + +template +LU::LU(const MatrixType& matrix) + : m_lu(matrix), + m_p(matrix.rows()), + m_q(matrix.cols()) +{ + const int size = matrix.diagonal().size(); + const int rows = matrix.rows(); + const int cols = matrix.cols(); + + IntColVectorType rows_transpositions(matrix.rows()); + IntRowVectorType cols_transpositions(matrix.cols()); + int number_of_transpositions = 0; + + for(int k = 0; k < size; k++) + { + int row_of_biggest, col_of_biggest; + const Scalar biggest = m_lu.corner(Eigen::BottomRight, rows-k, cols-k) + .cwise().abs() + .maxCoeff(&row_of_biggest, &col_of_biggest); + row_of_biggest += k; + col_of_biggest += k; + rows_transpositions.coeffRef(k) = row_of_biggest; + cols_transpositions.coeffRef(k) = col_of_biggest; + if(k != row_of_biggest) { + m_lu.row(k).swap(m_lu.row(row_of_biggest)); + number_of_transpositions++; + } + if(k != col_of_biggest) { + m_lu.col(k).swap(m_lu.col(col_of_biggest)); + number_of_transpositions++; + } + const Scalar lu_k_k = m_lu.coeff(k,k); + if(ei_isMuchSmallerThan(lu_k_k, biggest)) continue; + if(k= 0; k--) + std::swap(m_p.coeffRef(k), m_p.coeffRef(rows_transpositions.coeff(k))); + + for(int k = 0; k < matrix.cols(); k++) m_q.coeffRef(k) = k; + for(int k = 0; k < size; k++) + std::swap(m_q.coeffRef(k), m_q.coeffRef(cols_transpositions.coeff(k))); + + m_det_pq = (number_of_transpositions%2) ? -1 : 1; + + int index_of_biggest; + m_lu.diagonal().cwise().abs().maxCoeff(&index_of_biggest); + m_biggest_eigenvalue_of_u = m_lu.diagonal().coeff(index_of_biggest); + + m_dimension_of_kernel = 0; + for(int k = 0; k < size; k++) + m_dimension_of_kernel += ei_isMuchSmallerThan(m_lu.diagonal().coeff(k), m_biggest_eigenvalue_of_u); +} + +template +typename ei_traits::Scalar LU::determinant() const +{ + Scalar res = m_det_pq; + for(int k = 0; k < m_lu.diagonal().size(); k++) res *= m_lu.diagonal().coeff(k); + return res; +} + +/** \return the LU decomposition of \c *this. + * + * \sa class LU + */ +template +const LU::EvalType> +MatrixBase::lu() const +{ + return eval(); +} + +#endif // EIGEN_LU_H diff --git a/Eigen/src/QR/QR.h b/Eigen/src/QR/QR.h index 8ca787c2a..a6cd31f12 100644 --- a/Eigen/src/QR/QR.h +++ b/Eigen/src/QR/QR.h @@ -171,7 +171,7 @@ template const QR::EvalType> MatrixBase::qr() const { - return QR::type>(derived()); + return eval(); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 97c7f4937..f3f3a625c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,7 +2,7 @@ IF(BUILD_TESTS) IF(CMAKE_COMPILER_IS_GNUCXX) IF(CMAKE_SYSTEM_NAME MATCHES Linux) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O1 -g1") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g2") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O2 -g2") SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-inline-functions") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g2") @@ -95,7 +95,7 @@ EI_ADD_TEST(map) EI_ADD_TEST(array) EI_ADD_TEST(triangular) EI_ADD_TEST(cholesky) -# EI_ADD_TEST(determinant) +EI_ADD_TEST(determinant) EI_ADD_TEST(inverse) EI_ADD_TEST(qr) EI_ADD_TEST(eigensolver) diff --git a/test/determinant.cpp b/test/determinant.cpp index e19aee918..dc9f5eb54 100644 --- a/test/determinant.cpp +++ b/test/determinant.cpp @@ -25,54 +25,50 @@ #include "main.h" #include -template void nullDeterminant(const MatrixType& m) +template void determinant(const MatrixType& m) { /* this test covers the following files: Determinant.h */ - int rows = m.rows(); - int cols = m.cols(); + int size = m.rows(); + MatrixType m1(size, size), m2(size, size); + m1.setRandom(); + m2.setRandom(); typedef typename MatrixType::Scalar Scalar; - typedef Matrix SquareMatrixType; - typedef Matrix VectorType; - - MatrixType dinv(rows, cols), dnotinv(rows, cols); - - dinv.col(0).setOnes(); - dinv.block(0,1, rows, cols-2).setRandom(); - - dnotinv.col(0).setOnes(); - dnotinv.block(0,1, rows, cols-2).setRandom(); - dnotinv.col(cols-1).setOnes(); - - for (int i=0 ; i(99.999999,100.00000001)*dnotinv.row(i).block(0,1,1,cols-2).normalized(); - dnotinv(i,cols-1) = dnotinv.row(i).block(0,1,1,cols-2).norm2(); - dinv(i,cols-1) = dinv.row(i).block(0,1,1,cols-2).norm2(); - } - - SquareMatrixType invertibleCovarianceMatrix = dinv.transpose() * dinv; - SquareMatrixType notInvertibleCovarianceMatrix = dnotinv.transpose() * dnotinv; - - std::cout << notInvertibleCovarianceMatrix << "\n" << notInvertibleCovarianceMatrix.determinant() << "\n"; - - VERIFY_IS_MUCH_SMALLER_THAN(notInvertibleCovarianceMatrix.determinant(), - notInvertibleCovarianceMatrix.cwise().abs().maxCoeff()); - - VERIFY(invertibleCovarianceMatrix.inverse().exists()); - - VERIFY(!notInvertibleCovarianceMatrix.inverse().exists()); + Scalar x = ei_random(); + VERIFY(ei_isApprox(MatrixType::Identity(size, size).determinant(), Scalar(1))); + VERIFY(ei_isApprox((m1*m2).determinant(), m1.determinant() * m2.determinant())); + if(size==1) return; + int i = ei_random(0, size-1); + int j; + do { + j = ei_random(0, size-1); + } while(j==i); + m2 = m1; + m2.row(i).swap(m2.row(j)); + VERIFY(ei_isApprox(m2.determinant(), -m1.determinant())); + m2 = m1; + m2.col(i).swap(m2.col(j)); + VERIFY(ei_isApprox(m2.determinant(), -m1.determinant())); + VERIFY(ei_isApprox(m2.determinant(), m2.transpose().determinant())); + VERIFY(ei_isApprox(ei_conj(m2.determinant()), m2.adjoint().determinant())); + m2 = m1; + m2.row(i) += x*m2.row(j); + VERIFY(ei_isApprox(m2.determinant(), m1.determinant())); + m2 = m1; + m2.row(i) *= x; + VERIFY(ei_isApprox(m2.determinant(), m1.determinant() * x)); } void test_determinant() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( nullDeterminant(Matrix()) ); - CALL_SUBTEST( nullDeterminant(Matrix()) ); - CALL_SUBTEST( nullDeterminant(Matrix()) ); - CALL_SUBTEST( nullDeterminant(Matrix()) ); -// CALL_SUBTEST( nullDeterminant(MatrixXd(20,4)); + CALL_SUBTEST( determinant(Matrix()) ); + CALL_SUBTEST( determinant(Matrix()) ); + CALL_SUBTEST( determinant(Matrix()) ); + CALL_SUBTEST( determinant(Matrix()) ); + CALL_SUBTEST( determinant(Matrix, 10, 10>()) ); + CALL_SUBTEST( determinant(MatrixXd(20, 20)) ); } }