From ab5cc8284aedae58885902f3764d19a8ff05f758 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Tue, 22 Sep 2009 20:58:29 -0400 Subject: [PATCH 01/34] convert LU::solve() to the new API --- Eigen/src/Core/ReturnByValue.h | 5 + Eigen/src/LU/LU.h | 189 +++++++++++++++++++-------------- doc/Doxyfile.in | 5 +- doc/snippets/LU_solve.cpp | 9 +- test/lu.cpp | 10 +- 5 files changed, 122 insertions(+), 96 deletions(-) diff --git a/Eigen/src/Core/ReturnByValue.h b/Eigen/src/Core/ReturnByValue.h index 4a5d5c105..afe9ecbbd 100644 --- a/Eigen/src/Core/ReturnByValue.h +++ b/Eigen/src/Core/ReturnByValue.h @@ -2,6 +2,7 @@ // for linear algebra. // // Copyright (C) 2009 Gael Guennebaud +// Copyright (C) 2009 Benoit Jacob // // Eigen is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -37,6 +38,10 @@ struct ei_traits > }; }; +/* The ReturnByValue object doesn't even have a coeff() method. + * So the only way that nesting it in an expression can work, is by evaluating it into a plain matrix. + * So ei_nested always gives the plain return matrix type. + */ template struct ei_nested, n, PlainMatrixType> { diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index e848b5454..0475b5be3 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -1,7 +1,7 @@ // This file is part of Eigen, a lightweight C++ template library // for linear algebra. // -// Copyright (C) 2006-2008 Benoit Jacob +// Copyright (C) 2006-2009 Benoit Jacob // // Eigen is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -25,6 +25,90 @@ #ifndef EIGEN_LU_H #define EIGEN_LU_H +template struct ei_lu_solve_impl; + +template +struct ei_traits > +{ + typedef Matrix ReturnMatrixType; +}; + +template +struct ei_lu_solve_impl : public ReturnByValue > +{ + typedef typename ei_cleantype::type RhsNested; + typedef LU LUType; + + ei_lu_solve_impl(const LUType& lu, const Rhs& rhs) + : m_lu(lu), m_rhs(rhs) + {} + + inline int rows() const { return m_lu.matrixLU().cols(); } + inline int cols() const { return m_rhs.cols(); } + + template void evalTo(Dest& dst) const + { + dst.resize(m_lu.matrixLU().cols(), m_rhs.cols()); + if(m_lu.rank()==0) + { + dst.setZero(); + return; + } + + /* The decomposition PAQ = LU can be rewritten as A = P^{-1} L U Q^{-1}. + * So we proceed as follows: + * Step 1: compute c = P * rhs. + * Step 2: replace c by the solution x to Lx = c. Exists because L is invertible. + * Step 3: replace c by the solution x to Ux = c. May or may not exist. + * Step 4: result = Q * c; + */ + + const int rows = m_lu.matrixLU().rows(), + cols = m_lu.matrixLU().cols(), + rank = m_lu.rank(); + ei_assert(m_rhs.rows() == rows); + const int smalldim = std::min(rows, cols); + + typename Rhs::PlainMatrixType c(m_rhs.rows(), m_rhs.cols()); + + // Step 1 + for(int i = 0; i < rows; ++i) + c.row(m_lu.permutationP().coeff(i)) = m_rhs.row(i); + + // Step 2 + m_lu.matrixLU() + .corner(Eigen::TopLeft,smalldim,smalldim) + .template triangularView() + .solveInPlace(c.corner(Eigen::TopLeft, smalldim, c.cols())); + if(rows>cols) + { + c.corner(Eigen::BottomLeft, rows-cols, c.cols()) + -= m_lu.matrixLU().corner(Eigen::BottomLeft, rows-cols, cols) + * c.corner(Eigen::TopLeft, cols, c.cols()); + } + + // Step 3 + m_lu.matrixLU() + .corner(TopLeft, rank, rank) + .template triangularView() + .solveInPlace(c.corner(TopLeft, rank, c.cols())); + + // Step 4 + for(int i = 0; i < rank; ++i) + dst.row(m_lu.permutationQ().coeff(i)) = c.row(i); + for(int i = rank; i < m_lu.matrixLU().cols(); ++i) + dst.row(m_lu.permutationQ().coeff(i)).setZero(); + } + + const LUType& m_lu; + const typename Rhs::Nested m_rhs; +}; + /** \ingroup LU_Module * * \class LU @@ -83,8 +167,8 @@ template class LU > KernelResultType; typedef Matrix class LU * \returns a reference to *this */ LU& compute(const MatrixType& matrix); - + /** \returns the LU decomposition matrix: the upper-triangular part is U, the * unit-lower-triangular part is L (at least for square matrices; in the non-square * case, special care is needed, see the documentation of class LU). @@ -215,29 +299,35 @@ template class LU */ const ImageResultType image() const; - /** This method finds a solution x to the equation Ax=b, where A is the matrix of which - * *this is the LU decomposition, if any exists. + /** This method returns a solution x to the equation Ax=b, where A is the matrix of which + * *this is the LU decomposition. + * + * If no solution exists, then the result is undefined. If only an approximate solution exists, + * then the result is only such an approximate solution. * * \param b the right-hand-side of the equation to solve. Can be a vector or a matrix, * the only requirement in order for the equation to make sense is that * b.rows()==A.rows(), where A is the matrix of which *this is the LU decomposition. - * \param result a pointer to the vector or matrix in which to store the solution, if any exists. - * Resized if necessary, so that result->rows()==A.cols() and result->cols()==b.cols(). - * If no solution exists, *result is left with undefined coefficients. * - * \returns true if any solution exists, false if no solution exists. - * - * \note If there exist more than one solution, this method will arbitrarily choose one. - * If you need a complete analysis of the space of solutions, take the one solution obtained - * by this method and add to it elements of the kernel, as determined by kernel(). + * \returns a solution, if any exists. See notes below. * * Example: \include LU_solve.cpp * Output: \verbinclude LU_solve.out * + * \note \note_about_inexistant_solutions + * + * \note \note_about_arbitrary_choice_of_solution + * \note_about_using_kernel_to_study_multiple_solutions + * * \sa TriangularView::solve(), kernel(), computeKernel(), inverse(), computeInverse() */ - template - bool solve(const MatrixBase& b, ResultType *result) const; + template + inline const ei_lu_solve_impl + solve(const MatrixBase& b) const + { + ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + return ei_lu_solve_impl(*this, b.derived()); + } /** \returns the determinant of the matrix of which * *this is the LU decomposition. It has only linear complexity @@ -326,7 +416,7 @@ template class LU { ei_assert(m_originalMatrix != 0 && "LU is not initialized."); ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the inverse of a non-square matrix!"); - solve(MatrixType::Identity(m_lu.rows(), m_lu.cols()), result); + *result = solve(MatrixType::Identity(m_lu.rows(), m_lu.cols())); } /** \returns the inverse of the matrix of which *this is the LU decomposition. @@ -526,71 +616,6 @@ LU::image() const return result; } -template -template -bool LU::solve( - const MatrixBase& b, - ResultType *result -) const -{ - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - result->resize(m_lu.cols(), b.cols()); - if(m_rank==0) - { - if(b.squaredNorm() == RealScalar(0)) - { - result->setZero(); - return true; - } - else return false; - } - - /* The decomposition PAQ = LU can be rewritten as A = P^{-1} L U Q^{-1}. - * So we proceed as follows: - * Step 1: compute c = Pb. - * Step 2: replace c by the solution x to Lx = c. Exists because L is invertible. - * Step 3: replace c by the solution x to Ux = c. Check if a solution really exists. - * Step 4: result = Qc; - */ - - const int rows = m_lu.rows(), cols = m_lu.cols(); - ei_assert(b.rows() == rows); - const int smalldim = std::min(rows, cols); - - typename OtherDerived::PlainMatrixType c(b.rows(), b.cols()); - - // Step 1 - for(int i = 0; i < rows; ++i) c.row(m_p.coeff(i)) = b.row(i); - - // Step 2 - m_lu.corner(Eigen::TopLeft,smalldim,smalldim) - .template triangularView() - .solveInPlace(c.corner(Eigen::TopLeft, smalldim, c.cols())); - if(rows>cols) - { - c.corner(Eigen::BottomLeft, rows-cols, c.cols()) - -= m_lu.corner(Eigen::BottomLeft, rows-cols, cols) * c.corner(Eigen::TopLeft, cols, c.cols()); - } - - // Step 3 - if(!isSurjective()) - { - // is c is in the image of U ? - RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, m_rank, c.cols()).cwise().abs().maxCoeff(); - RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-m_rank, c.cols()).cwise().abs().maxCoeff(); - if(!ei_isMuchSmallerThan(biggest_in_lower_part_of_c, biggest_in_upper_part_of_c, m_precision)) - return false; - } - m_lu.corner(TopLeft, m_rank, m_rank) - .template triangularView() - .solveInPlace(c.corner(TopLeft, m_rank, c.cols())); - - // Step 4 - for(int i = 0; i < m_rank; ++i) result->row(m_q.coeff(i)) = c.row(i); - for(int i = m_rank; i < m_lu.cols(); ++i) result->row(m_q.coeff(i)).setZero(); - return true; -} - /** \lu_module * * \return the LU decomposition of \c *this. diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 5b055ed11..b62ba3238 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -215,7 +215,10 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "eigenvalues_module=This is defined in the %Eigenvalues module. \code #include \endcode" \ "label=\bug" \ "redstar=*" \ - "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\"" + "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\" \ + "note_about_arbitrary_choice_of_solution=If there exist more than one solution, this method will arbitrarily choose one." \ + "note_about_using_kernel_to_study_multiple_solutions=If you need a complete analysis of the space of solutions, take the one solution obtained by this method and add to it elements of the kernel, as determined by kernel()." \ + "note_about_inexistant_solutions=If only an approximate solution exists, then the result is only such an approximate solution. The case where no solution exists isn't treated separately, as that would require one to use an arbitrary threshold. If you want to check whether a solution exists, just call this function to get a solution and then compute the margin of error, or use MatrixBase::isApprox() directly, for instance like this: \code bool = a_solution_exists = (A*result).isApprox(b, precision); \endcode As long as the input matrices have finite numeric coefficients (no \c inf or \c nan values), the result will also have finite numeric coefficients, there is no risk of getting \nan values if no solution exists."" # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. diff --git a/doc/snippets/LU_solve.cpp b/doc/snippets/LU_solve.cpp index 7323338c3..301074305 100644 --- a/doc/snippets/LU_solve.cpp +++ b/doc/snippets/LU_solve.cpp @@ -1,13 +1,10 @@ -typedef Matrix Matrix2x3; -typedef Matrix Matrix3x2; -Matrix2x3 m = Matrix2x3::Random(); +Matrix m = Matrix::Random(); Matrix2f y = Matrix2f::Random(); cout << "Here is the matrix m:" << endl << m << endl; cout << "Here is the matrix y:" << endl << y << endl; -Matrix3x2 x; -if(m.lu().solve(y, &x)) +Matrix x = m.lu().solve(y); +if((m*x).isApprox(y)) { - assert(y.isApprox(m*x)); cout << "Here is a solution x to the equation mx=y:" << endl << x << endl; } else diff --git a/test/lu.cpp b/test/lu.cpp index 75680b96b..f366f0e36 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -53,10 +53,8 @@ template void lu_non_invertible() m2 = MatrixType::Random(cols,cols2); m3 = m1*m2; m2 = MatrixType::Random(cols,cols2); - VERIFY(lu.solve(m3, &m2)); + m2 = lu.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); - m3 = MatrixType::Random(rows,cols2); - VERIFY(!lu.solve(m3, &m2)); typedef Matrix SquareMatrixType; SquareMatrixType m4(rows, rows), m5(rows, rows); @@ -90,11 +88,9 @@ template void lu_invertible() VERIFY(lu.isInvertible()); VERIFY(lu.image().lu().isInvertible()); m3 = MatrixType::Random(size,size); - lu.solve(m3, &m2); + m2 = lu.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); VERIFY_IS_APPROX(m2, lu.inverse()*m3); - m3 = MatrixType::Random(size,size); - VERIFY(lu.solve(m3, &m2)); } template void lu_verify_assert() @@ -109,7 +105,7 @@ template void lu_verify_assert() VERIFY_RAISES_ASSERT(lu.computeImage(&tmp)) VERIFY_RAISES_ASSERT(lu.kernel()) VERIFY_RAISES_ASSERT(lu.image()) - VERIFY_RAISES_ASSERT(lu.solve(tmp,&tmp)) + VERIFY_RAISES_ASSERT(lu.solve(tmp)) VERIFY_RAISES_ASSERT(lu.determinant()) VERIFY_RAISES_ASSERT(lu.rank()) VERIFY_RAISES_ASSERT(lu.dimensionOfKernel()) From 0ad3494bd370ec992ac1eabaec60ea604ea14a29 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Tue, 22 Sep 2009 21:51:23 -0400 Subject: [PATCH 02/34] fix docs --- Eigen/src/LU/LU.h | 15 ++++++--------- doc/Doxyfile.in | 6 +++--- doc/snippets/PartialLU_solve.cpp | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index 0475b5be3..a5ca9bf8f 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -302,23 +302,20 @@ template class LU /** This method returns a solution x to the equation Ax=b, where A is the matrix of which * *this is the LU decomposition. * - * If no solution exists, then the result is undefined. If only an approximate solution exists, - * then the result is only such an approximate solution. - * * \param b the right-hand-side of the equation to solve. Can be a vector or a matrix, * the only requirement in order for the equation to make sense is that * b.rows()==A.rows(), where A is the matrix of which *this is the LU decomposition. * - * \returns a solution, if any exists. See notes below. + * \returns a solution. + * + * \note_about_inexistant_solutions + * + * \note_about_arbitrary_choice_of_solution + * \note_about_using_kernel_to_study_multiple_solutions * * Example: \include LU_solve.cpp * Output: \verbinclude LU_solve.out * - * \note \note_about_inexistant_solutions - * - * \note \note_about_arbitrary_choice_of_solution - * \note_about_using_kernel_to_study_multiple_solutions - * * \sa TriangularView::solve(), kernel(), computeKernel(), inverse(), computeInverse() */ template diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index b62ba3238..97bda5e41 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -215,10 +215,10 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "eigenvalues_module=This is defined in the %Eigenvalues module. \code #include \endcode" \ "label=\bug" \ "redstar=*" \ - "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\" \ - "note_about_arbitrary_choice_of_solution=If there exist more than one solution, this method will arbitrarily choose one." \ + "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\"" \ + "note_about_arbitrary_choice_of_solution=If there exists more than one solution, this method will arbitrarily choose one." \ "note_about_using_kernel_to_study_multiple_solutions=If you need a complete analysis of the space of solutions, take the one solution obtained by this method and add to it elements of the kernel, as determined by kernel()." \ - "note_about_inexistant_solutions=If only an approximate solution exists, then the result is only such an approximate solution. The case where no solution exists isn't treated separately, as that would require one to use an arbitrary threshold. If you want to check whether a solution exists, just call this function to get a solution and then compute the margin of error, or use MatrixBase::isApprox() directly, for instance like this: \code bool = a_solution_exists = (A*result).isApprox(b, precision); \endcode As long as the input matrices have finite numeric coefficients (no \c inf or \c nan values), the result will also have finite numeric coefficients, there is no risk of getting \nan values if no solution exists."" + "note_about_inexistant_solutions=If only an approximate solution exists, then the result is only such an approximate solution. The case where no solution exists isn't treated separately: non-existent solutions are just \"very inaccurate solutions\". If you want to check whether a solution exists, just call this function to get a solution and then compute the error margin, or use MatrixBase::isApprox() directly, for instance like this: \code bool a_solution_exists = (A*result).isApprox(b, precision); \endcode As long as the input matrices have finite numeric coefficients (no \c inf or \c nan values), the result will also have finite numeric coefficients, there is no risk of getting \c nan values if no solution exists." # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. diff --git a/doc/snippets/PartialLU_solve.cpp b/doc/snippets/PartialLU_solve.cpp index 12441437f..d9ccbe9f0 100644 --- a/doc/snippets/PartialLU_solve.cpp +++ b/doc/snippets/PartialLU_solve.cpp @@ -3,6 +3,6 @@ MatrixXd B = MatrixXd::Random(3,2); cout << "Here is the invertible matrix A:" << endl << A << endl; cout << "Here is the matrix B:" << endl << B << endl; MatrixXd X; -if(A.lu().solve(B, &X)) +A.partialLu().solve(B, &X); cout << "Here is the (unique) solution X to the equation AX=B:" << endl << X << endl; cout << "Relative error: " << (A*X-B).norm() / B.norm() << endl; From 4f9e27034392d4f6744509e52f7b7d829e9070ce Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Tue, 22 Sep 2009 00:16:51 -0400 Subject: [PATCH 03/34] * make LU::kernel() and LU::image() also use ReturnByValue * make them return zero vector in the degenerate case, instead of asserting (let's stick to the principle that we only assert on memory errors) --- Eigen/src/Core/ReturnByValue.h | 2 +- Eigen/src/LU/LU.h | 429 +++++++++++++++++---------------- test/lu.cpp | 9 +- 3 files changed, 229 insertions(+), 211 deletions(-) diff --git a/Eigen/src/Core/ReturnByValue.h b/Eigen/src/Core/ReturnByValue.h index afe9ecbbd..55652db48 100644 --- a/Eigen/src/Core/ReturnByValue.h +++ b/Eigen/src/Core/ReturnByValue.h @@ -51,9 +51,9 @@ struct ei_nested, n, PlainMatrixType> template class ReturnByValue : public MatrixBase > { - typedef typename ei_traits::ReturnMatrixType ReturnMatrixType; public: EIGEN_GENERIC_PUBLIC_INTERFACE(ReturnByValue) + typedef typename ei_traits::ReturnMatrixType ReturnMatrixType; template inline void evalTo(Dest& dst) const { static_cast(this)->evalTo(dst); } diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index a5ca9bf8f..300c7152f 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -26,88 +26,8 @@ #define EIGEN_LU_H template struct ei_lu_solve_impl; - -template -struct ei_traits > -{ - typedef Matrix ReturnMatrixType; -}; - -template -struct ei_lu_solve_impl : public ReturnByValue > -{ - typedef typename ei_cleantype::type RhsNested; - typedef LU LUType; - - ei_lu_solve_impl(const LUType& lu, const Rhs& rhs) - : m_lu(lu), m_rhs(rhs) - {} - - inline int rows() const { return m_lu.matrixLU().cols(); } - inline int cols() const { return m_rhs.cols(); } - - template void evalTo(Dest& dst) const - { - dst.resize(m_lu.matrixLU().cols(), m_rhs.cols()); - if(m_lu.rank()==0) - { - dst.setZero(); - return; - } - - /* The decomposition PAQ = LU can be rewritten as A = P^{-1} L U Q^{-1}. - * So we proceed as follows: - * Step 1: compute c = P * rhs. - * Step 2: replace c by the solution x to Lx = c. Exists because L is invertible. - * Step 3: replace c by the solution x to Ux = c. May or may not exist. - * Step 4: result = Q * c; - */ - - const int rows = m_lu.matrixLU().rows(), - cols = m_lu.matrixLU().cols(), - rank = m_lu.rank(); - ei_assert(m_rhs.rows() == rows); - const int smalldim = std::min(rows, cols); - - typename Rhs::PlainMatrixType c(m_rhs.rows(), m_rhs.cols()); - - // Step 1 - for(int i = 0; i < rows; ++i) - c.row(m_lu.permutationP().coeff(i)) = m_rhs.row(i); - - // Step 2 - m_lu.matrixLU() - .corner(Eigen::TopLeft,smalldim,smalldim) - .template triangularView() - .solveInPlace(c.corner(Eigen::TopLeft, smalldim, c.cols())); - if(rows>cols) - { - c.corner(Eigen::BottomLeft, rows-cols, c.cols()) - -= m_lu.matrixLU().corner(Eigen::BottomLeft, rows-cols, cols) - * c.corner(Eigen::TopLeft, cols, c.cols()); - } - - // Step 3 - m_lu.matrixLU() - .corner(TopLeft, rank, rank) - .template triangularView() - .solveInPlace(c.corner(TopLeft, rank, c.cols())); - - // Step 4 - for(int i = 0; i < rank; ++i) - dst.row(m_lu.permutationQ().coeff(i)) = c.row(i); - for(int i = rank; i < m_lu.matrixLU().cols(); ++i) - dst.row(m_lu.permutationQ().coeff(i)).setZero(); - } - - const LUType& m_lu; - const typename Rhs::Nested m_rhs; -}; +template struct ei_lu_kernel_impl; +template struct ei_lu_image_impl; /** \ingroup LU_Module * @@ -156,25 +76,6 @@ template class LU MatrixType::MaxRowsAtCompileTime) }; - typedef Matrix KernelResultType; - - typedef Matrix ImageResultType; - /** * \brief Default Constructor. * @@ -210,7 +111,15 @@ template class LU ei_assert(m_originalMatrix != 0 && "LU is not initialized."); return m_lu; } - + + /** \returns a pointer to the matrix of which *this is the LU decomposition. + */ + inline const MatrixType* originalMatrix() const + { + ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + return m_originalMatrix; + } + /** \returns a vector of integers, whose size is the number of rows of the matrix being decomposed, * representing the P permutation i.e. the permutation of the rows. For its precise meaning, * see the examples given in the documentation of class LU. @@ -235,69 +144,37 @@ template class LU return m_q; } - /** Computes a basis of the kernel of the matrix, also called the null-space of the matrix. - * - * \note This method is only allowed on non-invertible matrices, as determined by - * isInvertible(). Calling it on an invertible matrix will make an assertion fail. - * - * \param result a pointer to the matrix in which to store the kernel. The columns of this - * matrix will be set to form a basis of the kernel (it will be resized - * if necessary). - * - * Example: \include LU_computeKernel.cpp - * Output: \verbinclude LU_computeKernel.out - * - * \sa kernel(), computeImage(), image() - */ - template - void computeKernel(KernelMatrixType *result) const; - - /** Computes a basis of the image of the matrix, also called the column-space or range of he matrix. - * - * \note Calling this method on the zero matrix will make an assertion fail. - * - * \param result a pointer to the matrix in which to store the image. The columns of this - * matrix will be set to form a basis of the image (it will be resized - * if necessary). - * - * Example: \include LU_computeImage.cpp - * Output: \verbinclude LU_computeImage.out - * - * \sa image(), computeKernel(), kernel() - */ - template - void computeImage(ImageMatrixType *result) const; - /** \returns the kernel of the matrix, also called its null-space. The columns of the returned matrix * will form a basis of the kernel. * - * \note: this method is only allowed on non-invertible matrices, as determined by - * isInvertible(). Calling it on an invertible matrix will make an assertion fail. - * - * \note: this method returns a matrix by value, which induces some inefficiency. - * If you prefer to avoid this overhead, use computeKernel() instead. + * \note If the kernel has dimension zero, then the returned matrix is a column-vector filled with zeros. * * Example: \include LU_kernel.cpp * Output: \verbinclude LU_kernel.out * - * \sa computeKernel(), image() + * \sa image() */ - const KernelResultType kernel() const; + inline const ei_lu_kernel_impl kernel() const + { + ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + return ei_lu_kernel_impl(*this); + } /** \returns the image of the matrix, also called its column-space. The columns of the returned matrix * will form a basis of the kernel. * - * \note: Calling this method on the zero matrix will make an assertion fail. - * - * \note: this method returns a matrix by value, which induces some inefficiency. - * If you prefer to avoid this overhead, use computeImage() instead. + * \note If the image has dimension zero, then the returned matrix is a column-vector filled with zeros. * * Example: \include LU_image.cpp * Output: \verbinclude LU_image.out * - * \sa computeImage(), kernel() + * \sa kernel() */ - const ImageResultType image() const; + inline const ei_lu_image_impl image() const + { + ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + return ei_lu_image_impl(*this); + } /** This method returns a solution x to the equation Ax=b, where A is the matrix of which * *this is the LU decomposition. @@ -316,7 +193,7 @@ template class LU * Example: \include LU_solve.cpp * Output: \verbinclude LU_solve.out * - * \sa TriangularView::solve(), kernel(), computeKernel(), inverse(), computeInverse() + * \sa TriangularView::solve(), kernel(), inverse(), computeInverse() */ template inline const ei_lu_solve_impl @@ -547,71 +424,213 @@ typename ei_traits::Scalar LU::determinant() const return Scalar(m_det_pq) * m_lu.diagonal().prod(); } -template -template -void LU::computeKernel(KernelMatrixType *result) const -{ - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - const int dimker = dimensionOfKernel(), cols = m_lu.cols(); - result->resize(cols, dimker); +/********* Implementation of kernel() **************************************************/ - /* Let us use the following lemma: - * - * Lemma: If the matrix A has the LU decomposition PAQ = LU, - * then Ker A = Q(Ker U). - * - * Proof: trivial: just keep in mind that P, Q, L are invertible. +template +struct ei_traits > +{ + typedef Matrix< + typename MatrixType::Scalar, + MatrixType::ColsAtCompileTime, // the number of rows in the "kernel matrix" + // is the number of cols of the original matrix + // so that the product "matrix * kernel = zero" makes sense + Dynamic, // we don't know at compile-time the dimension of the kernel + MatrixType::Options, + MatrixType::MaxColsAtCompileTime, // see explanation for 2nd template parameter + MatrixType::MaxColsAtCompileTime // the kernel is a subspace of the domain space, + // whose dimension is the number of columns of the original matrix + > ReturnMatrixType; +}; + +template +struct ei_lu_kernel_impl : public ReturnByValue > +{ + typedef LU LUType; + const LUType& m_lu; + int m_dimKer; + + ei_lu_kernel_impl(const LUType& lu) : m_lu(lu), m_dimKer(lu.dimensionOfKernel()) {} + + inline int rows() const { return m_lu.matrixLU().cols(); } + inline int cols() const { return m_dimKer; } + + template void evalTo(Dest& dst) const + { + typedef typename MatrixType::Scalar Scalar; + const int rank = m_lu.rank(), + cols = m_lu.matrixLU().cols(); + if(m_dimKer == 0) + { + // The Kernel is just {0}, so it doesn't have a basis properly speaking, but let's + // avoid crashing/asserting as that depends on floating point calculations. Let's + // just return a single column vector filled with zeros. + dst.resize(cols,1); + dst.setZero(); + return; + } + + /* Let us use the following lemma: + * + * Lemma: If the matrix A has the LU decomposition PAQ = LU, + * then Ker A = Q(Ker U). + * + * Proof: trivial: just keep in mind that P, Q, L are invertible. + */ + + /* Thus, all we need to do is to compute Ker U, and then apply Q. + * + * U is upper triangular, with eigenvalues sorted so that any zeros appear at the end. + * Thus, the diagonal of U ends with exactly + * m_dimKer zero's. Let us use that to construct dimKer linearly + * independent vectors in Ker U. + */ + + dst.resize(cols, m_dimKer); + + Matrix + y(-m_lu.matrixLU().corner(TopRight, rank, m_dimKer)); + + m_lu.matrixLU() + .corner(TopLeft, rank, rank) + .template triangularView().solveInPlace(y); + + for(int i = 0; i < rank; ++i) dst.row(m_lu.permutationQ().coeff(i)) = y.row(i); + for(int i = rank; i < cols; ++i) dst.row(m_lu.permutationQ().coeff(i)).setZero(); + for(int k = 0; k < m_dimKer; ++k) dst.coeffRef(m_lu.permutationQ().coeff(rank+k), k) = Scalar(1); + } +}; + +/***** Implementation of image() *****************************************************/ + +template +struct ei_traits > +{ + typedef Matrix< + typename MatrixType::Scalar, + MatrixType::RowsAtCompileTime, // the image is a subspace of the destination space, whose + // dimension is the number of rows of the original matrix + Dynamic, // we don't know at compile time the dimension of the image (the rank) + MatrixType::Options, + MatrixType::MaxRowsAtCompileTime, // the image matrix will consist of columns from the original matrix, + MatrixType::MaxColsAtCompileTime // so it has the same number of rows and at most as many columns. + > ReturnMatrixType; +}; + +template +struct ei_lu_image_impl : public ReturnByValue > +{ + typedef LU LUType; + const LUType& m_lu; + + ei_lu_image_impl(const LUType& lu) : m_lu(lu) {} + + inline int rows() const { return m_lu.matrixLU().cols(); } + inline int cols() const { return m_lu.rank(); } + + template void evalTo(Dest& dst) const + { + int rank = m_lu.rank(); + if(rank == 0) + { + // The Image is just {0}, so it doesn't have a basis properly speaking, but let's + // avoid crashing/asserting as that depends on floating point calculations. Let's + // just return a single column vector filled with zeros. + dst.resize(m_lu.originalMatrix()->rows(), 1); + dst.setZero(); + return; + } + + dst.resize(m_lu.originalMatrix()->rows(), rank); + for(int i = 0; i < rank; ++i) + dst.col(i) = m_lu.originalMatrix()->col(m_lu.permutationQ().coeff(i)); + } +}; + +/***** Implementation of solve() *****************************************************/ + +template +struct ei_traits > +{ + typedef Matrix ReturnMatrixType; +}; + +template +struct ei_lu_solve_impl : public ReturnByValue > +{ + typedef typename ei_cleantype::type RhsNested; + typedef LU LUType; + const LUType& m_lu; + const typename Rhs::Nested m_rhs; + + ei_lu_solve_impl(const LUType& lu, const Rhs& rhs) + : m_lu(lu), m_rhs(rhs) + {} + + inline int rows() const { return m_lu.matrixLU().cols(); } + inline int cols() const { return m_rhs.cols(); } + + template void evalTo(Dest& dst) const + { + dst.resize(m_lu.matrixLU().cols(), m_rhs.cols()); + if(m_lu.rank()==0) + { + dst.setZero(); + return; + } + + /* The decomposition PAQ = LU can be rewritten as A = P^{-1} L U Q^{-1}. + * So we proceed as follows: + * Step 1: compute c = P * rhs. + * Step 2: replace c by the solution x to Lx = c. Exists because L is invertible. + * Step 3: replace c by the solution x to Ux = c. May or may not exist. + * Step 4: result = Q * c; */ - /* Thus, all we need to do is to compute Ker U, and then apply Q. - * - * U is upper triangular, with eigenvalues sorted so that any zeros appear at the end. - * Thus, the diagonal of U ends with exactly - * m_dimKer zero's. Let us use that to construct m_dimKer linearly - * independent vectors in Ker U. - */ + const int rows = m_lu.matrixLU().rows(), + cols = m_lu.matrixLU().cols(), + rank = m_lu.rank(); + ei_assert(m_rhs.rows() == rows); + const int smalldim = std::min(rows, cols); - Matrix - y(-m_lu.corner(TopRight, m_rank, dimker)); + typename Rhs::PlainMatrixType c(m_rhs.rows(), m_rhs.cols()); - m_lu.corner(TopLeft, m_rank, m_rank) - .template triangularView().solveInPlace(y); + // Step 1 + for(int i = 0; i < rows; ++i) + c.row(m_lu.permutationP().coeff(i)) = m_rhs.row(i); - for(int i = 0; i < m_rank; ++i) result->row(m_q.coeff(i)) = y.row(i); - for(int i = m_rank; i < cols; ++i) result->row(m_q.coeff(i)).setZero(); - for(int k = 0; k < dimker; ++k) result->coeffRef(m_q.coeff(m_rank+k), k) = Scalar(1); -} + // Step 2 + m_lu.matrixLU() + .corner(Eigen::TopLeft,smalldim,smalldim) + .template triangularView() + .solveInPlace(c.corner(Eigen::TopLeft, smalldim, c.cols())); + if(rows>cols) + { + c.corner(Eigen::BottomLeft, rows-cols, c.cols()) + -= m_lu.matrixLU().corner(Eigen::BottomLeft, rows-cols, cols) + * c.corner(Eigen::TopLeft, cols, c.cols()); + } -template -const typename LU::KernelResultType -LU::kernel() const -{ - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - KernelResultType result(m_lu.cols(), dimensionOfKernel()); - computeKernel(&result); - return result; -} + // Step 3 + m_lu.matrixLU() + .corner(TopLeft, rank, rank) + .template triangularView() + .solveInPlace(c.corner(TopLeft, rank, c.cols())); -template -template -void LU::computeImage(ImageMatrixType *result) const -{ - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - result->resize(m_originalMatrix->rows(), m_rank); - for(int i = 0; i < m_rank; ++i) - result->col(i) = m_originalMatrix->col(m_q.coeff(i)); -} + // Step 4 + for(int i = 0; i < rank; ++i) + dst.row(m_lu.permutationQ().coeff(i)) = c.row(i); + for(int i = rank; i < m_lu.matrixLU().cols(); ++i) + dst.row(m_lu.permutationQ().coeff(i)).setZero(); + } +}; -template -const typename LU::ImageResultType -LU::image() const -{ - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - ImageResultType result(m_originalMatrix->rows(), m_rank); - computeImage(&result); - return result; -} +/******* MatrixBase methods *****************************************************************/ /** \lu_module * diff --git a/test/lu.cpp b/test/lu.cpp index f366f0e36..f34c941f7 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -1,7 +1,7 @@ // This file is part of Eigen, a lightweight C++ template library // for linear algebra. // -// Copyright (C) 2008 Benoit Jacob +// Copyright (C) 2008-2009 Benoit Jacob // // Eigen is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -37,9 +37,10 @@ template void lu_non_invertible() createRandomMatrixOfRank(rank, rows, cols, m1); LU lu(m1); - typename LU::KernelResultType m1kernel = lu.kernel(); - typename LU::ImageResultType m1image = lu.image(); + typename ei_lu_kernel_impl::ReturnMatrixType m1kernel = lu.kernel(); + typename ei_lu_image_impl ::ReturnMatrixType m1image = lu.image(); + std::cout << "rows:" << rows << " cols:" << cols << " | " << rank << " ----- " << lu.rank() << std::endl; VERIFY(rank == lu.rank()); VERIFY(cols - lu.rank() == lu.dimensionOfKernel()); VERIFY(!lu.isInjective()); @@ -101,8 +102,6 @@ template void lu_verify_assert() VERIFY_RAISES_ASSERT(lu.matrixLU()) VERIFY_RAISES_ASSERT(lu.permutationP()) VERIFY_RAISES_ASSERT(lu.permutationQ()) - VERIFY_RAISES_ASSERT(lu.computeKernel(&tmp)) - VERIFY_RAISES_ASSERT(lu.computeImage(&tmp)) VERIFY_RAISES_ASSERT(lu.kernel()) VERIFY_RAISES_ASSERT(lu.image()) VERIFY_RAISES_ASSERT(lu.solve(tmp)) From 176c26feb55c6f3faf7f4402f7dc65431d92c8aa Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Tue, 22 Sep 2009 01:41:09 -0400 Subject: [PATCH 04/34] allow to do xpr = solve(b) etc... just by adding a dummy MatrixBase::resize() --- Eigen/src/Core/MatrixBase.h | 17 +++++++++++++++-- test/lu.cpp | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index a1659e2a7..5d5b147d9 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -189,11 +189,24 @@ template class MatrixBase /** \returns the size of the inner dimension according to the storage order, * i.e., the number of rows for a columns major matrix, and the number of cols otherwise */ int innerSize() const { return (int(Flags)&RowMajorBit) ? this->cols() : this->rows(); } - + + /** Only plain matrices, not expressions may be resized; therefore the only useful resize method is + * Matrix::resize(). The present method only asserts that the new size equals the old size, and does + * nothing else. + */ + void resize(int size) + { ei_assert(size == this->size() && "MatrixBase::resize() does not actually allow to resize."); } + /** Only plain matrices, not expressions may be resized; therefore the only useful resize method is + * Matrix::resize(). The present method only asserts that the new size equals the old size, and does + * nothing else. + */ + void resize(int rows, int cols) + { ei_assert(rows == this->rows() && cols == this->cols() && "MatrixBase::resize() does not actually allow to resize."); } + #ifndef EIGEN_PARSED_BY_DOXYGEN /** \internal the plain matrix type corresponding to this expression. Note that is not necessarily * exactly the return type of eval(): in the case of plain matrices, the return type of eval() is a const - * reference to a matrix, not a matrix! It guaranteed however, that the return type of eval() is either + * reference to a matrix, not a matrix! It is however guaranteed that the return type of eval() is either * PlainMatrixType or const PlainMatrixType&. */ typedef typename ei_plain_matrix_type::type PlainMatrixType; diff --git a/test/lu.cpp b/test/lu.cpp index f34c941f7..a7217f5b9 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -40,7 +40,6 @@ template void lu_non_invertible() typename ei_lu_kernel_impl::ReturnMatrixType m1kernel = lu.kernel(); typename ei_lu_image_impl ::ReturnMatrixType m1image = lu.image(); - std::cout << "rows:" << rows << " cols:" << cols << " | " << rank << " ----- " << lu.rank() << std::endl; VERIFY(rank == lu.rank()); VERIFY(cols - lu.rank() == lu.dimensionOfKernel()); VERIFY(!lu.isInjective()); @@ -54,7 +53,8 @@ template void lu_non_invertible() m2 = MatrixType::Random(cols,cols2); m3 = m1*m2; m2 = MatrixType::Random(cols,cols2); - m2 = lu.solve(m3); + // test that the code, which does resize(), may be applied to an xpr + m2.block(0,0,cols,cols2) = lu.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); typedef Matrix SquareMatrixType; From e82ab8a5dd1974db041098f40e8df819e6569d16 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Sat, 26 Sep 2009 11:40:29 -0400 Subject: [PATCH 05/34] move also inverse() to ReturnByValue, by doing a solve on NestByValue. also: adding resize() to MatrixBase was really needed ;) --- Eigen/src/Core/MatrixBase.h | 4 ++-- Eigen/src/LU/Inverse.h | 8 +++----- Eigen/src/LU/LU.h | 31 ++++++++----------------------- test/lu.cpp | 5 ++--- 4 files changed, 15 insertions(+), 33 deletions(-) diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index 5d5b147d9..2c25576a9 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -189,7 +189,7 @@ template class MatrixBase /** \returns the size of the inner dimension according to the storage order, * i.e., the number of rows for a columns major matrix, and the number of cols otherwise */ int innerSize() const { return (int(Flags)&RowMajorBit) ? this->cols() : this->rows(); } - + /** Only plain matrices, not expressions may be resized; therefore the only useful resize method is * Matrix::resize(). The present method only asserts that the new size equals the old size, and does * nothing else. @@ -202,7 +202,7 @@ template class MatrixBase */ void resize(int rows, int cols) { ei_assert(rows == this->rows() && cols == this->cols() && "MatrixBase::resize() does not actually allow to resize."); } - + #ifndef EIGEN_PARSED_BY_DOXYGEN /** \internal the plain matrix type corresponding to this expression. Note that is not necessarily * exactly the return type of eval(): in the case of plain matrices, the return type of eval() is a const diff --git a/Eigen/src/LU/Inverse.h b/Eigen/src/LU/Inverse.h index 248b48044..4bf7ffdc9 100644 --- a/Eigen/src/LU/Inverse.h +++ b/Eigen/src/LU/Inverse.h @@ -200,7 +200,7 @@ struct ei_compute_inverse { static inline void run(const MatrixType& matrix, MatrixType* result) { - matrix.partialLu().computeInverse(result); + result = matrix.partialLu().inverse(); } }; @@ -281,9 +281,7 @@ inline void MatrixBase::computeInverse(PlainMatrixType *result) const template inline const typename MatrixBase::PlainMatrixType MatrixBase::inverse() const { - PlainMatrixType result(rows(), cols()); - computeInverse(&result); - return result; + return inverse(*this); } @@ -299,7 +297,7 @@ struct ei_compute_inverse_with_check typedef typename MatrixType::Scalar Scalar; LU lu( matrix ); if( !lu.isInvertible() ) return false; - lu.computeInverse(result); + *result = lu.inverse(); return true; } }; diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index 300c7152f..fb81713a3 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -58,7 +58,7 @@ template struct ei_lu_image_impl; * \include class_LU.cpp * Output: \verbinclude class_LU.out * - * \sa MatrixBase::lu(), MatrixBase::determinant(), MatrixBase::inverse(), MatrixBase::computeInverse() + * \sa MatrixBase::lu(), MatrixBase::determinant(), MatrixBase::inverse() */ template class LU { @@ -193,7 +193,7 @@ template class LU * Example: \include LU_solve.cpp * Output: \verbinclude LU_solve.out * - * \sa TriangularView::solve(), kernel(), inverse(), computeInverse() + * \sa TriangularView::solve(), kernel(), inverse() */ template inline const ei_lu_solve_impl @@ -277,34 +277,19 @@ template class LU return isInjective() && isSurjective(); } - /** Computes the inverse of the matrix of which *this is the LU decomposition. - * - * \param result a pointer to the matrix into which to store the inverse. Resized if needed. - * - * \note If this matrix is not invertible, *result is left with undefined coefficients. - * Use isInvertible() to first determine whether this matrix is invertible. - * - * \sa MatrixBase::computeInverse(), inverse() - */ - inline void computeInverse(MatrixType *result) const - { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the inverse of a non-square matrix!"); - *result = solve(MatrixType::Identity(m_lu.rows(), m_lu.cols())); - } - /** \returns the inverse of the matrix of which *this is the LU decomposition. * * \note If this matrix is not invertible, the returned matrix has undefined coefficients. * Use isInvertible() to first determine whether this matrix is invertible. * - * \sa computeInverse(), MatrixBase::inverse() + * \sa MatrixBase::inverse() */ - inline MatrixType inverse() const + inline const ei_lu_solve_impl > inverse() const { - MatrixType result; - computeInverse(&result); - return result; + ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the inverse of a non-square matrix!"); + return ei_lu_solve_impl > + (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); } protected: diff --git a/test/lu.cpp b/test/lu.cpp index a7217f5b9..442b87f82 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -40,6 +40,7 @@ template void lu_non_invertible() typename ei_lu_kernel_impl::ReturnMatrixType m1kernel = lu.kernel(); typename ei_lu_image_impl ::ReturnMatrixType m1image = lu.image(); + // std::cerr << rank << " " << lu.rank() << std::endl; VERIFY(rank == lu.rank()); VERIFY(cols - lu.rank() == lu.dimensionOfKernel()); VERIFY(!lu.isInjective()); @@ -54,7 +55,7 @@ template void lu_non_invertible() m3 = m1*m2; m2 = MatrixType::Random(cols,cols2); // test that the code, which does resize(), may be applied to an xpr - m2.block(0,0,cols,cols2) = lu.solve(m3); + m2.block(0,0,m2.rows(),m2.cols()) = lu.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); typedef Matrix SquareMatrixType; @@ -111,7 +112,6 @@ template void lu_verify_assert() VERIFY_RAISES_ASSERT(lu.isInjective()) VERIFY_RAISES_ASSERT(lu.isSurjective()) VERIFY_RAISES_ASSERT(lu.isInvertible()) - VERIFY_RAISES_ASSERT(lu.computeInverse(&tmp)) VERIFY_RAISES_ASSERT(lu.inverse()) PartialLU plu; @@ -119,7 +119,6 @@ template void lu_verify_assert() VERIFY_RAISES_ASSERT(plu.permutationP()) VERIFY_RAISES_ASSERT(plu.solve(tmp,&tmp)) VERIFY_RAISES_ASSERT(plu.determinant()) - VERIFY_RAISES_ASSERT(plu.computeInverse(&tmp)) VERIFY_RAISES_ASSERT(plu.inverse()) } From 924b55e9a92cc30f6caf9e53ea6c5ec96f275dc3 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Sat, 26 Sep 2009 22:48:16 -0400 Subject: [PATCH 06/34] when copying a ReturnByValue into a MatrixBase, first eval it to a PlainMatrixType. This allows to limit the number of instantiations of the big template method evalTo. Also allows to get rid of the dummy MatrixBase::resize(). See "TODO" comment. --- Eigen/src/Core/MatrixBase.h | 13 ------------- Eigen/src/Core/ReturnByValue.h | 18 ++++++++++++++++-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index 2c25576a9..4817f4773 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -190,19 +190,6 @@ template class MatrixBase * i.e., the number of rows for a columns major matrix, and the number of cols otherwise */ int innerSize() const { return (int(Flags)&RowMajorBit) ? this->cols() : this->rows(); } - /** Only plain matrices, not expressions may be resized; therefore the only useful resize method is - * Matrix::resize(). The present method only asserts that the new size equals the old size, and does - * nothing else. - */ - void resize(int size) - { ei_assert(size == this->size() && "MatrixBase::resize() does not actually allow to resize."); } - /** Only plain matrices, not expressions may be resized; therefore the only useful resize method is - * Matrix::resize(). The present method only asserts that the new size equals the old size, and does - * nothing else. - */ - void resize(int rows, int cols) - { ei_assert(rows == this->rows() && cols == this->cols() && "MatrixBase::resize() does not actually allow to resize."); } - #ifndef EIGEN_PARSED_BY_DOXYGEN /** \internal the plain matrix type corresponding to this expression. Note that is not necessarily * exactly the return type of eval(): in the case of plain matrices, the return type of eval() is a const diff --git a/Eigen/src/Core/ReturnByValue.h b/Eigen/src/Core/ReturnByValue.h index 55652db48..297ed2456 100644 --- a/Eigen/src/Core/ReturnByValue.h +++ b/Eigen/src/Core/ReturnByValue.h @@ -65,8 +65,22 @@ template template Derived& MatrixBase::operator=(const ReturnByValue& other) { - other.evalTo(derived()); - return derived(); + // Here we evaluate to a temporary matrix tmp, which we then copy. The main purpose + // of this is to limit the number of instantiations of the template method evalTo(): + // we only instantiate for PlainMatrixType. + // Notice that this behaviour is specific to this operator in MatrixBase. The corresponding operator in class Matrix + // does not evaluate into a temporary first. + // TODO find a way to avoid evaluating into a temporary in the cases that matter. At least Block<> matters + // for the implementation of blocked algorithms. + // Should we: + // - try a trick like for the products, where the destination is abstracted as an array with stride? + // - or just add an operator in class Block, so we get a separate instantiation there (bad) but at least not more + // than that, and at least that's easy to make work? + // - or, since here we're talking about a compromise between code size and performance, let the user choose? + // Not obvious: many users will never find out about this feature, and it's hard to find a good API. + PlainMatrixType tmp; + other.evalTo(tmp); + return derived() = tmp; } #endif // EIGEN_RETURNBYVALUE_H From 41e942d3fb3a3bf26a7fad169adaf3968daa7d46 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Thu, 15 Oct 2009 16:09:17 -0400 Subject: [PATCH 07/34] don't try to finish early --- Eigen/src/LU/LU.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index fb81713a3..6eeb4fae8 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -361,8 +361,8 @@ LU& LU::compute(const MatrixType& matrix) col_of_biggest_in_corner += k; if(k==0) biggest = biggest_in_corner; - // if the corner is negligible, then we have less than full rank, and we can finish early - if(ei_isMuchSmallerThan(biggest_in_corner, biggest, m_precision)) + // if the corner is exactly zero, terminate to avoid generating NaN values + if(biggest_in_corner == RealScalar(0)) { m_rank = k; for(int i = k; i < size; i++) From 8332c232dbce4c7bf117c872924c42fccca5dc97 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Sun, 18 Oct 2009 00:47:40 -0400 Subject: [PATCH 08/34] big huge changes in LU! * continue the decomposition until a pivot is exactly zero; don't try to compute the rank in the decomposition itself. * Instead, methods such as rank() use a new internal parameter called 'threshold' to determine which pivots are to be considered nonzero. * The threshold is by default determined by defaultThreshold() but the user can override that by calling useThreshold(value). * In solve/kernel/image, don't assume that the diagonal of U is sorted in decreasing order, because that's only approximately true. Additional work was needed to extract the right pivots. --- Eigen/src/Core/util/Constants.h | 5 + Eigen/src/LU/LU.h | 272 +++++++++++++++++++++++--------- test/lu.cpp | 8 +- 3 files changed, 205 insertions(+), 80 deletions(-) diff --git a/Eigen/src/Core/util/Constants.h b/Eigen/src/Core/util/Constants.h index affc1d478..226a53c33 100644 --- a/Eigen/src/Core/util/Constants.h +++ b/Eigen/src/Core/util/Constants.h @@ -258,6 +258,11 @@ namespace { EIGEN_UNUSED NoChange_t NoChange; } +struct Default_t {}; +namespace { + EIGEN_UNUSED Default_t Default; +} + enum { IsDense = 0, IsSparse = SparseBit, diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index 6eeb4fae8..3292eaccc 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -40,8 +40,7 @@ template struct ei_lu_image_impl; * This class represents 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 is a rank-revealing LU decomposition. The eigenvalues (diagonal - * coefficients) of U are sorted in such a way that any zeros are at the end, so that the rank - * of A is the index of the first zero on the diagonal of U (with indices starting at 0) if any. + * coefficients) of U are sorted in such a way that any zeros are at the end. * * This decomposition provides the generic approach to solving systems of linear equations, computing * the rank, invertibility, inverse, kernel, and determinant. @@ -112,6 +111,24 @@ template class LU return m_lu; } + /** \returns the number of nonzero pivots in the LU decomposition. + * Here nonzero is meant in the exact sense, not in a fuzzy sense. + * So that notion isn't really intrinsically interesting, but it is + * still useful when implementing algorithms. + * + * \sa rank() + */ + inline int nonzeroPivots() const + { + ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + return m_nonzero_pivots; + } + + /** \returns the absolute value of the biggest pivot, i.e. the biggest + * diagonal coefficient of U. + */ + RealScalar maxPivot() const { return m_maxpivot; } + /** \returns a pointer to the matrix of which *this is the LU decomposition. */ inline const MatrixType* originalMatrix() const @@ -149,6 +166,10 @@ template class LU * * \note If the kernel has dimension zero, then the returned matrix is a column-vector filled with zeros. * + * \note This method has to determine which pivots should be considered nonzero. + * For that, it uses the threshold value that you can control by calling + * useThreshold(const RealScalar&). + * * Example: \include LU_kernel.cpp * Output: \verbinclude LU_kernel.out * @@ -165,6 +186,10 @@ template class LU * * \note If the image has dimension zero, then the returned matrix is a column-vector filled with zeros. * + * \note This method has to determine which pivots should be considered nonzero. + * For that, it uses the threshold value that you can control by calling + * useThreshold(const RealScalar&). + * * Example: \include LU_image.cpp * Output: \verbinclude LU_image.out * @@ -220,61 +245,131 @@ template class LU */ typename ei_traits::Scalar determinant() const; + /** Allows to prescribe a threshold to be used by certain methods, such as rank(), + * who need to determine when pivots are to be considered nonzero. This is not used for the + * LU decomposition itself. + * + * When it needs to get the threshold value, Eigen calls threshold(). By default, this calls + * defaultThreshold(). Once you have called the present method useThreshold(const RealScalar&), + * your value is used instead. + * + * \param threshold The new value to use as the threshold. + * + * A pivot will be considered nonzero if its absolute value is strictly greater than + * \f$ \vert pivot \vert \leqslant precision \times \vert maxpivot \vert \f$ + * where maxpivot is the biggest pivot. + * + * If you want to come back to the default behavior, call useThreshold(Default_t) + */ + LU& useThreshold(const RealScalar& threshold) + { + m_usePrescribedThreshold = true; + m_prescribedThreshold = threshold; + } + + /** Allows to come back to the default behavior, to use the return value of defaultThreshold(). + * + * You should pass the special object Eigen::Default as parameter here. + * \code lu.useThreshold(Eigen::Default); \endcode + * + * See the documentation of useThreshold(const RealScalar&). + */ + LU& useThreshold(Default_t) + { + m_usePrescribedThreshold = false; + } + + /** Returns the threshold that will be used by default by certain methods such as rank(), + * unless the user overrides it by calling useThreshold(const RealScalar&). + * + * See the documentation of useThreshold(const RealScalar&). + * + * Notice that this method returns a value that depends on the size of the matrix being decomposed. + * Namely, it is the product of the diagonal size times the machine epsilon. + * + * \sa threshold() + */ + RealScalar defaultThreshold() const + { + // this formula comes from experimenting (see "LU precision tuning" thread on the list) + // and turns out to be identical to Higham's formula used already in LDLt. + ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + return epsilon() * m_lu.diagonalSize(); + } + + /** Returns the threshold that will be used by certain methods such as rank(). + * + * See the documentation of useThreshold(const RealScalar&). + */ + RealScalar threshold() const + { + return m_usePrescribedThreshold ? m_prescribedThreshold : defaultThreshold(); + } + /** \returns the rank of the matrix of which *this is the LU decomposition. * - * \note This is computed at the time of the construction of the LU decomposition. This - * method does not perform any further computation. + * \note This method has to determine which pivots should be considered nonzero. + * For that, it uses the threshold value that you can control by calling + * useThreshold(const RealScalar&). */ inline int rank() const { ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - return m_rank; + RealScalar premultiplied_threshold = ei_abs(m_maxpivot) * threshold(); + int result = 0; + for(int i = 0; i < m_nonzero_pivots; ++i) + result += (ei_abs(m_lu.coeff(i,i)) > premultiplied_threshold); + return result; } - + /** \returns the dimension of the kernel of the matrix of which *this is the LU decomposition. * - * \note Since the rank is computed at the time of the construction of the LU decomposition, this - * method almost does not perform any further computation. + * \note This method has to determine which pivots should be considered nonzero. + * For that, it uses the threshold value that you can control by calling + * useThreshold(const RealScalar&). */ inline int dimensionOfKernel() const { ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - return m_lu.cols() - m_rank; + return m_lu.cols() - rank(); } /** \returns true if the matrix of which *this is the LU decomposition represents an injective * linear map, i.e. has trivial kernel; false otherwise. * - * \note Since the rank is computed at the time of the construction of the LU decomposition, this - * method almost does not perform any further computation. + * \note This method has to determine which pivots should be considered nonzero. + * For that, it uses the threshold value that you can control by calling + * useThreshold(const RealScalar&). */ inline bool isInjective() const { ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - return m_rank == m_lu.cols(); + return rank() == m_lu.cols(); } /** \returns true if the matrix of which *this is the LU decomposition represents a surjective * linear map; false otherwise. * - * \note Since the rank is computed at the time of the construction of the LU decomposition, this - * method almost does not perform any further computation. + * \note This method has to determine which pivots should be considered nonzero. + * For that, it uses the threshold value that you can control by calling + * useThreshold(const RealScalar&). */ inline bool isSurjective() const { ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - return m_rank == m_lu.rows(); + return rank() == m_lu.rows(); } /** \returns true if the matrix of which *this is the LU decomposition is invertible. * - * \note Since the rank is computed at the time of the construction of the LU decomposition, this - * method almost does not perform any further computation. + * \note This method has to determine which pivots should be considered nonzero. + * For that, it uses the threshold value that you can control by calling + * useThreshold(const RealScalar&). */ inline bool isInvertible() const { ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - return isInjective() && isSurjective(); + return isInjective() && (m_lu.rows() == m_lu.cols()); } /** \returns the inverse of the matrix of which *this is the LU decomposition. @@ -298,31 +393,21 @@ template class LU IntColVectorType m_p; IntRowVectorType m_q; int m_det_pq; - int m_rank; - RealScalar m_precision; + int m_nonzero_pivots; + RealScalar m_maxpivot; + bool m_usePrescribedThreshold; + RealScalar m_prescribedThreshold; }; template LU::LU() - : m_originalMatrix(0), - m_lu(), - m_p(), - m_q(), - m_det_pq(0), - m_rank(-1), - m_precision(precision()) + : m_originalMatrix(0), m_usePrescribedThreshold(false) { } template LU::LU(const MatrixType& matrix) - : m_originalMatrix(0), - m_lu(), - m_p(), - m_q(), - m_det_pq(0), - m_rank(-1), - m_precision(precision()) + : m_originalMatrix(0), m_usePrescribedThreshold(false) { compute(matrix); } @@ -339,16 +424,13 @@ LU& LU::compute(const MatrixType& matrix) const int rows = matrix.rows(); const int cols = matrix.cols(); - // this formula comes from experimenting (see "LU precision tuning" thread on the list) - // and turns out to be identical to Higham's formula used already in LDLt. - m_precision = epsilon() * size; - IntColVectorType rows_transpositions(matrix.rows()); IntRowVectorType cols_transpositions(matrix.cols()); int number_of_transpositions = 0; RealScalar biggest = RealScalar(0); - m_rank = size; + m_nonzero_pivots = size; + m_maxpivot = RealScalar(0); for(int k = 0; k < size; ++k) { int row_of_biggest_in_corner, col_of_biggest_in_corner; @@ -361,10 +443,10 @@ LU& LU::compute(const MatrixType& matrix) col_of_biggest_in_corner += k; if(k==0) biggest = biggest_in_corner; - // if the corner is exactly zero, terminate to avoid generating NaN values + // if the corner is exactly zero, terminate to avoid generating nan/inf values if(biggest_in_corner == RealScalar(0)) { - m_rank = k; + m_nonzero_pivots = k; for(int i = k; i < size; i++) { rows_transpositions.coeffRef(i) = i; @@ -373,6 +455,8 @@ LU& LU::compute(const MatrixType& matrix) break; } + if(biggest_in_corner > m_maxpivot) m_maxpivot = biggest_in_corner; + rows_transpositions.coeffRef(k) = row_of_biggest_in_corner; cols_transpositions.coeffRef(k) = col_of_biggest_in_corner; if(k != row_of_biggest_in_corner) { @@ -431,20 +515,23 @@ template struct ei_lu_kernel_impl : public ReturnByValue > { typedef LU LUType; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; const LUType& m_lu; - int m_dimKer; + int m_rank, m_dimker; - ei_lu_kernel_impl(const LUType& lu) : m_lu(lu), m_dimKer(lu.dimensionOfKernel()) {} + ei_lu_kernel_impl(const LUType& lu) + : m_lu(lu), + m_rank(lu.rank()), + m_dimker(m_lu.matrixLU().cols() - m_rank) {} inline int rows() const { return m_lu.matrixLU().cols(); } - inline int cols() const { return m_dimKer; } + inline int cols() const { return m_dimker; } template void evalTo(Dest& dst) const { - typedef typename MatrixType::Scalar Scalar; - const int rank = m_lu.rank(), - cols = m_lu.matrixLU().cols(); - if(m_dimKer == 0) + const int cols = m_lu.matrixLU().cols(); + if(m_dimker == 0) { // The Kernel is just {0}, so it doesn't have a basis properly speaking, but let's // avoid crashing/asserting as that depends on floating point calculations. Let's @@ -470,19 +557,41 @@ struct ei_lu_kernel_impl : public ReturnByValue > * independent vectors in Ker U. */ - dst.resize(cols, m_dimKer); + dst.resize(cols, m_dimker); + Matrix pivots(m_rank); + RealScalar premultiplied_threshold = m_lu.maxPivot() * m_lu.threshold(); + int p = 0; + for(int i = 0; i < m_lu.nonzeroPivots(); ++i) + if(ei_abs(m_lu.matrixLU().coeff(i,i)) > premultiplied_threshold) + pivots.coeffRef(p++) = i; + ei_assert(p == m_rank && "You hit a bug in Eigen! Please report (backtrace and matrix)!"); + + // FIXME when we get triangularView-for-rectangular-matrices, this can be simplified Matrix - y(-m_lu.matrixLU().corner(TopRight, rank, m_dimKer)); + LUType::MaxSmallDimAtCompileTime, MatrixType::MaxColsAtCompileTime> + m(m_lu.matrixLU().block(0, 0, m_rank, cols)); + for(int i = 0; i < m_rank; ++i) + { + if(i) m.row(i).start(i).setZero(); + m.row(i).end(cols-i) = m_lu.matrixLU().row(pivots.coeff(i)).end(cols-i); + } + m.block(0, 0, m_rank, m_rank).template triangularView().setZero(); + + for(int i = 0; i < m_rank; ++i) + m.col(i).swap(m.col(pivots.coeff(i))); - m_lu.matrixLU() - .corner(TopLeft, rank, rank) - .template triangularView().solveInPlace(y); + m.corner(TopLeft, m_rank, m_rank) + .template triangularView().solveInPlace( + m.corner(TopRight, m_rank, m_dimker) + ); - for(int i = 0; i < rank; ++i) dst.row(m_lu.permutationQ().coeff(i)) = y.row(i); - for(int i = rank; i < cols; ++i) dst.row(m_lu.permutationQ().coeff(i)).setZero(); - for(int k = 0; k < m_dimKer; ++k) dst.coeffRef(m_lu.permutationQ().coeff(rank+k), k) = Scalar(1); + for(int i = m_rank-1; i >= 0; --i) + m.col(i).swap(m.col(pivots.coeff(i))); + + for(int i = 0; i < m_rank; ++i) dst.row(m_lu.permutationQ().coeff(i)) = -m.row(i).end(m_dimker); + for(int i = m_rank; i < cols; ++i) dst.row(m_lu.permutationQ().coeff(i)).setZero(); + for(int k = 0; k < m_dimker; ++k) dst.coeffRef(m_lu.permutationQ().coeff(m_rank+k), k) = Scalar(1); } }; @@ -506,17 +615,19 @@ template struct ei_lu_image_impl : public ReturnByValue > { typedef LU LUType; + typedef typename MatrixType::RealScalar RealScalar; const LUType& m_lu; + int m_rank; - ei_lu_image_impl(const LUType& lu) : m_lu(lu) {} + ei_lu_image_impl(const LUType& lu) + : m_lu(lu), m_rank(lu.rank()) {} inline int rows() const { return m_lu.matrixLU().cols(); } - inline int cols() const { return m_lu.rank(); } + inline int cols() const { return m_rank; } template void evalTo(Dest& dst) const { - int rank = m_lu.rank(); - if(rank == 0) + if(m_rank == 0) { // The Image is just {0}, so it doesn't have a basis properly speaking, but let's // avoid crashing/asserting as that depends on floating point calculations. Let's @@ -526,9 +637,17 @@ struct ei_lu_image_impl : public ReturnByValue > return; } - dst.resize(m_lu.originalMatrix()->rows(), rank); - for(int i = 0; i < rank; ++i) - dst.col(i) = m_lu.originalMatrix()->col(m_lu.permutationQ().coeff(i)); + Matrix pivots(m_rank); + RealScalar premultiplied_threshold = m_lu.maxPivot() * m_lu.threshold(); + int p = 0; + for(int i = 0; i < m_lu.nonzeroPivots(); ++i) + if(ei_abs(m_lu.matrixLU().coeff(i,i)) > premultiplied_threshold) + pivots.coeffRef(p++) = i; + ei_assert(p == m_rank && "You hit a bug in Eigen! Please report (backtrace and matrix)!"); + + dst.resize(m_lu.originalMatrix()->rows(), m_rank); + for(int i = 0; i < m_rank; ++i) + dst.col(i) = m_lu.originalMatrix()->col(m_lu.permutationQ().coeff(pivots.coeff(i))); } }; @@ -562,13 +681,6 @@ struct ei_lu_solve_impl : public ReturnByValue template void evalTo(Dest& dst) const { - dst.resize(m_lu.matrixLU().cols(), m_rhs.cols()); - if(m_lu.rank()==0) - { - dst.setZero(); - return; - } - /* The decomposition PAQ = LU can be rewritten as A = P^{-1} L U Q^{-1}. * So we proceed as follows: * Step 1: compute c = P * rhs. @@ -579,10 +691,18 @@ struct ei_lu_solve_impl : public ReturnByValue const int rows = m_lu.matrixLU().rows(), cols = m_lu.matrixLU().cols(), - rank = m_lu.rank(); + nonzero_pivots = m_lu.nonzeroPivots(); ei_assert(m_rhs.rows() == rows); const int smalldim = std::min(rows, cols); + dst.resize(m_lu.matrixLU().cols(), m_rhs.cols()); + + if(nonzero_pivots == 0) + { + dst.setZero(); + return; + } + typename Rhs::PlainMatrixType c(m_rhs.rows(), m_rhs.cols()); // Step 1 @@ -603,14 +723,14 @@ struct ei_lu_solve_impl : public ReturnByValue // Step 3 m_lu.matrixLU() - .corner(TopLeft, rank, rank) + .corner(TopLeft, nonzero_pivots, nonzero_pivots) .template triangularView() - .solveInPlace(c.corner(TopLeft, rank, c.cols())); + .solveInPlace(c.corner(TopLeft, nonzero_pivots, c.cols())); // Step 4 - for(int i = 0; i < rank; ++i) + for(int i = 0; i < nonzero_pivots; ++i) dst.row(m_lu.permutationQ().coeff(i)) = c.row(i); - for(int i = rank; i < m_lu.matrixLU().cols(); ++i) + for(int i = nonzero_pivots; i < m_lu.matrixLU().cols(); ++i) dst.row(m_lu.permutationQ().coeff(i)).setZero(); } }; diff --git a/test/lu.cpp b/test/lu.cpp index 442b87f82..5fd58f6e8 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -126,19 +126,19 @@ void test_lu() { for(int i = 0; i < g_repeat; i++) { CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_non_invertible() ); +/* CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_invertible() ); CALL_SUBTEST( lu_invertible() ); CALL_SUBTEST( lu_invertible() ); - CALL_SUBTEST( lu_invertible() ); + CALL_SUBTEST( lu_invertible() );*/ } - CALL_SUBTEST( lu_verify_assert() ); +/* CALL_SUBTEST( lu_verify_assert() ); CALL_SUBTEST( lu_verify_assert() ); CALL_SUBTEST( lu_verify_assert() ); CALL_SUBTEST( lu_verify_assert() ); CALL_SUBTEST( lu_verify_assert() ); - CALL_SUBTEST( lu_verify_assert() ); + CALL_SUBTEST( lu_verify_assert() );*/ } From 0255f2827948a3fc0fb9d9e38ffa78d7dbd6e6a8 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Sun, 18 Oct 2009 01:35:07 -0400 Subject: [PATCH 09/34] oops, didn't want to commit that --- test/lu.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/lu.cpp b/test/lu.cpp index 5fd58f6e8..442b87f82 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -126,19 +126,19 @@ void test_lu() { for(int i = 0; i < g_repeat; i++) { CALL_SUBTEST( lu_non_invertible() ); -/* CALL_SUBTEST( lu_non_invertible() ); + CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_invertible() ); CALL_SUBTEST( lu_invertible() ); CALL_SUBTEST( lu_invertible() ); - CALL_SUBTEST( lu_invertible() );*/ + CALL_SUBTEST( lu_invertible() ); } -/* CALL_SUBTEST( lu_verify_assert() ); + CALL_SUBTEST( lu_verify_assert() ); CALL_SUBTEST( lu_verify_assert() ); CALL_SUBTEST( lu_verify_assert() ); CALL_SUBTEST( lu_verify_assert() ); CALL_SUBTEST( lu_verify_assert() ); - CALL_SUBTEST( lu_verify_assert() );*/ + CALL_SUBTEST( lu_verify_assert() ); } From d71c7f42d316266e6e2b534fa0534a423f9652ea Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Sun, 18 Oct 2009 14:20:14 -0400 Subject: [PATCH 10/34] * useThreshold -> setThreshold * remove defaultThreshold() --- Eigen/src/LU/LU.h | 57 ++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index 3292eaccc..65f7dc4c9 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -168,7 +168,7 @@ template class LU * * \note This method has to determine which pivots should be considered nonzero. * For that, it uses the threshold value that you can control by calling - * useThreshold(const RealScalar&). + * setThreshold(const RealScalar&). * * Example: \include LU_kernel.cpp * Output: \verbinclude LU_kernel.out @@ -188,7 +188,7 @@ template class LU * * \note This method has to determine which pivots should be considered nonzero. * For that, it uses the threshold value that you can control by calling - * useThreshold(const RealScalar&). + * setThreshold(const RealScalar&). * * Example: \include LU_image.cpp * Output: \verbinclude LU_image.out @@ -250,67 +250,54 @@ template class LU * LU decomposition itself. * * When it needs to get the threshold value, Eigen calls threshold(). By default, this calls - * defaultThreshold(). Once you have called the present method useThreshold(const RealScalar&), + * defaultThreshold(). Once you have called the present method setThreshold(const RealScalar&), * your value is used instead. * * \param threshold The new value to use as the threshold. * * A pivot will be considered nonzero if its absolute value is strictly greater than - * \f$ \vert pivot \vert \leqslant precision \times \vert maxpivot \vert \f$ + * \f$ \vert pivot \vert \leqslant threshold \times \vert maxpivot \vert \f$ * where maxpivot is the biggest pivot. * - * If you want to come back to the default behavior, call useThreshold(Default_t) + * If you want to come back to the default behavior, call setThreshold(Default_t) */ - LU& useThreshold(const RealScalar& threshold) + LU& setThreshold(const RealScalar& threshold) { m_usePrescribedThreshold = true; m_prescribedThreshold = threshold; } - /** Allows to come back to the default behavior, to use the return value of defaultThreshold(). + /** Allows to come back to the default behavior, letting Eigen use its default formula for + * determining the threshold. * * You should pass the special object Eigen::Default as parameter here. - * \code lu.useThreshold(Eigen::Default); \endcode + * \code lu.setThreshold(Eigen::Default); \endcode * - * See the documentation of useThreshold(const RealScalar&). + * See the documentation of setThreshold(const RealScalar&). */ - LU& useThreshold(Default_t) + LU& setThreshold(Default_t) { m_usePrescribedThreshold = false; } - /** Returns the threshold that will be used by default by certain methods such as rank(), - * unless the user overrides it by calling useThreshold(const RealScalar&). - * - * See the documentation of useThreshold(const RealScalar&). - * - * Notice that this method returns a value that depends on the size of the matrix being decomposed. - * Namely, it is the product of the diagonal size times the machine epsilon. - * - * \sa threshold() - */ - RealScalar defaultThreshold() const - { - // this formula comes from experimenting (see "LU precision tuning" thread on the list) - // and turns out to be identical to Higham's formula used already in LDLt. - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - return epsilon() * m_lu.diagonalSize(); - } - /** Returns the threshold that will be used by certain methods such as rank(). * - * See the documentation of useThreshold(const RealScalar&). + * See the documentation of setThreshold(const RealScalar&). */ RealScalar threshold() const { - return m_usePrescribedThreshold ? m_prescribedThreshold : defaultThreshold(); + ei_assert(m_originalMatrix != 0 || m_usePrescribedThreshold); + return m_usePrescribedThreshold ? m_prescribedThreshold + // this formula comes from experimenting (see "LU precision tuning" thread on the list) + // and turns out to be identical to Higham's formula used already in LDLt. + : epsilon() * m_lu.diagonalSize(); } /** \returns the rank of the matrix of which *this is the LU decomposition. * * \note This method has to determine which pivots should be considered nonzero. * For that, it uses the threshold value that you can control by calling - * useThreshold(const RealScalar&). + * setThreshold(const RealScalar&). */ inline int rank() const { @@ -326,7 +313,7 @@ template class LU * * \note This method has to determine which pivots should be considered nonzero. * For that, it uses the threshold value that you can control by calling - * useThreshold(const RealScalar&). + * setThreshold(const RealScalar&). */ inline int dimensionOfKernel() const { @@ -339,7 +326,7 @@ template class LU * * \note This method has to determine which pivots should be considered nonzero. * For that, it uses the threshold value that you can control by calling - * useThreshold(const RealScalar&). + * setThreshold(const RealScalar&). */ inline bool isInjective() const { @@ -352,7 +339,7 @@ template class LU * * \note This method has to determine which pivots should be considered nonzero. * For that, it uses the threshold value that you can control by calling - * useThreshold(const RealScalar&). + * setThreshold(const RealScalar&). */ inline bool isSurjective() const { @@ -364,7 +351,7 @@ template class LU * * \note This method has to determine which pivots should be considered nonzero. * For that, it uses the threshold value that you can control by calling - * useThreshold(const RealScalar&). + * setThreshold(const RealScalar&). */ inline bool isInvertible() const { From 47eeb4038004b761db08b91cbb04b0b9403c4f18 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Sun, 18 Oct 2009 15:21:19 -0400 Subject: [PATCH 11/34] remove the m_originalMatrix member. Instead, image() now takes the original matrix as parameter. It was the only method to use it anyway. Introduce m_isInitialized. --- Eigen/src/LU/LU.h | 68 +++++++++++++++++++++++------------------------ test/lu.cpp | 6 ++--- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index 65f7dc4c9..9ba6162f0 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -107,7 +107,7 @@ template class LU */ inline const MatrixType& matrixLU() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return m_lu; } @@ -120,7 +120,7 @@ template class LU */ inline int nonzeroPivots() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return m_nonzero_pivots; } @@ -129,14 +129,6 @@ template class LU */ RealScalar maxPivot() const { return m_maxpivot; } - /** \returns a pointer to the matrix of which *this is the LU decomposition. - */ - inline const MatrixType* originalMatrix() const - { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - return m_originalMatrix; - } - /** \returns a vector of integers, whose size is the number of rows of the matrix being decomposed, * representing the P permutation i.e. the permutation of the rows. For its precise meaning, * see the examples given in the documentation of class LU. @@ -145,7 +137,7 @@ template class LU */ inline const IntColVectorType& permutationP() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return m_p; } @@ -157,7 +149,7 @@ template class LU */ inline const IntRowVectorType& permutationQ() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return m_q; } @@ -177,13 +169,18 @@ template class LU */ inline const ei_lu_kernel_impl kernel() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return ei_lu_kernel_impl(*this); } /** \returns the image of the matrix, also called its column-space. The columns of the returned matrix * will form a basis of the kernel. * + * \param originalMatrix the original matrix, of which *this is the LU decomposition. + * The reason why it is needed to pass it here, is that this allows + * a large optimization, as otherwise this method would need to reconstruct it + * from the LU decomposition. + * * \note If the image has dimension zero, then the returned matrix is a column-vector filled with zeros. * * \note This method has to determine which pivots should be considered nonzero. @@ -195,10 +192,12 @@ template class LU * * \sa kernel() */ - inline const ei_lu_image_impl image() const + template + inline const ei_lu_image_impl + image(const MatrixBase& originalMatrix) const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); - return ei_lu_image_impl(*this); + ei_assert(m_isInitialized && "LU is not initialized."); + return ei_lu_image_impl(*this, originalMatrix.derived()); } /** This method returns a solution x to the equation Ax=b, where A is the matrix of which @@ -224,7 +223,7 @@ template class LU inline const ei_lu_solve_impl solve(const MatrixBase& b) const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return ei_lu_solve_impl(*this, b.derived()); } @@ -286,7 +285,7 @@ template class LU */ RealScalar threshold() const { - ei_assert(m_originalMatrix != 0 || m_usePrescribedThreshold); + ei_assert(m_isInitialized || m_usePrescribedThreshold); return m_usePrescribedThreshold ? m_prescribedThreshold // this formula comes from experimenting (see "LU precision tuning" thread on the list) // and turns out to be identical to Higham's formula used already in LDLt. @@ -301,7 +300,7 @@ template class LU */ inline int rank() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); RealScalar premultiplied_threshold = ei_abs(m_maxpivot) * threshold(); int result = 0; for(int i = 0; i < m_nonzero_pivots; ++i) @@ -317,7 +316,7 @@ template class LU */ inline int dimensionOfKernel() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return m_lu.cols() - rank(); } @@ -330,7 +329,7 @@ template class LU */ inline bool isInjective() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return rank() == m_lu.cols(); } @@ -343,7 +342,7 @@ template class LU */ inline bool isSurjective() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return rank() == m_lu.rows(); } @@ -355,7 +354,7 @@ template class LU */ inline bool isInvertible() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); return isInjective() && (m_lu.rows() == m_lu.cols()); } @@ -368,14 +367,14 @@ template class LU */ inline const ei_lu_solve_impl > inverse() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the inverse of a non-square matrix!"); return ei_lu_solve_impl > (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); } protected: - const MatrixType* m_originalMatrix; + bool m_isInitialized; MatrixType m_lu; IntColVectorType m_p; IntRowVectorType m_q; @@ -388,13 +387,13 @@ template class LU template LU::LU() - : m_originalMatrix(0), m_usePrescribedThreshold(false) + : m_isInitialized(false), m_usePrescribedThreshold(false) { } template LU::LU(const MatrixType& matrix) - : m_originalMatrix(0), m_usePrescribedThreshold(false) + : m_isInitialized(false), m_usePrescribedThreshold(false) { compute(matrix); } @@ -402,7 +401,7 @@ LU::LU(const MatrixType& matrix) template LU& LU::compute(const MatrixType& matrix) { - m_originalMatrix = &matrix; + m_isInitialized = true; m_lu = matrix; m_p.resize(matrix.rows()); m_q.resize(matrix.cols()); @@ -475,7 +474,7 @@ LU& LU::compute(const MatrixType& matrix) template typename ei_traits::Scalar LU::determinant() const { - ei_assert(m_originalMatrix != 0 && "LU is not initialized."); + ei_assert(m_isInitialized && "LU is not initialized."); ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the determinant of a non-square matrix!"); return Scalar(m_det_pq) * m_lu.diagonal().prod(); } @@ -605,9 +604,10 @@ struct ei_lu_image_impl : public ReturnByValue > typedef typename MatrixType::RealScalar RealScalar; const LUType& m_lu; int m_rank; + const MatrixType& m_originalMatrix; - ei_lu_image_impl(const LUType& lu) - : m_lu(lu), m_rank(lu.rank()) {} + ei_lu_image_impl(const LUType& lu, const MatrixType& originalMatrix) + : m_lu(lu), m_rank(lu.rank()), m_originalMatrix(originalMatrix) {} inline int rows() const { return m_lu.matrixLU().cols(); } inline int cols() const { return m_rank; } @@ -619,7 +619,7 @@ struct ei_lu_image_impl : public ReturnByValue > // The Image is just {0}, so it doesn't have a basis properly speaking, but let's // avoid crashing/asserting as that depends on floating point calculations. Let's // just return a single column vector filled with zeros. - dst.resize(m_lu.originalMatrix()->rows(), 1); + dst.resize(m_originalMatrix.rows(), 1); dst.setZero(); return; } @@ -632,9 +632,9 @@ struct ei_lu_image_impl : public ReturnByValue > pivots.coeffRef(p++) = i; ei_assert(p == m_rank && "You hit a bug in Eigen! Please report (backtrace and matrix)!"); - dst.resize(m_lu.originalMatrix()->rows(), m_rank); + dst.resize(m_originalMatrix.rows(), m_rank); for(int i = 0; i < m_rank; ++i) - dst.col(i) = m_lu.originalMatrix()->col(m_lu.permutationQ().coeff(pivots.coeff(i))); + dst.col(i) = m_originalMatrix.col(m_lu.permutationQ().coeff(pivots.coeff(i))); } }; diff --git a/test/lu.cpp b/test/lu.cpp index 442b87f82..a08614e28 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -38,7 +38,7 @@ template void lu_non_invertible() LU lu(m1); typename ei_lu_kernel_impl::ReturnMatrixType m1kernel = lu.kernel(); - typename ei_lu_image_impl ::ReturnMatrixType m1image = lu.image(); + typename ei_lu_image_impl ::ReturnMatrixType m1image = lu.image(m1); // std::cerr << rank << " " << lu.rank() << std::endl; VERIFY(rank == lu.rank()); @@ -88,7 +88,7 @@ template void lu_invertible() VERIFY(lu.isInjective()); VERIFY(lu.isSurjective()); VERIFY(lu.isInvertible()); - VERIFY(lu.image().lu().isInvertible()); + VERIFY(lu.image(m1).lu().isInvertible()); m3 = MatrixType::Random(size,size); m2 = lu.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); @@ -104,7 +104,7 @@ template void lu_verify_assert() VERIFY_RAISES_ASSERT(lu.permutationP()) VERIFY_RAISES_ASSERT(lu.permutationQ()) VERIFY_RAISES_ASSERT(lu.kernel()) - VERIFY_RAISES_ASSERT(lu.image()) + VERIFY_RAISES_ASSERT(lu.image(tmp)) VERIFY_RAISES_ASSERT(lu.solve(tmp)) VERIFY_RAISES_ASSERT(lu.determinant()) VERIFY_RAISES_ASSERT(lu.rank()) From 9a700c297490024bd94edfcd73f14c8a984705c9 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 19 Oct 2009 10:56:37 -0400 Subject: [PATCH 12/34] * LU unit test: finally test fixed sizes * ReturnByValue: after all don't eval to temporary for generic MatrixBase impl --- Eigen/src/Core/ReturnByValue.h | 18 ++---------- Eigen/src/LU/LU.h | 33 ++++++++++------------ test/lu.cpp | 50 +++++++++++++++++++++++++++------- 3 files changed, 56 insertions(+), 45 deletions(-) diff --git a/Eigen/src/Core/ReturnByValue.h b/Eigen/src/Core/ReturnByValue.h index 297ed2456..55652db48 100644 --- a/Eigen/src/Core/ReturnByValue.h +++ b/Eigen/src/Core/ReturnByValue.h @@ -65,22 +65,8 @@ template template Derived& MatrixBase::operator=(const ReturnByValue& other) { - // Here we evaluate to a temporary matrix tmp, which we then copy. The main purpose - // of this is to limit the number of instantiations of the template method evalTo(): - // we only instantiate for PlainMatrixType. - // Notice that this behaviour is specific to this operator in MatrixBase. The corresponding operator in class Matrix - // does not evaluate into a temporary first. - // TODO find a way to avoid evaluating into a temporary in the cases that matter. At least Block<> matters - // for the implementation of blocked algorithms. - // Should we: - // - try a trick like for the products, where the destination is abstracted as an array with stride? - // - or just add an operator in class Block, so we get a separate instantiation there (bad) but at least not more - // than that, and at least that's easy to make work? - // - or, since here we're talking about a compromise between code size and performance, let the user choose? - // Not obvious: many users will never find out about this feature, and it's hard to find a good API. - PlainMatrixType tmp; - other.evalTo(tmp); - return derived() = tmp; + other.evalTo(derived()); + return derived(); } #endif // EIGEN_RETURNBYVALUE_H diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index 9ba6162f0..455a7d67e 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -504,25 +504,24 @@ struct ei_lu_kernel_impl : public ReturnByValue > typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; const LUType& m_lu; - int m_rank, m_dimker; + int m_rank, m_cols; ei_lu_kernel_impl(const LUType& lu) : m_lu(lu), m_rank(lu.rank()), - m_dimker(m_lu.matrixLU().cols() - m_rank) {} + m_cols(m_rank==lu.matrixLU().cols() ? 1 : lu.matrixLU().cols() - m_rank){} inline int rows() const { return m_lu.matrixLU().cols(); } - inline int cols() const { return m_dimker; } + inline int cols() const { return m_cols; } template void evalTo(Dest& dst) const { - const int cols = m_lu.matrixLU().cols(); - if(m_dimker == 0) + const int cols = m_lu.matrixLU().cols(), dimker = cols - m_rank; + if(dimker == 0) { // The Kernel is just {0}, so it doesn't have a basis properly speaking, but let's // avoid crashing/asserting as that depends on floating point calculations. Let's // just return a single column vector filled with zeros. - dst.resize(cols,1); dst.setZero(); return; } @@ -543,8 +542,6 @@ struct ei_lu_kernel_impl : public ReturnByValue > * independent vectors in Ker U. */ - dst.resize(cols, m_dimker); - Matrix pivots(m_rank); RealScalar premultiplied_threshold = m_lu.maxPivot() * m_lu.threshold(); int p = 0; @@ -569,15 +566,15 @@ struct ei_lu_kernel_impl : public ReturnByValue > m.corner(TopLeft, m_rank, m_rank) .template triangularView().solveInPlace( - m.corner(TopRight, m_rank, m_dimker) + m.corner(TopRight, m_rank, dimker) ); for(int i = m_rank-1; i >= 0; --i) m.col(i).swap(m.col(pivots.coeff(i))); - for(int i = 0; i < m_rank; ++i) dst.row(m_lu.permutationQ().coeff(i)) = -m.row(i).end(m_dimker); + for(int i = 0; i < m_rank; ++i) dst.row(m_lu.permutationQ().coeff(i)) = -m.row(i).end(dimker); for(int i = m_rank; i < cols; ++i) dst.row(m_lu.permutationQ().coeff(i)).setZero(); - for(int k = 0; k < m_dimker; ++k) dst.coeffRef(m_lu.permutationQ().coeff(m_rank+k), k) = Scalar(1); + for(int k = 0; k < dimker; ++k) dst.coeffRef(m_lu.permutationQ().coeff(m_rank+k), k) = Scalar(1); } }; @@ -603,14 +600,16 @@ struct ei_lu_image_impl : public ReturnByValue > typedef LU LUType; typedef typename MatrixType::RealScalar RealScalar; const LUType& m_lu; - int m_rank; + int m_rank, m_cols; const MatrixType& m_originalMatrix; ei_lu_image_impl(const LUType& lu, const MatrixType& originalMatrix) - : m_lu(lu), m_rank(lu.rank()), m_originalMatrix(originalMatrix) {} + : m_lu(lu), m_rank(lu.rank()), + m_cols(m_rank == 0 ? 1 : m_rank), + m_originalMatrix(originalMatrix) {} - inline int rows() const { return m_lu.matrixLU().cols(); } - inline int cols() const { return m_rank; } + inline int rows() const { return m_lu.matrixLU().rows(); } + inline int cols() const { return m_cols; } template void evalTo(Dest& dst) const { @@ -619,7 +618,6 @@ struct ei_lu_image_impl : public ReturnByValue > // The Image is just {0}, so it doesn't have a basis properly speaking, but let's // avoid crashing/asserting as that depends on floating point calculations. Let's // just return a single column vector filled with zeros. - dst.resize(m_originalMatrix.rows(), 1); dst.setZero(); return; } @@ -632,7 +630,6 @@ struct ei_lu_image_impl : public ReturnByValue > pivots.coeffRef(p++) = i; ei_assert(p == m_rank && "You hit a bug in Eigen! Please report (backtrace and matrix)!"); - dst.resize(m_originalMatrix.rows(), m_rank); for(int i = 0; i < m_rank; ++i) dst.col(i) = m_originalMatrix.col(m_lu.permutationQ().coeff(pivots.coeff(i))); } @@ -682,8 +679,6 @@ struct ei_lu_solve_impl : public ReturnByValue ei_assert(m_rhs.rows() == rows); const int smalldim = std::min(rows, cols); - dst.resize(m_lu.matrixLU().cols(), m_rhs.cols()); - if(nonzero_pivots == 0) { dst.setZero(); diff --git a/test/lu.cpp b/test/lu.cpp index a08614e28..90755f59c 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -30,15 +30,43 @@ template void lu_non_invertible() /* this test covers the following files: LU.h */ - int rows = ei_random(20,200), cols = ei_random(20,200), cols2 = ei_random(20,200); + int rows, cols, cols2; + if(MatrixType::RowsAtCompileTime==Dynamic) + { + rows = ei_random(20,200); + } + else + { + rows = MatrixType::RowsAtCompileTime; + } + if(MatrixType::ColsAtCompileTime==Dynamic) + { + cols = ei_random(20,200); + cols2 = ei_random(20,200); + } + else + { + cols2 = cols = MatrixType::ColsAtCompileTime; + } + + typedef typename ei_lu_kernel_impl::ReturnMatrixType KernelMatrixType; + typedef typename ei_lu_image_impl ::ReturnMatrixType ImageMatrixType; + typedef Matrix DynamicMatrixType; + typedef Matrix + CMatrixType; + int rank = ei_random(1, std::min(rows, cols)-1); - MatrixType m1(rows, cols), m2(cols, cols2), m3(rows, cols2), k(1,1); + // The image of the zero matrix should consist of a single (zero) column vector + VERIFY((MatrixType::Zero(rows,cols).lu().image(MatrixType::Zero(rows,cols)).cols() == 1)); + + MatrixType m1(rows, cols), m3(rows, cols2); + CMatrixType m2(cols, cols2); createRandomMatrixOfRank(rank, rows, cols, m1); LU lu(m1); - typename ei_lu_kernel_impl::ReturnMatrixType m1kernel = lu.kernel(); - typename ei_lu_image_impl ::ReturnMatrixType m1image = lu.image(m1); + KernelMatrixType m1kernel = lu.kernel(); + ImageMatrixType m1image = lu.image(m1); // std::cerr << rank << " " << lu.rank() << std::endl; VERIFY(rank == lu.rank()); @@ -48,19 +76,18 @@ template void lu_non_invertible() VERIFY(!lu.isSurjective()); VERIFY((m1 * m1kernel).isMuchSmallerThan(m1)); VERIFY(m1image.lu().rank() == rank); - MatrixType sidebyside(m1.rows(), m1.cols() + m1image.cols()); + DynamicMatrixType sidebyside(m1.rows(), m1.cols() + m1image.cols()); sidebyside << m1, m1image; VERIFY(sidebyside.lu().rank() == rank); - m2 = MatrixType::Random(cols,cols2); + m2 = CMatrixType::Random(cols,cols2); m3 = m1*m2; - m2 = MatrixType::Random(cols,cols2); + m2 = CMatrixType::Random(cols,cols2); // test that the code, which does resize(), may be applied to an xpr m2.block(0,0,m2.rows(),m2.cols()) = lu.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); - typedef Matrix SquareMatrixType; - SquareMatrixType m4(rows, rows), m5(rows, rows); - createRandomMatrixOfRank(rows/2, rows, rows, m4); + CMatrixType m4(cols, cols), m5(cols, cols); + createRandomMatrixOfRank(cols/2, cols, cols, m4); VERIFY(!m4.computeInverseWithCheck(&m5)); } @@ -84,6 +111,7 @@ template void lu_invertible() LU lu(m1); VERIFY(0 == lu.dimensionOfKernel()); + VERIFY(lu.kernel().cols() == 1); // the kernel() should consist of a single (zero) column vector VERIFY(size == lu.rank()); VERIFY(lu.isInjective()); VERIFY(lu.isSurjective()); @@ -125,6 +153,8 @@ template void lu_verify_assert() void test_lu() { for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST( lu_non_invertible() ); + CALL_SUBTEST( (lu_non_invertible >()) ); CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_non_invertible() ); From 580672ea43a29c7f0d10b6d92ffc07518500d4da Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 19 Oct 2009 13:29:00 -0400 Subject: [PATCH 13/34] Add new default option EIGEN_SPLIT_LARGE_TESTS and cmake macro ei_add_test_multi. When enabled, large tests are split into smaller executables. This needs minimal changes in the unit tests. Updated the LU test to use it. --- cmake/EigenTesting.cmake | 131 +++++++++++++++++++++++++++------------ test/CMakeLists.txt | 4 +- test/lu.cpp | 26 +++++--- 3 files changed, 111 insertions(+), 50 deletions(-) diff --git a/cmake/EigenTesting.cmake b/cmake/EigenTesting.cmake index b8e159be7..996b8443f 100644 --- a/cmake/EigenTesting.cmake +++ b/cmake/EigenTesting.cmake @@ -18,30 +18,11 @@ macro(ei_add_property prop value) set_property(GLOBAL PROPERTY ${prop} "${previous} ${value}") endmacro(ei_add_property) -# Macro to add a test -# -# the unique parameter testname must correspond to a file -# .cpp which follows this pattern: -# -# #include "main.h" -# void test_() { ... } -# -# this macro add an executable test_ as well as a ctest test -# named . -# -# it also adds another executable debug_ that compiles in full debug mode -# and is not added to the test target. The idea is that when a test fails you want -# a quick way of rebuilding this specific test in full debug mode. -# -# On platforms with bash simply run: -# "ctest -V" or "ctest -V -R " -# On other platform use ctest as usual -# -macro(ei_add_test testname) - - set(targetname test_${testname}) +#internal. See documentation of ei_add_test for details. +macro(ei_add_test_internal testname testname_with_suffix) + set(targetname test_${testname_with_suffix}) if(NOT MSVC_IDE) - set(debug_targetname debug_${testname}) + set(debug_targetname debug_${testname_with_suffix}) endif(NOT MSVC_IDE) set(filename ${testname}.cpp) @@ -80,10 +61,14 @@ macro(ei_add_test testname) endif(NOT EIGEN_NO_ASSERTION_CHECKING) - # let the user pass e.g. optimization flags, but don't apply them to the debug target - if(${ARGC} GREATER 1) - ei_add_target_property(${targetname} COMPILE_FLAGS "${ARGV1}") - endif(${ARGC} GREATER 1) + # let the user pass flags. Note that if the user passes an optimization flag here, it's important that + # we counter it by a no-optimization flag! + if(${ARGC} GREATER 2) + ei_add_target_property(${targetname} COMPILE_FLAGS "${ARGV2}") + if(NOT MSVC_IDE) + ei_add_target_property(${debug_targetname} COMPILE_FLAGS "${ARGV2} ${EI_NO_OPTIMIZATION_FLAG}") + endif(NOT MSVC_IDE) + endif(${ARGC} GREATER 2) # for the debug target, add full debug options if(CMAKE_COMPILER_IS_GNUCXX) @@ -101,29 +86,94 @@ macro(ei_add_test testname) endif(NOT MSVC_IDE) target_link_libraries(${targetname} ${EXTERNAL_LIBS}) - if(${ARGC} GREATER 2) - string(STRIP "${ARGV2}" ARGV2_stripped) - string(LENGTH "${ARGV2_stripped}" ARGV2_stripped_length) - if(${ARGV2_stripped_length} GREATER 0) - target_link_libraries(${targetname} ${ARGV2}) + if(${ARGC} GREATER 3) + string(STRIP "${ARGV3}" ARGV3_stripped) + string(LENGTH "${ARGV3_stripped}" ARGV3_stripped_length) + if(${ARGV3_stripped_length} GREATER 0) + target_link_libraries(${targetname} ${ARGV3}) if(NOT MSVC_IDE) - target_link_libraries(${debug_targetname} ${ARGV2}) + target_link_libraries(${debug_targetname} ${ARGV3}) endif(NOT MSVC_IDE) - endif(${ARGV2_stripped_length} GREATER 0) - endif(${ARGC} GREATER 2) + endif(${ARGV3_stripped_length} GREATER 0) + endif(${ARGC} GREATER 3) if(WIN32) if(CYGWIN) - add_test(${testname} "${Eigen_SOURCE_DIR}/test/runtest.sh" "${testname}") + add_test(${testname_with_suffix} "${Eigen_SOURCE_DIR}/test/runtest.sh" "${testname_with_suffix}") else(CYGWIN) - add_test(${testname} "${targetname}") + add_test(${testname_with_suffix} "${targetname}") endif(CYGWIN) else(WIN32) - add_test(${testname} "${Eigen_SOURCE_DIR}/test/runtest.sh" "${testname}") + add_test(${testname_with_suffix} "${Eigen_SOURCE_DIR}/test/runtest.sh" "${testname_with_suffix}") endif(WIN32) +endmacro(ei_add_test_internal) + + +# Macro to add a test +# +# the unique parameter testname must correspond to a file +# .cpp which follows this pattern: +# +# #include "main.h" +# void test_() { ... } +# +# this macro add an executable test_ as well as a ctest test +# named . +# +# it also adds another executable debug_ that compiles in full debug mode +# and is not added to the test target. The idea is that when a test fails you want +# a quick way of rebuilding this specific test in full debug mode. +# +# On platforms with bash simply run: +# "ctest -V" or "ctest -V -R " +# On other platform use ctest as usual +# +macro(ei_add_test testname) + ei_add_test_internal(${testname} ${testname} "${ARGV1}" "${ARGV2}") endmacro(ei_add_test) +# Macro to add a multi-part test. Allows to split large tests into multiple executables. +# +# The first parameter is the number of parts. +# +# the second parameter testname must correspond to a file +# .cpp which follows this pattern: +# +# #include "main.h" +# void test_() { ... } +# +# this macro adds executables test__N for N ranging from 1 to the number of parts +# (first parameter) as well as corresponding ctest tests named . +# +# it also adds corresponding debug targets and ctest tests, see the documentation of ei_add_test. +# +# On platforms with bash simply run: +# "ctest -V" or "ctest -V -R " +# On other platforms use ctest as usual +macro(ei_add_test_multi parts testname) + if(EIGEN_SPLIT_LARGE_TESTS) + add_custom_target(test_${testname}) + if(NOT MSVC_IDE) + add_custom_target(debug_${testname}) + endif(NOT MSVC_IDE) + foreach(part RANGE 1 ${parts}) + message("multipart argv2 ${ARGV2} argv3 ${ARGV3}") + ei_add_test_internal(${testname} ${testname}_${part} "${ARGV2} -DEIGEN_TEST_PART_${part}" "${ARGV3}") + add_dependencies(test_${testname} test_${testname}_${part}) + if(NOT MSVC_IDE) + add_dependencies(debug_${testname} debug_${testname}_${part}) + endif(NOT MSVC_IDE) + endforeach(part) + else(EIGEN_SPLIT_LARGE_TESTS) + set(symbols_to_enable_all_parts "") + foreach(part RANGE 1 ${parts}) + set(symbols_to_enable_all_parts "${symbols_to_enable_all_parts} -DEIGEN_TEST_PART_${part}") + endforeach(part) + ei_add_test_internal(${testname} ${testname} "${ARGV2} ${symbols_to_enable_all_parts}" "${ARGV3}") + endif(EIGEN_SPLIT_LARGE_TESTS) +endmacro(ei_add_test_multi) + # print a summary of the different options macro(ei_testing_print_summary) @@ -204,12 +254,15 @@ if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_FLAGS} -g2") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${COVERAGE_FLAGS} -O2 -g2") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${COVERAGE_FLAGS} -fno-inline-functions") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${COVERAGE_FLAGS} -O0 -g2") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${COVERAGE_FLAGS} -O0 -g3") endif(CMAKE_SYSTEM_NAME MATCHES Linux) set(EI_OFLAG "-O2") + set(EI_NO_OPTIMIZATION_FLAG "-O0") elseif(MSVC) set(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Zi /Ob0 /Od" CACHE STRING "Flags used by the compiler during debug builds." FORCE) set(EI_OFLAG "/O2") + set(EI_NO_OPTIMIZATION_FLAG "/O0") else(CMAKE_COMPILER_IS_GNUCXX) set(EI_OFLAG "") + set(EI_NO_OPTIMIZATION_FLAG "") endif(CMAKE_COMPILER_IS_GNUCXX) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b46971493..fdc4afdd1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,6 +3,8 @@ add_custom_target(btest) include(EigenTesting) ei_init_testing() +option(EIGEN_SPLIT_LARGE_TESTS "Split large tests into smaller executables" ON) + find_package(GSL) if(GSL_FOUND AND GSL_VERSION_MINOR LESS 9) set(GSL_FOUND "") @@ -119,7 +121,7 @@ ei_add_test(product_notemporary ${EI_OFLAG}) ei_add_test(stable_norm) ei_add_test(bandmatrix) ei_add_test(cholesky " " "${GSL_LIBRARIES}") -ei_add_test(lu ${EI_OFLAG}) +ei_add_test_multi(6 lu ${EI_OFLAG}) ei_add_test(determinant) ei_add_test(inverse ${EI_OFLAG}) ei_add_test(qr) diff --git a/test/lu.cpp b/test/lu.cpp index 90755f59c..1dd78bb46 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -153,22 +153,28 @@ template void lu_verify_assert() void test_lu() { for(int i = 0; i < g_repeat; i++) { +#if defined EIGEN_TEST_PART_1 CALL_SUBTEST( lu_non_invertible() ); + CALL_SUBTEST( lu_verify_assert() ); +#elif defined EIGEN_TEST_PART_2 CALL_SUBTEST( (lu_non_invertible >()) ); + CALL_SUBTEST( (lu_verify_assert >()) ); +#elif defined EIGEN_TEST_PART_3 CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_invertible() ); + CALL_SUBTEST( lu_verify_assert() ); +#elif defined EIGEN_TEST_PART_4 + CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_invertible() ); + CALL_SUBTEST( lu_verify_assert() ); +#elif defined EIGEN_TEST_PART_5 + CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_invertible() ); + CALL_SUBTEST( lu_verify_assert() ); +#elif defined EIGEN_TEST_PART_6 + CALL_SUBTEST( lu_non_invertible() ); CALL_SUBTEST( lu_invertible() ); + CALL_SUBTEST( lu_verify_assert() ); +#endif } - - CALL_SUBTEST( lu_verify_assert() ); - CALL_SUBTEST( lu_verify_assert() ); - CALL_SUBTEST( lu_verify_assert() ); - CALL_SUBTEST( lu_verify_assert() ); - CALL_SUBTEST( lu_verify_assert() ); - CALL_SUBTEST( lu_verify_assert() ); } From 6c1b91678b5406fb10fe5d4692014f8017d1cf1c Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 19 Oct 2009 14:40:35 -0400 Subject: [PATCH 14/34] kill ei_add_test_multi. Now the macro ei_add_test does all that automatically, by parsing the source file. No risk anymore to specify the wrong number of tests! Also, introduce CALL_SUBTESTX for X=1..10 that allows to port existing code much quicker. And port already the product* and eigensolver* files. --- cmake/EigenTesting.cmake | 74 +++++++++++++++++++------------- test/CMakeLists.txt | 2 +- test/eigensolver_complex.cpp | 4 +- test/eigensolver_generic.cpp | 20 ++++----- test/eigensolver_selfadjoint.cpp | 18 ++++---- test/lu.cpp | 44 +++++++++---------- test/main.h | 60 ++++++++++++++++++++++++++ test/product_extra.cpp | 6 +-- test/product_large.cpp | 12 +++--- test/product_notemporary.cpp | 4 +- test/product_selfadjoint.cpp | 16 +++---- test/product_small.cpp | 12 +++--- test/product_symm.cpp | 8 ++-- test/product_syrk.cpp | 4 +- test/product_trmm.cpp | 4 +- test/product_trmv.cpp | 12 +++--- test/product_trsm.cpp | 4 +- 17 files changed, 189 insertions(+), 115 deletions(-) diff --git a/cmake/EigenTesting.cmake b/cmake/EigenTesting.cmake index 996b8443f..f33e9fea1 100644 --- a/cmake/EigenTesting.cmake +++ b/cmake/EigenTesting.cmake @@ -1,5 +1,3 @@ - - option(EIGEN_NO_ASSERTION_CHECKING "Disable checking of assertions" OFF) # similar to set_target_properties but append the property instead of overwriting it @@ -118,6 +116,10 @@ endmacro(ei_add_test_internal) # #include "main.h" # void test_() { ... } # +# Depending on the contents of that file, this macro can have 2 behaviors. +# +# A. Default behavior +# # this macro add an executable test_ as well as a ctest test # named . # @@ -129,50 +131,60 @@ endmacro(ei_add_test_internal) # "ctest -V" or "ctest -V -R " # On other platform use ctest as usual # +# B. Multi-part behavior +# +# If the source file matches the regexp +# CALL_SUBTEST[0-9]+|EIGEN_TEST_PART_[0-9]+ +# then it is interpreted as a multi-part test. The behavior then depends on the +# CMake option EIGEN_SPLIT_LARGE_TESTS, which is ON by default. +# +# If EIGEN_SPLIT_LARGE_TESTS is OFF, the behavior is the same as in A (the multi-part +# aspect is ignored). +# +# If EIGEN_SPLIT_LARGE_TESTS is ON, the test is split into multiple executables +# test__ +# where N runs from 1 to the greatest occurence found in the source file. Each of these +# executables is built passing -DEIGEN_TEST_PART_N. This allows to split large tests +# into smaller executables. +# +# The same holds for the debug executables. +# +# Moreover, targets test_ and debug_ are still generated, they +# have the effect of building all the parts of the test. +# +# Again, ctest -R allows to run all matching tests. +# macro(ei_add_test testname) - ei_add_test_internal(${testname} ${testname} "${ARGV1}" "${ARGV2}") -endmacro(ei_add_test) - -# Macro to add a multi-part test. Allows to split large tests into multiple executables. -# -# The first parameter is the number of parts. -# -# the second parameter testname must correspond to a file -# .cpp which follows this pattern: -# -# #include "main.h" -# void test_() { ... } -# -# this macro adds executables test__N for N ranging from 1 to the number of parts -# (first parameter) as well as corresponding ctest tests named . -# -# it also adds corresponding debug targets and ctest tests, see the documentation of ei_add_test. -# -# On platforms with bash simply run: -# "ctest -V" or "ctest -V -R " -# On other platforms use ctest as usual -macro(ei_add_test_multi parts testname) - if(EIGEN_SPLIT_LARGE_TESTS) + file(READ "${testname}.cpp" test_source) + set(parts 0) + string(REGEX MATCHALL "CALL_SUBTEST[0-9]+|EIGEN_TEST_PART_[0-9]+" occurences "${test_source}") + foreach(occurence ${occurences}) + string(REGEX MATCH "([0-9]+)" _number_in_occurence "${occurence}") + set(number ${CMAKE_MATCH_1}) + if(${number} GREATER ${parts}) + set(parts ${number}) + endif(${number} GREATER ${parts}) + endforeach(occurence) + if(EIGEN_SPLIT_LARGE_TESTS AND (parts GREATER 0)) add_custom_target(test_${testname}) if(NOT MSVC_IDE) add_custom_target(debug_${testname}) endif(NOT MSVC_IDE) foreach(part RANGE 1 ${parts}) - message("multipart argv2 ${ARGV2} argv3 ${ARGV3}") - ei_add_test_internal(${testname} ${testname}_${part} "${ARGV2} -DEIGEN_TEST_PART_${part}" "${ARGV3}") + ei_add_test_internal(${testname} ${testname}_${part} "${ARGV1} -DEIGEN_TEST_PART_${part}" "${ARGV2}") add_dependencies(test_${testname} test_${testname}_${part}) if(NOT MSVC_IDE) add_dependencies(debug_${testname} debug_${testname}_${part}) endif(NOT MSVC_IDE) endforeach(part) - else(EIGEN_SPLIT_LARGE_TESTS) + else(EIGEN_SPLIT_LARGE_TESTS AND (parts GREATER 0)) set(symbols_to_enable_all_parts "") foreach(part RANGE 1 ${parts}) set(symbols_to_enable_all_parts "${symbols_to_enable_all_parts} -DEIGEN_TEST_PART_${part}") endforeach(part) - ei_add_test_internal(${testname} ${testname} "${ARGV2} ${symbols_to_enable_all_parts}" "${ARGV3}") - endif(EIGEN_SPLIT_LARGE_TESTS) -endmacro(ei_add_test_multi) + ei_add_test_internal(${testname} ${testname} "${ARGV1} ${symbols_to_enable_all_parts}" "${ARGV2}") + endif(EIGEN_SPLIT_LARGE_TESTS AND (parts GREATER 0)) +endmacro(ei_add_test) # print a summary of the different options macro(ei_testing_print_summary) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fdc4afdd1..0139f2d0b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -121,7 +121,7 @@ ei_add_test(product_notemporary ${EI_OFLAG}) ei_add_test(stable_norm) ei_add_test(bandmatrix) ei_add_test(cholesky " " "${GSL_LIBRARIES}") -ei_add_test_multi(6 lu ${EI_OFLAG}) +ei_add_test(lu ${EI_OFLAG}) ei_add_test(determinant) ei_add_test(inverse ${EI_OFLAG}) ei_add_test(qr) diff --git a/test/eigensolver_complex.cpp b/test/eigensolver_complex.cpp index 38ede7c4a..a8e6c709e 100644 --- a/test/eigensolver_complex.cpp +++ b/test/eigensolver_complex.cpp @@ -54,8 +54,8 @@ template void eigensolver(const MatrixType& m) void test_eigensolver_complex() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( eigensolver(Matrix4cf()) ); - CALL_SUBTEST( eigensolver(MatrixXcd(14,14)) ); + CALL_SUBTEST1( eigensolver(Matrix4cf()) ); + CALL_SUBTEST2( eigensolver(MatrixXcd(14,14)) ); } } diff --git a/test/eigensolver_generic.cpp b/test/eigensolver_generic.cpp index e2b2055b4..6c91ebabe 100644 --- a/test/eigensolver_generic.cpp +++ b/test/eigensolver_generic.cpp @@ -75,18 +75,18 @@ template void eigensolver_verify_assert() void test_eigensolver_generic() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( eigensolver(Matrix4f()) ); - CALL_SUBTEST( eigensolver(MatrixXd(17,17)) ); + CALL_SUBTEST1( eigensolver(Matrix4f()) ); + CALL_SUBTEST2( eigensolver(MatrixXd(17,17)) ); // some trivial but implementation-wise tricky cases - CALL_SUBTEST( eigensolver(MatrixXd(1,1)) ); - CALL_SUBTEST( eigensolver(MatrixXd(2,2)) ); - CALL_SUBTEST( eigensolver(Matrix()) ); - CALL_SUBTEST( eigensolver(Matrix()) ); + CALL_SUBTEST2( eigensolver(MatrixXd(1,1)) ); + CALL_SUBTEST2( eigensolver(MatrixXd(2,2)) ); + CALL_SUBTEST3( eigensolver(Matrix()) ); + CALL_SUBTEST4( eigensolver(Matrix2d()) ); } - CALL_SUBTEST( eigensolver_verify_assert() ); - CALL_SUBTEST( eigensolver_verify_assert() ); - CALL_SUBTEST( eigensolver_verify_assert() ); - CALL_SUBTEST( eigensolver_verify_assert() ); + CALL_SUBTEST1( eigensolver_verify_assert() ); + CALL_SUBTEST2( eigensolver_verify_assert() ); + CALL_SUBTEST4( eigensolver_verify_assert() ); + CALL_SUBTEST5( eigensolver_verify_assert() ); } diff --git a/test/eigensolver_selfadjoint.cpp b/test/eigensolver_selfadjoint.cpp index 3836c074b..27a5f2246 100644 --- a/test/eigensolver_selfadjoint.cpp +++ b/test/eigensolver_selfadjoint.cpp @@ -117,17 +117,17 @@ void test_eigensolver_selfadjoint() { for(int i = 0; i < g_repeat; i++) { // very important to test a 3x3 matrix since we provide a special path for it - CALL_SUBTEST( selfadjointeigensolver(Matrix3f()) ); - CALL_SUBTEST( selfadjointeigensolver(Matrix4d()) ); - CALL_SUBTEST( selfadjointeigensolver(MatrixXf(10,10)) ); - CALL_SUBTEST( selfadjointeigensolver(MatrixXd(19,19)) ); - CALL_SUBTEST( selfadjointeigensolver(MatrixXcd(17,17)) ); + CALL_SUBTEST1( selfadjointeigensolver(Matrix3f()) ); + CALL_SUBTEST2( selfadjointeigensolver(Matrix4d()) ); + CALL_SUBTEST3( selfadjointeigensolver(MatrixXf(10,10)) ); + CALL_SUBTEST4( selfadjointeigensolver(MatrixXd(19,19)) ); + CALL_SUBTEST5( selfadjointeigensolver(MatrixXcd(17,17)) ); // some trivial but implementation-wise tricky cases - CALL_SUBTEST( selfadjointeigensolver(MatrixXd(1,1)) ); - CALL_SUBTEST( selfadjointeigensolver(MatrixXd(2,2)) ); - CALL_SUBTEST( selfadjointeigensolver(Matrix()) ); - CALL_SUBTEST( selfadjointeigensolver(Matrix()) ); + CALL_SUBTEST4( selfadjointeigensolver(MatrixXd(1,1)) ); + CALL_SUBTEST4( selfadjointeigensolver(MatrixXd(2,2)) ); + CALL_SUBTEST6( selfadjointeigensolver(Matrix()) ); + CALL_SUBTEST7( selfadjointeigensolver(Matrix()) ); } } diff --git a/test/lu.cpp b/test/lu.cpp index 1dd78bb46..1813172cd 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -153,28 +153,26 @@ template void lu_verify_assert() void test_lu() { for(int i = 0; i < g_repeat; i++) { -#if defined EIGEN_TEST_PART_1 - CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_verify_assert() ); -#elif defined EIGEN_TEST_PART_2 - CALL_SUBTEST( (lu_non_invertible >()) ); - CALL_SUBTEST( (lu_verify_assert >()) ); -#elif defined EIGEN_TEST_PART_3 - CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_invertible() ); - CALL_SUBTEST( lu_verify_assert() ); -#elif defined EIGEN_TEST_PART_4 - CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_invertible() ); - CALL_SUBTEST( lu_verify_assert() ); -#elif defined EIGEN_TEST_PART_5 - CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_invertible() ); - CALL_SUBTEST( lu_verify_assert() ); -#elif defined EIGEN_TEST_PART_6 - CALL_SUBTEST( lu_non_invertible() ); - CALL_SUBTEST( lu_invertible() ); - CALL_SUBTEST( lu_verify_assert() ); -#endif + CALL_SUBTEST1( lu_non_invertible() ); + CALL_SUBTEST1( lu_verify_assert() ); + + CALL_SUBTEST2( (lu_non_invertible >()) ); + CALL_SUBTEST2( (lu_verify_assert >()) ); + + CALL_SUBTEST3( lu_non_invertible() ); + CALL_SUBTEST3( lu_invertible() ); + CALL_SUBTEST3( lu_verify_assert() ); + + CALL_SUBTEST4( lu_non_invertible() ); + CALL_SUBTEST4( lu_invertible() ); + CALL_SUBTEST4( lu_verify_assert() ); + + CALL_SUBTEST5( lu_non_invertible() ); + CALL_SUBTEST5( lu_invertible() ); + CALL_SUBTEST5( lu_verify_assert() ); + + CALL_SUBTEST6( lu_non_invertible() ); + CALL_SUBTEST6( lu_invertible() ); + CALL_SUBTEST6( lu_verify_assert() ); } } diff --git a/test/main.h b/test/main.h index 51b719814..15ae3d645 100644 --- a/test/main.h +++ b/test/main.h @@ -170,6 +170,66 @@ namespace Eigen g_test_stack.pop_back(); \ } while (0) +#ifdef EIGEN_TEST_PART_1 +#define CALL_SUBTEST1(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST1(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_2 +#define CALL_SUBTEST2(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST2(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_3 +#define CALL_SUBTEST3(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST3(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_4 +#define CALL_SUBTEST4(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST4(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_5 +#define CALL_SUBTEST5(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST5(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_6 +#define CALL_SUBTEST6(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST6(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_7 +#define CALL_SUBTEST7(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST7(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_8 +#define CALL_SUBTEST8(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST8(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_9 +#define CALL_SUBTEST9(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST9(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_10 +#define CALL_SUBTEST10(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST10(FUNC) +#endif + namespace Eigen { template inline typename NumTraits::Real test_precision(); diff --git a/test/product_extra.cpp b/test/product_extra.cpp index 8e55c6010..aeaf04d83 100644 --- a/test/product_extra.cpp +++ b/test/product_extra.cpp @@ -119,8 +119,8 @@ template void product_extra(const MatrixType& m) void test_product_extra() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( product_extra(MatrixXf(ei_random(2,320), ei_random(2,320))) ); - CALL_SUBTEST( product_extra(MatrixXcf(ei_random(50,50), ei_random(50,50))) ); - CALL_SUBTEST( product_extra(Matrix,Dynamic,Dynamic,RowMajor>(ei_random(2,50), ei_random(2,50))) ); + CALL_SUBTEST1( product_extra(MatrixXf(ei_random(2,320), ei_random(2,320))) ); + CALL_SUBTEST2( product_extra(MatrixXcf(ei_random(50,50), ei_random(50,50))) ); + CALL_SUBTEST3( product_extra(Matrix,Dynamic,Dynamic,RowMajor>(ei_random(2,50), ei_random(2,50))) ); } } diff --git a/test/product_large.cpp b/test/product_large.cpp index 9b53e7b89..a64775f6c 100644 --- a/test/product_large.cpp +++ b/test/product_large.cpp @@ -27,13 +27,14 @@ void test_product_large() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( product(MatrixXf(ei_random(1,320), ei_random(1,320))) ); - CALL_SUBTEST( product(MatrixXd(ei_random(1,320), ei_random(1,320))) ); - CALL_SUBTEST( product(MatrixXi(ei_random(1,320), ei_random(1,320))) ); - CALL_SUBTEST( product(MatrixXcf(ei_random(1,50), ei_random(1,50))) ); - CALL_SUBTEST( product(Matrix(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST1( product(MatrixXf(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST2( product(MatrixXd(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST3( product(MatrixXi(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST4( product(MatrixXcf(ei_random(1,50), ei_random(1,50))) ); + CALL_SUBTEST5( product(Matrix(ei_random(1,320), ei_random(1,320))) ); } +#if defined EIGEN_TEST_PART_6 { // test a specific issue in DiagonalProduct int N = 1000000; @@ -48,4 +49,5 @@ void test_product_large() MatrixXf a = MatrixXf::Random(10,4), b = MatrixXf::Random(4,10), c = a; VERIFY_IS_APPROX((a = a * b), (c * b).eval()); } +#endif } diff --git a/test/product_notemporary.cpp b/test/product_notemporary.cpp index 1a3d65291..e9efeaaae 100644 --- a/test/product_notemporary.cpp +++ b/test/product_notemporary.cpp @@ -117,8 +117,8 @@ void test_product_notemporary() int s; for(int i = 0; i < g_repeat; i++) { s = ei_random(16,320); - CALL_SUBTEST( product_notemporary(MatrixXf(s, s)) ); + CALL_SUBTEST1( product_notemporary(MatrixXf(s, s)) ); s = ei_random(16,120); - CALL_SUBTEST( product_notemporary(MatrixXcd(s,s)) ); + CALL_SUBTEST2( product_notemporary(MatrixXcd(s,s)) ); } } diff --git a/test/product_selfadjoint.cpp b/test/product_selfadjoint.cpp index e47358197..065d4ad8c 100644 --- a/test/product_selfadjoint.cpp +++ b/test/product_selfadjoint.cpp @@ -78,13 +78,13 @@ template void product_selfadjoint(const MatrixType& m) void test_product_selfadjoint() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST( product_selfadjoint(Matrix()) ); - CALL_SUBTEST( product_selfadjoint(Matrix()) ); - CALL_SUBTEST( product_selfadjoint(Matrix3d()) ); - CALL_SUBTEST( product_selfadjoint(MatrixXcf(4, 4)) ); - CALL_SUBTEST( product_selfadjoint(MatrixXcd(21,21)) ); - CALL_SUBTEST( product_selfadjoint(MatrixXd(14,14)) ); - CALL_SUBTEST( product_selfadjoint(Matrix(17,17)) ); - CALL_SUBTEST( product_selfadjoint(Matrix,Dynamic,Dynamic,RowMajor>(19, 19)) ); + CALL_SUBTEST1( product_selfadjoint(Matrix()) ); + CALL_SUBTEST2( product_selfadjoint(Matrix()) ); + CALL_SUBTEST3( product_selfadjoint(Matrix3d()) ); + CALL_SUBTEST4( product_selfadjoint(MatrixXcf(4, 4)) ); + CALL_SUBTEST5( product_selfadjoint(MatrixXcd(21,21)) ); + CALL_SUBTEST6( product_selfadjoint(MatrixXd(14,14)) ); + CALL_SUBTEST7( product_selfadjoint(Matrix(17,17)) ); + CALL_SUBTEST8( product_selfadjoint(Matrix,Dynamic,Dynamic,RowMajor>(19, 19)) ); } } diff --git a/test/product_small.cpp b/test/product_small.cpp index 182af71db..3a667a5dc 100644 --- a/test/product_small.cpp +++ b/test/product_small.cpp @@ -28,16 +28,18 @@ void test_product_small() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( product(Matrix()) ); - CALL_SUBTEST( product(Matrix()) ); - CALL_SUBTEST( product(Matrix3d()) ); - CALL_SUBTEST( product(Matrix4d()) ); - CALL_SUBTEST( product(Matrix4f()) ); + CALL_SUBTEST1( product(Matrix()) ); + CALL_SUBTEST2( product(Matrix()) ); + CALL_SUBTEST3( product(Matrix3d()) ); + CALL_SUBTEST4( product(Matrix4d()) ); + CALL_SUBTEST5( product(Matrix4f()) ); } +#ifdef EIGEN_TEST_PART_6 { // test compilation of (outer_product) * vector Vector3f v = Vector3f::Random(); VERIFY_IS_APPROX( (v * v.transpose()) * v, (v * v.transpose()).eval() * v); } +#endif } diff --git a/test/product_symm.cpp b/test/product_symm.cpp index cf0299c64..ecd46c78e 100644 --- a/test/product_symm.cpp +++ b/test/product_symm.cpp @@ -108,10 +108,10 @@ void test_product_symm() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST(( symm(ei_random(10,320),ei_random(10,320)) )); - CALL_SUBTEST(( symm,Dynamic,Dynamic>(ei_random(10,320),ei_random(10,320)) )); + CALL_SUBTEST1(( symm(ei_random(10,320),ei_random(10,320)) )); + CALL_SUBTEST2(( symm,Dynamic,Dynamic>(ei_random(10,320),ei_random(10,320)) )); - CALL_SUBTEST(( symm(ei_random(10,320)) )); - CALL_SUBTEST(( symm,Dynamic,1>(ei_random(10,320)) )); + CALL_SUBTEST3(( symm(ei_random(10,320)) )); + CALL_SUBTEST4(( symm,Dynamic,1>(ei_random(10,320)) )); } } diff --git a/test/product_syrk.cpp b/test/product_syrk.cpp index 657dec9bc..78b67bb1a 100644 --- a/test/product_syrk.cpp +++ b/test/product_syrk.cpp @@ -75,8 +75,8 @@ void test_product_syrk() { int s; s = ei_random(10,320); - CALL_SUBTEST( syrk(MatrixXf(s, s)) ); + CALL_SUBTEST1( syrk(MatrixXf(s, s)) ); s = ei_random(10,320); - CALL_SUBTEST( syrk(MatrixXcd(s, s)) ); + CALL_SUBTEST2( syrk(MatrixXcd(s, s)) ); } } diff --git a/test/product_trmm.cpp b/test/product_trmm.cpp index 734d8c970..c3234ba7e 100644 --- a/test/product_trmm.cpp +++ b/test/product_trmm.cpp @@ -63,7 +63,7 @@ void test_product_trmm() { for(int i = 0; i < g_repeat ; i++) { - trmm(ei_random(1,320),ei_random(1,320)); - trmm >(ei_random(1,320),ei_random(1,320)); + CALL_SUBTEST1((trmm(ei_random(1,320),ei_random(1,320)))); + CALL_SUBTEST2((trmm >(ei_random(1,320),ei_random(1,320)))); } } diff --git a/test/product_trmv.cpp b/test/product_trmv.cpp index b4d45cca2..602cdca03 100644 --- a/test/product_trmv.cpp +++ b/test/product_trmv.cpp @@ -82,11 +82,11 @@ template void trmv(const MatrixType& m) void test_product_trmv() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST( trmv(Matrix()) ); - CALL_SUBTEST( trmv(Matrix()) ); - CALL_SUBTEST( trmv(Matrix3d()) ); - CALL_SUBTEST( trmv(Matrix,23, 23>()) ); - CALL_SUBTEST( trmv(MatrixXcd(17,17)) ); - CALL_SUBTEST( trmv(Matrix(19, 19)) ); + CALL_SUBTEST1( trmv(Matrix()) ); + CALL_SUBTEST2( trmv(Matrix()) ); + CALL_SUBTEST3( trmv(Matrix3d()) ); + CALL_SUBTEST4( trmv(Matrix,23, 23>()) ); + CALL_SUBTEST5( trmv(MatrixXcd(17,17)) ); + CALL_SUBTEST6( trmv(Matrix(19, 19)) ); } } diff --git a/test/product_trsm.cpp b/test/product_trsm.cpp index 756034df9..2bd2e5e02 100644 --- a/test/product_trsm.cpp +++ b/test/product_trsm.cpp @@ -59,7 +59,7 @@ void test_product_trsm() { for(int i = 0; i < g_repeat ; i++) { - trsm(ei_random(1,320),ei_random(1,320)); - trsm >(ei_random(1,320),ei_random(1,320)); + CALL_SUBTEST1((trsm(ei_random(1,320),ei_random(1,320)))); + CALL_SUBTEST2((trsm >(ei_random(1,320),ei_random(1,320)))); } } From 890bff977eeff9a5d461812a0048c89ea30fd30a Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 19 Oct 2009 15:56:03 -0400 Subject: [PATCH 15/34] * proper check for Make * fix documentation of ei_add_test --- CMakeLists.txt | 34 ++++++++++++++++++---------------- cmake/EigenTesting.cmake | 9 +++++++-- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c068e6f5..ed9e6d183 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,21 +142,23 @@ if(EIGEN_BUILD_BTL) endif(EIGEN_BUILD_BTL) ei_testing_print_summary() -if(NOT MSVC_IDE) + message("") message("Configured Eigen ${EIGEN_VERSION_NUMBER}") -message("You can now do the following:") -message("--------------+----------------------------------------------------------------") -message("Command | Description") -message("--------------+----------------------------------------------------------------") -message("make install | Install to ${CMAKE_INSTALL_PREFIX}") -message(" | * To change that: cmake . -DCMAKE_INSTALL_PREFIX=yourpath") -message("make btest | Build the unit tests") -message(" | * That takes lots of memory! Easy on the -j option") -message("make test | Build and run the unit tests (using CTest)") -message("make test_qr | Build a specific test, here test_qr. To run it: test/test_qr") -message("make debug_qr | Build a test with full debug info. To run it: test/debug_qr") -message("make blas | Build BLAS library (not the same thing as Eigen)") -message("make doc | Generate the API documentation, requires Doxygen & LaTeX") -message("--------------+----------------------------------------------------------------") -endif(NOT MSVC_IDE) + +string(TOLOWER "${CMAKE_GENERATOR}" cmake_generator_tolower) +if(cmake_generator_tolower MATCHES "makefile") + message("You can now do the following:") + message("--------------+----------------------------------------------------------------") + message("Command | Description") + message("--------------+----------------------------------------------------------------") + message("make install | Install to ${CMAKE_INSTALL_PREFIX}") + message(" | * To change that: cmake . -DCMAKE_INSTALL_PREFIX=yourpath") + message("make btest | Build the unit tests") + message("make test | Build and run the unit tests (using CTest)") + message("make test_qr | Build a specific test, here test_qr. To run it: test/test_qr") + message("make debug_qr | Build a test with full debug info. To run it: test/debug_qr") + message("make blas | Build BLAS library (not the same thing as Eigen)") + message("make doc | Generate the API documentation, requires Doxygen & LaTeX") + message("--------------+----------------------------------------------------------------") +endif() diff --git a/cmake/EigenTesting.cmake b/cmake/EigenTesting.cmake index f33e9fea1..22aea3d0d 100644 --- a/cmake/EigenTesting.cmake +++ b/cmake/EigenTesting.cmake @@ -110,13 +110,18 @@ endmacro(ei_add_test_internal) # Macro to add a test # -# the unique parameter testname must correspond to a file +# the unique mandatory parameter testname must correspond to a file # .cpp which follows this pattern: # # #include "main.h" # void test_() { ... } # -# Depending on the contents of that file, this macro can have 2 behaviors. +# Depending on the contents of that file, this macro can have 2 behaviors, +# see below. +# +# Optional parameters can be passed: the 2nd parameter is additional compile flags +# (optimization will be explicitly disabled for the debug test targets) and the 3rd +# parameter is libraries to link to. # # A. Default behavior # From d1db1352f5632281a822fd0f1f59dd7d96b876a1 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 19 Oct 2009 17:22:04 -0400 Subject: [PATCH 16/34] update doc snippets --- doc/snippets/LU_computeImage.cpp | 13 ------------- doc/snippets/LU_computeKernel.cpp | 10 ---------- doc/snippets/LU_image.cpp | 2 +- 3 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 doc/snippets/LU_computeImage.cpp delete mode 100644 doc/snippets/LU_computeKernel.cpp diff --git a/doc/snippets/LU_computeImage.cpp b/doc/snippets/LU_computeImage.cpp deleted file mode 100644 index 5c812cc4c..000000000 --- a/doc/snippets/LU_computeImage.cpp +++ /dev/null @@ -1,13 +0,0 @@ -MatrixXd m(3,3); -m << 1,1,0, - 1,3,2, - 0,1,1; -cout << "Here is the matrix m:" << endl << m << endl; -LU lu(m); -// allocate the matrix img with the correct size to avoid reallocation -MatrixXd img(m.rows(), lu.rank()); -lu.computeImage(&img); -cout << "Notice that the middle column is the sum of the two others, so the " - << "columns are linearly dependent." << endl; -cout << "Here is a matrix whose columns have the same span but are linearly independent:" - << endl << img << endl; diff --git a/doc/snippets/LU_computeKernel.cpp b/doc/snippets/LU_computeKernel.cpp deleted file mode 100644 index b08f7f1ea..000000000 --- a/doc/snippets/LU_computeKernel.cpp +++ /dev/null @@ -1,10 +0,0 @@ -MatrixXf m = MatrixXf::Random(3,5); -cout << "Here is the matrix m:" << endl << m << endl; -LU lu(m); -// allocate the matrix ker with the correct size to avoid reallocation -MatrixXf ker(m.rows(), lu.dimensionOfKernel()); -lu.computeKernel(&ker); -cout << "Here is a matrix whose columns form a basis of the kernel of m:" - << endl << ker << endl; -cout << "By definition of the kernel, m*ker is zero:" - << endl << m*ker << endl; diff --git a/doc/snippets/LU_image.cpp b/doc/snippets/LU_image.cpp index 0d1088a2f..d3092e8b6 100644 --- a/doc/snippets/LU_image.cpp +++ b/doc/snippets/LU_image.cpp @@ -6,4 +6,4 @@ cout << "Here is the matrix m:" << endl << m << endl; cout << "Notice that the middle column is the sum of the two others, so the " << "columns are linearly dependent." << endl; cout << "Here is a matrix whose columns have the same span but are linearly independent:" - << endl << m.lu().image() << endl; + << endl << m.lu().image(m) << endl; From 13f31b8daf4d67d6310c567de36a34afa7c6e18f Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Tue, 20 Oct 2009 00:36:07 -0400 Subject: [PATCH 17/34] * make PartialLU avoid to generate inf/nan when given a singular matrix (result undefined, but at least it won't take forever on intel 387) * add lots of comments, especially to LU.h * fix stuff I had broken in Inverse.h * split inverse test --- Eigen/src/LU/Inverse.h | 6 +++-- Eigen/src/LU/LU.h | 41 +++++++++++++++++++++++++--------- Eigen/src/LU/PartialLU.h | 47 ++++++++++++++++++++++++++++++++------- doc/snippets/LU_solve.cpp | 1 - test/inverse.cpp | 14 +++++++----- 5 files changed, 82 insertions(+), 27 deletions(-) diff --git a/Eigen/src/LU/Inverse.h b/Eigen/src/LU/Inverse.h index f1154a56b..b4e10b023 100644 --- a/Eigen/src/LU/Inverse.h +++ b/Eigen/src/LU/Inverse.h @@ -200,7 +200,7 @@ struct ei_compute_inverse { static inline void run(const MatrixType& matrix, ResultType* result) { - result = matrix.partialLu().inverse(); + *result = matrix.partialLu().inverse(); } }; @@ -282,7 +282,9 @@ inline void MatrixBase::computeInverse(ResultType *result) const template inline const typename MatrixBase::PlainMatrixType MatrixBase::inverse() const { - return inverse(*this); + typename MatrixBase::PlainMatrixType result; + computeInverse(&result); + return result; } diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index 455a7d67e..2baa71f67 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -410,28 +410,32 @@ LU& LU::compute(const MatrixType& matrix) const int rows = matrix.rows(); const int cols = matrix.cols(); + // will store the transpositions, before we accumulate them at the end. + // can't accumulate on-the-fly because that will be done in reverse order for the rows. IntColVectorType rows_transpositions(matrix.rows()); IntRowVectorType cols_transpositions(matrix.cols()); - int number_of_transpositions = 0; + int number_of_transpositions = 0; // number of NONTRIVIAL transpositions, i.e. rows_transpositions[i]!=i - RealScalar biggest = RealScalar(0); - m_nonzero_pivots = size; + m_nonzero_pivots = size; // the generic case is that in which all pivots are nonzero (invertible case) m_maxpivot = RealScalar(0); for(int k = 0; k < size; ++k) { + // First, we need to find the pivot. + + // biggest coefficient in the remaining bottom-right corner (starting at row k, col k) int row_of_biggest_in_corner, col_of_biggest_in_corner; RealScalar biggest_in_corner; - biggest_in_corner = m_lu.corner(Eigen::BottomRight, rows-k, cols-k) .cwise().abs() .maxCoeff(&row_of_biggest_in_corner, &col_of_biggest_in_corner); - row_of_biggest_in_corner += k; - col_of_biggest_in_corner += k; - if(k==0) biggest = biggest_in_corner; + row_of_biggest_in_corner += k; // correct the values! since they were computed in the corner, + col_of_biggest_in_corner += k; // need to add k to them. - // if the corner is exactly zero, terminate to avoid generating nan/inf values + // if the pivot (hence the corner) is exactly zero, terminate to avoid generating nan/inf values if(biggest_in_corner == RealScalar(0)) { + // before exiting, make sure to initialize the still uninitialized row_transpositions + // in a sane state without destroying what we already have. m_nonzero_pivots = k; for(int i = k; i < size; i++) { @@ -443,6 +447,9 @@ LU& LU::compute(const MatrixType& matrix) if(biggest_in_corner > m_maxpivot) m_maxpivot = biggest_in_corner; + // Now that we've found the pivot, we need to apply the row/col swaps to + // bring it to the location (k,k). + rows_transpositions.coeffRef(k) = row_of_biggest_in_corner; cols_transpositions.coeffRef(k) = col_of_biggest_in_corner; if(k != row_of_biggest_in_corner) { @@ -453,12 +460,19 @@ LU& LU::compute(const MatrixType& matrix) m_lu.col(k).swap(m_lu.col(col_of_biggest_in_corner)); ++number_of_transpositions; } + + // Now that the pivot is at the right location, we update the remaining + // bottom-right corner by Gaussian elimination. + if(k= 0; --k) std::swap(m_p.coeffRef(k), m_p.coeffRef(rows_transpositions.coeff(k))); @@ -549,7 +563,10 @@ struct ei_lu_kernel_impl : public ReturnByValue > if(ei_abs(m_lu.matrixLU().coeff(i,i)) > premultiplied_threshold) pivots.coeffRef(p++) = i; ei_assert(p == m_rank && "You hit a bug in Eigen! Please report (backtrace and matrix)!"); - + + // we construct a temporaty trapezoid matrix m, by taking the U matrix and + // permuting the rows and cols to bring the nonnegligible pivots to the top of + // the main diagonal. We need that to be able to apply our triangular solvers. // FIXME when we get triangularView-for-rectangular-matrices, this can be simplified Matrix @@ -560,18 +577,22 @@ struct ei_lu_kernel_impl : public ReturnByValue > m.row(i).end(cols-i) = m_lu.matrixLU().row(pivots.coeff(i)).end(cols-i); } m.block(0, 0, m_rank, m_rank).template triangularView().setZero(); - for(int i = 0; i < m_rank; ++i) m.col(i).swap(m.col(pivots.coeff(i))); + // ok, we have our trapezoid matrix, we can apply the triangular solver. + // notice that the math behind this suggests that we should apply this to the + // negative of the RHS, but for performance we just put the negative sign elsewhere, see below. m.corner(TopLeft, m_rank, m_rank) .template triangularView().solveInPlace( m.corner(TopRight, m_rank, dimker) ); + // now we must undo the column permutation that we had applied! for(int i = m_rank-1; i >= 0; --i) m.col(i).swap(m.col(pivots.coeff(i))); + // see the negative sign in the next line, that's what we were talking about above. for(int i = 0; i < m_rank; ++i) dst.row(m_lu.permutationQ().coeff(i)) = -m.row(i).end(dimker); for(int i = m_rank; i < cols; ++i) dst.row(m_lu.permutationQ().coeff(i)).setZero(); for(int k = 0; k < dimker; ++k) dst.coeffRef(m_lu.permutationQ().coeff(m_rank+k), k) = Scalar(1); diff --git a/Eigen/src/LU/PartialLU.h b/Eigen/src/LU/PartialLU.h index e467c62f0..3675b0309 100644 --- a/Eigen/src/LU/PartialLU.h +++ b/Eigen/src/LU/PartialLU.h @@ -216,6 +216,7 @@ struct ei_partial_lu_impl typedef Map > MapLU; typedef Block MatrixType; typedef Block BlockType; + typedef typename MatrixType::RealScalar RealScalar; /** \internal performs the LU decomposition in-place of the matrix \a lu * using an unblocked algorithm. @@ -224,8 +225,12 @@ struct ei_partial_lu_impl * vector \a row_transpositions which must have a size equal to the number * of columns of the matrix \a lu, and an integer \a nb_transpositions * which returns the actual number of transpositions. + * + * \returns false if some pivot is exactly zero, in which case the matrix is left with + * undefined coefficients (to avoid generating inf/nan values). Returns true + * otherwise. */ - static void unblocked_lu(MatrixType& lu, int* row_transpositions, int& nb_transpositions) + static bool unblocked_lu(MatrixType& lu, int* row_transpositions, int& nb_transpositions) { const int rows = lu.rows(); const int size = std::min(lu.rows(),lu.cols()); @@ -233,9 +238,22 @@ struct ei_partial_lu_impl for(int k = 0; k < size; ++k) { int row_of_biggest_in_col; - lu.col(k).end(rows-k).cwise().abs().maxCoeff(&row_of_biggest_in_col); + RealScalar biggest_in_corner + = lu.col(k).end(rows-k).cwise().abs().maxCoeff(&row_of_biggest_in_col); row_of_biggest_in_col += k; + if(biggest_in_corner == 0) // the pivot is exactly zero: the matrix is singular + { + // end quickly, avoid generating inf/nan values. Although in this unblocked_lu case + // the result is still valid, there's no need to boast about it because + // the blocked_lu code can't guarantee the same. + // before exiting, make sure to initialize the still uninitialized row_transpositions + // in a sane state without destroying what we already have. + for(int i = k; i < size; i++) + row_transpositions[i] = i; + return false; + } + row_transpositions[k] = row_of_biggest_in_col; if(k != row_of_biggest_in_col) @@ -252,6 +270,7 @@ struct ei_partial_lu_impl lu.corner(BottomRight,rrows,rsize).noalias() -= lu.col(k).end(rrows) * lu.row(k).end(rsize); } } + return true; } /** \internal performs the LU decomposition in-place of the matrix represented @@ -263,11 +282,15 @@ struct ei_partial_lu_impl * of columns of the matrix \a lu, and an integer \a nb_transpositions * which returns the actual number of transpositions. * + * \returns false if some pivot is exactly zero, in which case the matrix is left with + * undefined coefficients (to avoid generating inf/nan values). Returns true + * otherwise. + * * \note This very low level interface using pointers, etc. is to: * 1 - reduce the number of instanciations to the strict minimum * 2 - avoid infinite recursion of the instanciations with Block > > */ - static void blocked_lu(int rows, int cols, Scalar* lu_data, int luStride, int* row_transpositions, int& nb_transpositions, int maxBlockSize=256) + static bool blocked_lu(int rows, int cols, Scalar* lu_data, int luStride, int* row_transpositions, int& nb_transpositions, int maxBlockSize=256) { MapLU lu1(lu_data,StorageOrder==RowMajor?rows:luStride,StorageOrder==RowMajor?luStride:cols); MatrixType lu(lu1,0,0,rows,cols); @@ -277,8 +300,7 @@ struct ei_partial_lu_impl // if the matrix is too small, no blocking: if(size<=16) { - unblocked_lu(lu, row_transpositions, nb_transpositions); - return; + return unblocked_lu(lu, row_transpositions, nb_transpositions); } // automatically adjust the number of subdivisions to the size @@ -311,12 +333,20 @@ struct ei_partial_lu_impl int nb_transpositions_in_panel; // recursively calls the blocked LU algorithm with a very small // blocking size: - blocked_lu(trows+bs, bs, &lu.coeffRef(k,k), luStride, - row_transpositions+k, nb_transpositions_in_panel, 16); + if(!blocked_lu(trows+bs, bs, &lu.coeffRef(k,k), luStride, + row_transpositions+k, nb_transpositions_in_panel, 16)) + { + // end quickly with undefined coefficients, just avoid generating inf/nan values. + // before exiting, make sure to initialize the still uninitialized row_transpositions + // in a sane state without destroying what we already have. + for(int i=k; i()) ); - CALL_SUBTEST( inverse(Matrix2d()) ); - CALL_SUBTEST( inverse(Matrix3f()) ); - CALL_SUBTEST( inverse(Matrix4f()) ); + CALL_SUBTEST1( inverse(Matrix()) ); + CALL_SUBTEST2( inverse(Matrix2d()) ); + CALL_SUBTEST3( inverse(Matrix3f()) ); + CALL_SUBTEST4( inverse(Matrix4f()) ); s = ei_random(50,320); - CALL_SUBTEST( inverse(MatrixXf(s,s)) ); + CALL_SUBTEST5( inverse(MatrixXf(s,s)) ); s = ei_random(25,100); - CALL_SUBTEST( inverse(MatrixXcd(s,s)) ); + CALL_SUBTEST6( inverse(MatrixXcd(s,s)) ); } +#ifdef EIGEN_TEST_PART_4 // test some tricky cases for 4x4 matrices VERIFY_IS_APPROX((Matrix4f() << 0,0,1,0, 1,0,0,0, 0,1,0,0, 0,0,0,1).finished().inverse(), (Matrix4f() << 0,1,0,0, 0,0,1,0, 1,0,0,0, 0,0,0,1).finished()); VERIFY_IS_APPROX((Matrix4f() << 1,0,0,0, 0,0,1,0, 0,0,0,1, 0,1,0,0).finished().inverse(), (Matrix4f() << 1,0,0,0, 0,0,0,1, 0,1,0,0, 0,0,1,0).finished()); +#endif } From 68d48511b27b43b80d9268953a8567cc8abd66c1 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 21 Oct 2009 17:06:42 -0400 Subject: [PATCH 18/34] move PartialLU to the new API --- Eigen/src/LU/LU.h | 11 ++- Eigen/src/LU/PartialLU.h | 140 ++++++++++++++++++++++----------------- doc/Doxyfile.in | 2 +- 3 files changed, 84 insertions(+), 69 deletions(-) diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/LU.h index 2baa71f67..4792aaf07 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/LU.h @@ -209,7 +209,7 @@ template class LU * * \returns a solution. * - * \note_about_inexistant_solutions + * \note_about_checking_solutions * * \note_about_arbitrary_choice_of_solution * \note_about_using_kernel_to_study_multiple_solutions @@ -374,15 +374,12 @@ template class LU } protected: - bool m_isInitialized; MatrixType m_lu; IntColVectorType m_p; IntRowVectorType m_q; - int m_det_pq; - int m_nonzero_pivots; - RealScalar m_maxpivot; - bool m_usePrescribedThreshold; - RealScalar m_prescribedThreshold; + int m_det_pq, m_nonzero_pivots; + RealScalar m_maxpivot, m_prescribedThreshold; + bool m_isInitialized, m_usePrescribedThreshold; }; template diff --git a/Eigen/src/LU/PartialLU.h b/Eigen/src/LU/PartialLU.h index 3675b0309..30e633eda 100644 --- a/Eigen/src/LU/PartialLU.h +++ b/Eigen/src/LU/PartialLU.h @@ -26,6 +26,8 @@ #ifndef EIGEN_PARTIALLU_H #define EIGEN_PARTIALLU_H +template struct ei_partiallu_solve_impl; + /** \ingroup LU_Module * * \class PartialLU @@ -112,26 +114,46 @@ template class PartialLU return m_p; } - /** This method finds the solution x to the equation Ax=b, where A is the matrix of which - * *this is the LU decomposition. Since if this partial pivoting decomposition the matrix is assumed - * to have full rank, such a solution is assumed to exist and to be unique. - * - * \warning Again, if your matrix may not have full rank, use class LU instead. See LU::solve(). + /** This method returns a solution x to the equation Ax=b, where A is the matrix of which + * *this is the LU decomposition. * * \param b the right-hand-side of the equation to solve. Can be a vector or a matrix, * the only requirement in order for the equation to make sense is that * b.rows()==A.rows(), where A is the matrix of which *this is the LU decomposition. - * \param result a pointer to the vector or matrix in which to store the solution, if any exists. - * Resized if necessary, so that result->rows()==A.cols() and result->cols()==b.cols(). - * If no solution exists, *result is left with undefined coefficients. + * + * \returns the solution. * * Example: \include PartialLU_solve.cpp * Output: \verbinclude PartialLU_solve.out * + * Since this PartialLU class assumes anyway that the matrix A is invertible, the solution + * theoretically exists and is unique regardless of b. + * + * \note_about_checking_solutions + * * \sa TriangularView::solve(), inverse(), computeInverse() */ - template - void solve(const MatrixBase& b, ResultType *result) const; + template + inline const ei_partiallu_solve_impl + solve(const MatrixBase& b) const + { + ei_assert(m_isInitialized && "LU is not initialized."); + return ei_partiallu_solve_impl(*this, b.derived()); + } + + /** \returns the inverse of the matrix of which *this is the LU decomposition. + * + * \warning The matrix being decomposed here is assumed to be invertible. If you need to check for + * invertibility, use class LU instead. + * + * \sa MatrixBase::inverse(), LU::inverse() + */ + inline const ei_partiallu_solve_impl > inverse() const + { + ei_assert(m_isInitialized && "LU is not initialized."); + return ei_partiallu_solve_impl > + (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); + } /** \returns the determinant of the matrix of which * *this is the LU decomposition. It has only linear complexity @@ -148,34 +170,6 @@ template class PartialLU */ typename ei_traits::Scalar determinant() const; - /** Computes the inverse of the matrix of which *this is the LU decomposition. - * - * \param result a pointer to the matrix into which to store the inverse. Resized if needed. - * - * \warning The matrix being decomposed here is assumed to be invertible. If you need to check for - * invertibility, use class LU instead. - * - * \sa MatrixBase::computeInverse(), inverse() - */ - inline void computeInverse(MatrixType *result) const - { - solve(MatrixType::Identity(m_lu.rows(), m_lu.cols()), result); - } - - /** \returns the inverse of the matrix of which *this is the LU decomposition. - * - * \warning The matrix being decomposed here is assumed to be invertible. If you need to check for - * invertibility, use class LU instead. - * - * \sa computeInverse(), MatrixBase::inverse() - */ - inline MatrixType inverse() const - { - MatrixType result; - computeInverse(&result); - return result; - } - protected: MatrixType m_lu; IntColVectorType m_p; @@ -411,36 +405,60 @@ typename ei_traits::Scalar PartialLU::determinant() cons return Scalar(m_det_p) * m_lu.diagonal().prod(); } -template -template -void PartialLU::solve( - const MatrixBase& b, - ResultType *result -) const +/***** Implementation of solve() *****************************************************/ + +template +struct ei_traits > { - ei_assert(m_isInitialized && "PartialLU is not initialized."); + typedef Matrix ReturnMatrixType; +}; - /* The decomposition PA = LU can be rewritten as A = P^{-1} L U. - * So we proceed as follows: - * Step 1: compute c = Pb. - * Step 2: replace c by the solution x to Lx = c. - * Step 3: replace c by the solution x to Ux = c. - */ +template +struct ei_partiallu_solve_impl : public ReturnByValue > +{ + typedef typename ei_cleantype::type RhsNested; + typedef PartialLU LUType; + const LUType& m_lu; + const typename Rhs::Nested m_rhs; - const int size = m_lu.rows(); - ei_assert(b.rows() == size); + ei_partiallu_solve_impl(const LUType& lu, const Rhs& rhs) + : m_lu(lu), m_rhs(rhs) + {} - result->resize(size, b.cols()); + inline int rows() const { return m_lu.matrixLU().cols(); } + inline int cols() const { return m_rhs.cols(); } - // Step 1 - for(int i = 0; i < size; ++i) result->row(m_p.coeff(i)) = b.row(i); + template void evalTo(Dest& dst) const + { + /* The decomposition PA = LU can be rewritten as A = P^{-1} L U. + * So we proceed as follows: + * Step 1: compute c = Pb. + * Step 2: replace c by the solution x to Lx = c. + * Step 3: replace c by the solution x to Ux = c. + */ - // Step 2 - m_lu.template triangularView().solveInPlace(*result); + const int size = m_lu.matrixLU().rows(); + ei_assert(m_rhs.rows() == size); - // Step 3 - m_lu.template triangularView().solveInPlace(*result); -} + dst.resize(size, m_rhs.cols()); + + // Step 1 + for(int i = 0; i < size; ++i) dst.row(m_lu.permutationP().coeff(i)) = m_rhs.row(i); + + // Step 2 + m_lu.matrixLU().template triangularView().solveInPlace(dst); + + // Step 3 + m_lu.matrixLU().template triangularView().solveInPlace(dst); + } +}; + +/******** MatrixBase methods *******/ /** \lu_module * diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 97bda5e41..fb00a3cc5 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -218,7 +218,7 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\"" \ "note_about_arbitrary_choice_of_solution=If there exists more than one solution, this method will arbitrarily choose one." \ "note_about_using_kernel_to_study_multiple_solutions=If you need a complete analysis of the space of solutions, take the one solution obtained by this method and add to it elements of the kernel, as determined by kernel()." \ - "note_about_inexistant_solutions=If only an approximate solution exists, then the result is only such an approximate solution. The case where no solution exists isn't treated separately: non-existent solutions are just \"very inaccurate solutions\". If you want to check whether a solution exists, just call this function to get a solution and then compute the error margin, or use MatrixBase::isApprox() directly, for instance like this: \code bool a_solution_exists = (A*result).isApprox(b, precision); \endcode As long as the input matrices have finite numeric coefficients (no \c inf or \c nan values), the result will also have finite numeric coefficients, there is no risk of getting \c nan values if no solution exists." + "note_about_checking_solutions=This method just tries to find as good a solution as possible. If you want to check whether a solution "exists" or if it is accurate, just call this function to get a solution and then compute the error margin, or use MatrixBase::isApprox() directly, for instance like this: \code bool a_solution_exists = (A*result).isApprox(b, precision); \endcode The non-existence of a solution doesn't by itself mean that you'll get \c inf or \c nan values." # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. From ec02388a5da8e3974e5dd5ab42dfc1565ee3b7e7 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 26 Oct 2009 11:18:23 -0400 Subject: [PATCH 19/34] big rewrite in Inverse.h in particular, the API is essentially finalized and the 4x4 case is fixed to be numerically stable. --- Eigen/src/Core/MatrixBase.h | 9 +- Eigen/src/LU/Inverse.h | 450 +++++++++++++++++++----------------- test/inverse.cpp | 15 +- 3 files changed, 259 insertions(+), 215 deletions(-) diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index cccb23241..24b3feb6f 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -703,9 +703,12 @@ template class MatrixBase const PartialLU partialLu() const; const PlainMatrixType inverse() const; template - void computeInverse(ResultType *result) const; - template - bool computeInverseWithCheck(ResultType *result ) const; + void computeInverseAndDetWithCheck( + ResultType& inverse, + typename ResultType::Scalar& determinant, + bool& invertible, + const RealScalar& absDeterminantThreshold = precision() + ) const; Scalar determinant() const; /////////// Cholesky module /////////// diff --git a/Eigen/src/LU/Inverse.h b/Eigen/src/LU/Inverse.h index b4e10b023..87fb721e6 100644 --- a/Eigen/src/LU/Inverse.h +++ b/Eigen/src/LU/Inverse.h @@ -1,7 +1,7 @@ // This file is part of Eigen, a lightweight C++ template library // for linear algebra. // -// Copyright (C) 2008 Benoit Jacob +// Copyright (C) 2008-2009 Benoit Jacob // // Eigen is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -29,74 +29,96 @@ *** Part 1 : optimized implementations for fixed-size 2,3,4 cases *** ********************************************************************/ -template +template inline void ei_compute_inverse_size2_helper( - const XprType& matrix, const typename MatrixType::Scalar& invdet, - MatrixType* result) + const MatrixType& matrix, const typename ResultType::Scalar& invdet, + ResultType& result) { - result->coeffRef(0,0) = matrix.coeff(1,1) * invdet; - result->coeffRef(1,0) = -matrix.coeff(1,0) * invdet; - result->coeffRef(0,1) = -matrix.coeff(0,1) * invdet; - result->coeffRef(1,1) = matrix.coeff(0,0) * invdet; -} - -template -inline void ei_compute_inverse_size2(const MatrixType& matrix, MatrixType* result) -{ - typedef typename MatrixType::Scalar Scalar; - const Scalar invdet = Scalar(1) / matrix.determinant(); - ei_compute_inverse_size2_helper( matrix, invdet, result ); -} - -template -bool ei_compute_inverse_size2_with_check(const XprType& matrix, MatrixType* result) -{ - typedef typename MatrixType::Scalar Scalar; - const Scalar det = matrix.determinant(); - if(ei_isMuchSmallerThan(det, matrix.cwise().abs().maxCoeff())) return false; - const Scalar invdet = Scalar(1) / det; - ei_compute_inverse_size2_helper( matrix, invdet, result ); - return true; -} - -template -void ei_compute_inverse_size3_helper( - const XprType& matrix, - const typename MatrixType::Scalar& invdet, - const typename MatrixType::Scalar& det_minor00, - const typename MatrixType::Scalar& det_minor10, - const typename MatrixType::Scalar& det_minor20, - MatrixType* result) -{ - result->coeffRef(0, 0) = det_minor00 * invdet; - result->coeffRef(0, 1) = -det_minor10 * invdet; - result->coeffRef(0, 2) = det_minor20 * invdet; - result->coeffRef(1, 0) = -matrix.minor(0,1).determinant() * invdet; - result->coeffRef(1, 1) = matrix.minor(1,1).determinant() * invdet; - result->coeffRef(1, 2) = -matrix.minor(2,1).determinant() * invdet; - result->coeffRef(2, 0) = matrix.minor(0,2).determinant() * invdet; - result->coeffRef(2, 1) = -matrix.minor(1,2).determinant() * invdet; - result->coeffRef(2, 2) = matrix.minor(2,2).determinant() * invdet; -} - -template -bool ei_compute_inverse_size3(const XprType& matrix, MatrixType* result) -{ - typedef typename MatrixType::Scalar Scalar; - const Scalar det_minor00 = matrix.minor(0,0).determinant(); - const Scalar det_minor10 = matrix.minor(1,0).determinant(); - const Scalar det_minor20 = matrix.minor(2,0).determinant(); - const Scalar det = ( det_minor00 * matrix.coeff(0,0) - - det_minor10 * matrix.coeff(1,0) - + det_minor20 * matrix.coeff(2,0) ); - if(Check) if(ei_isMuchSmallerThan(det, matrix.cwise().abs().maxCoeff())) return false; - const Scalar invdet = Scalar(1) / det; - ei_compute_inverse_size3_helper( matrix, invdet, det_minor00, det_minor10, det_minor20, result ); - return true; + result.coeffRef(0,0) = matrix.coeff(1,1) * invdet; + result.coeffRef(1,0) = -matrix.coeff(1,0) * invdet; + result.coeffRef(0,1) = -matrix.coeff(0,1) * invdet; + result.coeffRef(1,1) = matrix.coeff(0,0) * invdet; } template -bool ei_compute_inverse_size4_helper(const MatrixType& matrix, ResultType* result) +inline void ei_compute_inverse_size2(const MatrixType& matrix, ResultType& result) +{ + typedef typename ResultType::Scalar Scalar; + const Scalar invdet = typename MatrixType::Scalar(1) / matrix.determinant(); + ei_compute_inverse_size2_helper(matrix, invdet, result); +} + +template +inline void ei_compute_inverse_and_det_size2_with_check( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& inverse, + typename ResultType::Scalar& determinant, + bool& invertible + ) +{ + typedef typename ResultType::Scalar Scalar; + determinant = matrix.determinant(); + invertible = ei_abs(determinant) > absDeterminantThreshold; + if(!invertible) return; + const Scalar invdet = Scalar(1) / determinant; + ei_compute_inverse_size2_helper(matrix, invdet, inverse); +} + +template +void ei_compute_inverse_size3_helper( + const MatrixType& matrix, + const typename ResultType::Scalar& invdet, + const Matrix& cofactors_col0, + ResultType& result) +{ + result.row(0) = cofactors_col0 * invdet; + result.coeffRef(1,0) = -matrix.minor(0,1).determinant() * invdet; + result.coeffRef(1,1) = matrix.minor(1,1).determinant() * invdet; + result.coeffRef(1,2) = -matrix.minor(2,1).determinant() * invdet; + result.coeffRef(2,0) = matrix.minor(0,2).determinant() * invdet; + result.coeffRef(2,1) = -matrix.minor(1,2).determinant() * invdet; + result.coeffRef(2,2) = matrix.minor(2,2).determinant() * invdet; +} + +template +void ei_compute_inverse_size3( + const MatrixType& matrix, + ResultType& result) +{ + typedef typename ResultType::Scalar Scalar; + Matrix cofactors_col0; + cofactors_col0.coeffRef(0) = matrix.minor(0,0).determinant(); + cofactors_col0.coeffRef(1) = -matrix.minor(1,0).determinant(); + cofactors_col0.coeffRef(2) = matrix.minor(2,0).determinant(); + const Scalar det = (cofactors_col0.cwise()*matrix.col(0)).sum(); + const Scalar invdet = Scalar(1) / det; + ei_compute_inverse_size3_helper(matrix, invdet, cofactors_col0, result); +} + +template +void ei_compute_inverse_and_det_size3_with_check( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& inverse, + typename ResultType::Scalar& determinant, + bool& invertible + ) +{ + typedef typename ResultType::Scalar Scalar; + Matrix cofactors_col0; + cofactors_col0.coeffRef(0) = matrix.minor(0,0).determinant(); + cofactors_col0.coeffRef(1) = -matrix.minor(1,0).determinant(); + cofactors_col0.coeffRef(2) = matrix.minor(2,0).determinant(); + determinant = (cofactors_col0.cwise()*matrix.col(0)).sum(); + invertible = ei_abs(determinant) > absDeterminantThreshold; + if(!invertible) return; + const Scalar invdet = Scalar(1) / determinant; + ei_compute_inverse_size3_helper(matrix, invdet, cofactors_col0, inverse); +} + +template +void ei_compute_inverse_size4_helper(const MatrixType& matrix, ResultType& result) { /* Let's split M into four 2x2 blocks: * (P Q) @@ -111,113 +133,106 @@ bool ei_compute_inverse_size4_helper(const MatrixType& matrix, ResultType* resul * Q' = -(P_inverse*Q) * S' * R' = -S' * (R*P_inverse) */ - typedef Block XprBlock22; + typedef Block XprBlock22; typedef typename MatrixBase::PlainMatrixType Block22; Block22 P_inverse; - if(ei_compute_inverse_size2_with_check(matrix.template block<2,2>(0,0), &P_inverse)) - { - const Block22 Q = matrix.template block<2,2>(0,2); - const Block22 P_inverse_times_Q = P_inverse * Q; - const XprBlock22 R = matrix.template block<2,2>(2,0); - const Block22 R_times_P_inverse = R * P_inverse; - const Block22 R_times_P_inverse_times_Q = R_times_P_inverse * Q; - const XprBlock22 S = matrix.template block<2,2>(2,2); - const Block22 X = S - R_times_P_inverse_times_Q; - Block22 Y; - ei_compute_inverse_size2(X, &Y); - result->template block<2,2>(2,2) = Y; - result->template block<2,2>(2,0) = - Y * R_times_P_inverse; - const Block22 Z = P_inverse_times_Q * Y; - result->template block<2,2>(0,2) = - Z; - result->template block<2,2>(0,0) = P_inverse + Z * R_times_P_inverse; - return true; - } - else - { - return false; - } + ei_compute_inverse_size2(matrix.template block<2,2>(0,0), P_inverse); + const Block22 Q = matrix.template block<2,2>(0,2); + const Block22 P_inverse_times_Q = P_inverse * Q; + const XprBlock22 R = matrix.template block<2,2>(2,0); + const Block22 R_times_P_inverse = R * P_inverse; + const Block22 R_times_P_inverse_times_Q = R_times_P_inverse * Q; + const XprBlock22 S = matrix.template block<2,2>(2,2); + const Block22 X = S - R_times_P_inverse_times_Q; + Block22 Y; + ei_compute_inverse_size2(X, Y); + result.template block<2,2>(2,2) = Y; + result.template block<2,2>(2,0) = - Y * R_times_P_inverse; + const Block22 Z = P_inverse_times_Q * Y; + result.template block<2,2>(0,2) = - Z; + result.template block<2,2>(0,0) = P_inverse + Z * R_times_P_inverse; } -template -bool ei_compute_inverse_size4_with_check(const XprType& matrix, MatrixType* result) +template +void ei_compute_inverse_size4(const MatrixType& _matrix, ResultType& result) { - if(ei_compute_inverse_size4_helper(matrix, result)) - { - // good ! The topleft 2x2 block was invertible, so the 2x2 blocks approach is successful. - return true; - } - else - { - // rare case: the topleft 2x2 block is not invertible (but the matrix itself is assumed to be). - // since this is a rare case, we don't need to optimize it. We just want to handle it with little - // additional code. - MatrixType m(matrix); - m.row(0).swap(m.row(2)); - m.row(1).swap(m.row(3)); - if(ei_compute_inverse_size4_helper(m, result)) + typedef typename ResultType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + + // we will do row permutations on the matrix. This copy should have negligible cost. + // if not, consider working in-place on the matrix (const-cast it, but then undo the permutations + // to nevertheless honor constness) + typename MatrixType::PlainMatrixType matrix(_matrix); + + // let's extract from the 2 first colums a 2x2 block whose determinant is as big as possible. + int good_row0=0, good_row1=1; + RealScalar good_absdet(-1); + // this double for loop shouldn't be too costly: only 6 iterations + for(int row0=0; row0<4; ++row0) { + for(int row1=row0+1; row1<4; ++row1) { - // good, the topleft 2x2 block of m is invertible. Since m is different from matrix in that some - // rows were permuted, the actual inverse of matrix is derived from the inverse of m by permuting - // the corresponding columns. - result->col(0).swap(result->col(2)); - result->col(1).swap(result->col(3)); - return true; - } - else - { - // first, undo the swaps previously made - m.row(0).swap(m.row(2)); - m.row(1).swap(m.row(3)); - // swap row 0 with the the row among 0 and 1 that has the biggest 2 first coeffs - int swap0with = ei_abs(m.coeff(0,0))+ei_abs(m.coeff(0,1))>ei_abs(m.coeff(1,0))+ei_abs(m.coeff(1,1)) ? 0 : 1; - m.row(0).swap(m.row(swap0with)); - // swap row 1 with the the row among 2 and 3 that has the biggest 2 first coeffs - int swap1with = ei_abs(m.coeff(2,0))+ei_abs(m.coeff(2,1))>ei_abs(m.coeff(3,0))+ei_abs(m.coeff(3,1)) ? 2 : 3; - m.row(1).swap(m.row(swap1with)); - if( ei_compute_inverse_size4_helper(m, result) ) + RealScalar absdet = ei_abs(matrix.coeff(row0,0)*matrix.coeff(row1,1) + - matrix.coeff(row0,1)*matrix.coeff(row1,0)); + if(absdet > good_absdet) { - result->col(1).swap(result->col(swap1with)); - result->col(0).swap(result->col(swap0with)); - return true; - } - else - { - // non-invertible matrix - return false; + good_absdet = absdet; + good_row0 = row0; + good_row1 = row1; } } } + // do row permutations to move this 2x2 block to the top + matrix.row(0).swap(matrix.row(good_row0)); + matrix.row(1).swap(matrix.row(good_row1)); + // now applying our helper function is numerically stable + ei_compute_inverse_size4_helper(matrix, result); + // Since we did row permutations on the original matrix, we need to do column permutations + // in the reverse order on the inverse + result.col(1).swap(result.col(good_row1)); + result.col(0).swap(result.col(good_row0)); } - +template +void ei_compute_inverse_and_det_size4_with_check( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& result, + typename ResultType::Scalar& determinant, + bool& invertible + ) +{ + determinant = matrix.determinant(); + invertible = ei_abs(determinant) > absDeterminantThreshold; + if(invertible) ei_compute_inverse_size4(matrix, result); +} /*********************************************** -*** Part 2 : selector and MatrixBase methods *** +*** Part 2 : selectors and MatrixBase methods *** ***********************************************/ template struct ei_compute_inverse { - static inline void run(const MatrixType& matrix, ResultType* result) + static inline void run(const MatrixType& matrix, ResultType& result) { - *result = matrix.partialLu().inverse(); + result = matrix.partialLu().inverse(); } }; template struct ei_compute_inverse { - static inline void run(const MatrixType& matrix, ResultType* result) + static inline void run(const MatrixType& matrix, ResultType& result) { typedef typename MatrixType::Scalar Scalar; - result->coeffRef(0,0) = Scalar(1) / matrix.coeff(0,0); + result.coeffRef(0,0) = Scalar(1) / matrix.coeff(0,0); } }; template struct ei_compute_inverse { - static inline void run(const MatrixType& matrix, ResultType* result) + static inline void run(const MatrixType& matrix, ResultType& result) { ei_compute_inverse_size2(matrix, result); } @@ -226,64 +241,53 @@ struct ei_compute_inverse template struct ei_compute_inverse { - static inline void run(const MatrixType& matrix, ResultType* result) + static inline void run(const MatrixType& matrix, ResultType& result) { - ei_compute_inverse_size3(matrix, result); + ei_compute_inverse_size3(matrix, result); } }; template struct ei_compute_inverse { - static inline void run(const MatrixType& matrix, ResultType* result) + static inline void run(const MatrixType& matrix, ResultType& result) { - ei_compute_inverse_size4_with_check(matrix, result); + ei_compute_inverse_size4(matrix, result); } }; -/** \lu_module - * - * Computes the matrix inverse of this matrix. - * - * \note This matrix must be invertible, otherwise the result is undefined. If you need an invertibility check, use - * computeInverseWithCheck(). - * - * \param result Pointer to the matrix in which to store the result. - * - * Example: \include MatrixBase_computeInverse.cpp - * Output: \verbinclude MatrixBase_computeInverse.out - * - * \sa inverse(), computeInverseWithCheck() - */ -template -template -inline void MatrixBase::computeInverse(ResultType *result) const -{ - ei_assert(rows() == cols()); - EIGEN_STATIC_ASSERT(NumTraits::HasFloatingPoint,NUMERIC_TYPE_MUST_BE_FLOATING_POINT) - ei_compute_inverse::run(eval(), result); -} - /** \lu_module * * \returns the matrix inverse of this matrix. * - * \note This matrix must be invertible, otherwise the result is undefined. If you need an invertibility check, use - * computeInverseWithCheck(). + * For small fixed sizes up to 4x4, this method uses ad-hoc methods (cofactors up to 3x3, Euler's trick for 4x4). + * In the general case, this method uses class PartialLU. * - * \note This method returns a matrix by value, which can be inefficient. To avoid that overhead, - * use computeInverse() instead. + * \note This matrix must be invertible, otherwise the result is undefined. If you need an + * invertibility check, do the following: + * \li for fixed sizes up to 4x4, use computeInverseAndDetWithCheck(). + * \li for the general case, use class LU. * * Example: \include MatrixBase_inverse.cpp * Output: \verbinclude MatrixBase_inverse.out * - * \sa computeInverse(), computeInverseWithCheck() + * \sa computeInverseAndDetWithCheck() */ template inline const typename MatrixBase::PlainMatrixType MatrixBase::inverse() const { - typename MatrixBase::PlainMatrixType result; - computeInverse(&result); + EIGEN_STATIC_ASSERT(NumTraits::HasFloatingPoint,NUMERIC_TYPE_MUST_BE_FLOATING_POINT) + ei_assert(rows() == cols()); + typedef typename MatrixBase::PlainMatrixType ResultType; + ResultType result(rows(), cols()); + // for 2x2, it's worth giving a chance to avoid evaluating. + // for larger sizes, evaluating has negligible cost and limits code size. + typedef typename ei_meta_if< + RowsAtCompileTime == 2, + typename ei_cleantype::type>::type, + PlainMatrixType + >::ret MatrixType; + ei_compute_inverse::run(derived(), result); return result; } @@ -293,74 +297,108 @@ inline const typename MatrixBase::PlainMatrixType MatrixBase:: *******************************************/ template -struct ei_compute_inverse_with_check +struct ei_compute_inverse_and_det_with_check {}; + +template +struct ei_compute_inverse_and_det_with_check { - static inline bool run(const MatrixType& matrix, ResultType* result) + static inline void run( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& result, + typename ResultType::Scalar& determinant, + bool& invertible + ) { - typedef typename MatrixType::Scalar Scalar; - LU lu( matrix ); - if( !lu.isInvertible() ) return false; - *result = lu.inverse(); - return true; + determinant = matrix.coeff(0,0); + invertible = ei_abs(determinant) > absDeterminantThreshold; + if(invertible) result.coeffRef(0,0) = typename ResultType::Scalar(1) / determinant; } }; template -struct ei_compute_inverse_with_check +struct ei_compute_inverse_and_det_with_check { - static inline bool run(const MatrixType& matrix, ResultType* result) + static inline void run( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& result, + typename ResultType::Scalar& determinant, + bool& invertible + ) { - typedef typename MatrixType::Scalar Scalar; - if( matrix.coeff(0,0) == Scalar(0) ) return false; - result->coeffRef(0,0) = Scalar(1) / matrix.coeff(0,0); - return true; + ei_compute_inverse_and_det_size2_with_check + (matrix, absDeterminantThreshold, result, determinant, invertible); } }; template -struct ei_compute_inverse_with_check +struct ei_compute_inverse_and_det_with_check { - static inline bool run(const MatrixType& matrix, ResultType* result) + static inline void run( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& result, + typename ResultType::Scalar& determinant, + bool& invertible + ) { - return ei_compute_inverse_size2_with_check(matrix, result); + ei_compute_inverse_and_det_size3_with_check + (matrix, absDeterminantThreshold, result, determinant, invertible); } }; template -struct ei_compute_inverse_with_check +struct ei_compute_inverse_and_det_with_check { - static inline bool run(const MatrixType& matrix, ResultType* result) + static inline void run( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& result, + typename ResultType::Scalar& determinant, + bool& invertible + ) { - return ei_compute_inverse_size3(matrix, result); - } -}; - -template -struct ei_compute_inverse_with_check -{ - static inline bool run(const MatrixType& matrix, ResultType* result) - { - return ei_compute_inverse_size4_with_check(matrix, result); + ei_compute_inverse_and_det_size4_with_check + (matrix, absDeterminantThreshold, result, determinant, invertible); } }; /** \lu_module * - * Computation of matrix inverse, with invertibility check. + * Computation of matrix inverse and determinant, with invertibility check. * - * \returns true if the matrix is invertible, false otherwise. + * This is only for fixed-size square matrices of size up to 4x4. * - * \param result Pointer to the matrix in which to store the result. + * \param inverse Reference to the matrix in which to store the inverse. + * \param determinant Reference to the variable in which to store the inverse. + * \param invertible Reference to the bool variable in which to store whether the matrix is invertible. + * \param absDeterminantThreshold Optional parameter controlling the invertibility check. + * The matrix will be declared invertible if the absolute value of its + * determinant is greater than this threshold. * - * \sa inverse(), computeInverse() + * \sa inverse() */ template template -inline bool MatrixBase::computeInverseWithCheck(ResultType *result) const +inline void MatrixBase::computeInverseAndDetWithCheck( + ResultType& inverse, + typename ResultType::Scalar& determinant, + bool& invertible, + const RealScalar& absDeterminantThreshold + ) const { + // i'd love to put some static assertions there, but SFINAE means that they have no effect... ei_assert(rows() == cols()); - EIGEN_STATIC_ASSERT(NumTraits::HasFloatingPoint,NUMERIC_TYPE_MUST_BE_FLOATING_POINT) - return ei_compute_inverse_with_check::run(eval(), result); + // for 2x2, it's worth giving a chance to avoid evaluating. + // for larger sizes, evaluating has negligible cost and limits code size. + typedef typename ei_meta_if< + RowsAtCompileTime == 2, + typename ei_cleantype::type>::type, + PlainMatrixType + >::ret MatrixType; + ei_compute_inverse_and_det_with_check::run + (derived(), absDeterminantThreshold, inverse, determinant, invertible); } diff --git a/test/inverse.cpp b/test/inverse.cpp index 6fc65786c..b8170a738 100644 --- a/test/inverse.cpp +++ b/test/inverse.cpp @@ -53,9 +53,6 @@ template void inverse(const MatrixType& m) m2 = m1.inverse(); VERIFY_IS_APPROX(m1, m2.inverse() ); - m1.computeInverse(&m2); - VERIFY_IS_APPROX(m1, m2.inverse() ); - VERIFY_IS_APPROX((Scalar(2)*m2).inverse(), m2.inverse()*Scalar(0.5)); VERIFY_IS_APPROX(identity, m1.inverse() * m1 ); @@ -66,17 +63,23 @@ template void inverse(const MatrixType& m) // since for the general case we implement separately row-major and col-major, test that VERIFY_IS_APPROX(m1.transpose().inverse(), m1.inverse().transpose()); - //computeInverseWithCheck tests +#if !defined(EIGEN_TEST_PART_5) && !defined(EIGEN_TEST_PART_6) + //computeInverseAndDetWithCheck tests //First: an invertible matrix - bool invertible = m1.computeInverseWithCheck(&m2); + bool invertible; + RealScalar det; + m1.computeInverseAndDetWithCheck(m2, det, invertible); VERIFY(invertible); VERIFY_IS_APPROX(identity, m1*m2); + VERIFY_IS_APPROX(det, m1.determinant()); //Second: a rank one matrix (not invertible, except for 1x1 matrices) VectorType v3 = VectorType::Random(rows); MatrixType m3 = v3*v3.transpose(), m4(rows,cols); - invertible = m3.computeInverseWithCheck( &m4 ); + m3.computeInverseAndDetWithCheck(m4, det, invertible); VERIFY( rows==1 ? invertible : !invertible ); + VERIFY_IS_APPROX(det, m3.determinant()); +#endif } void test_inverse() From 07d1bcffda9436fe8ce8c56091d68841b8c3be59 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 26 Oct 2009 12:30:29 -0400 Subject: [PATCH 20/34] remove 1 useless layer of functions --- Eigen/src/LU/Inverse.h | 399 ++++++++++++++++++----------------------- 1 file changed, 175 insertions(+), 224 deletions(-) diff --git a/Eigen/src/LU/Inverse.h b/Eigen/src/LU/Inverse.h index 87fb721e6..71dabc663 100644 --- a/Eigen/src/LU/Inverse.h +++ b/Eigen/src/LU/Inverse.h @@ -25,9 +25,56 @@ #ifndef EIGEN_INVERSE_H #define EIGEN_INVERSE_H -/******************************************************************** -*** Part 1 : optimized implementations for fixed-size 2,3,4 cases *** -********************************************************************/ +/********************************** +*** General case implementation *** +**********************************/ + +template +struct ei_compute_inverse +{ + static inline void run(const MatrixType& matrix, ResultType& result) + { + result = matrix.partialLu().inverse(); + } +}; + +template +struct ei_compute_inverse_and_det_with_check { /* nothing! general case not supported. */ }; + +/**************************** +*** Size 1 implementation *** +****************************/ + +template +struct ei_compute_inverse +{ + static inline void run(const MatrixType& matrix, ResultType& result) + { + typedef typename MatrixType::Scalar Scalar; + result.coeffRef(0,0) = Scalar(1) / matrix.coeff(0,0); + } +}; + +template +struct ei_compute_inverse_and_det_with_check +{ + static inline void run( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& result, + typename ResultType::Scalar& determinant, + bool& invertible + ) + { + determinant = matrix.coeff(0,0); + invertible = ei_abs(determinant) > absDeterminantThreshold; + if(invertible) result.coeffRef(0,0) = typename ResultType::Scalar(1) / determinant; + } +}; + +/**************************** +*** Size 2 implementation *** +****************************/ template inline void ei_compute_inverse_size2_helper( @@ -41,29 +88,39 @@ inline void ei_compute_inverse_size2_helper( } template -inline void ei_compute_inverse_size2(const MatrixType& matrix, ResultType& result) +struct ei_compute_inverse { - typedef typename ResultType::Scalar Scalar; - const Scalar invdet = typename MatrixType::Scalar(1) / matrix.determinant(); - ei_compute_inverse_size2_helper(matrix, invdet, result); -} + static inline void run(const MatrixType& matrix, ResultType& result) + { + typedef typename ResultType::Scalar Scalar; + const Scalar invdet = typename MatrixType::Scalar(1) / matrix.determinant(); + ei_compute_inverse_size2_helper(matrix, invdet, result); + } +}; template -inline void ei_compute_inverse_and_det_size2_with_check( - const MatrixType& matrix, - const typename MatrixType::RealScalar& absDeterminantThreshold, - ResultType& inverse, - typename ResultType::Scalar& determinant, - bool& invertible - ) +struct ei_compute_inverse_and_det_with_check { - typedef typename ResultType::Scalar Scalar; - determinant = matrix.determinant(); - invertible = ei_abs(determinant) > absDeterminantThreshold; - if(!invertible) return; - const Scalar invdet = Scalar(1) / determinant; - ei_compute_inverse_size2_helper(matrix, invdet, inverse); -} + static inline void run( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& inverse, + typename ResultType::Scalar& determinant, + bool& invertible + ) + { + typedef typename ResultType::Scalar Scalar; + determinant = matrix.determinant(); + invertible = ei_abs(determinant) > absDeterminantThreshold; + if(!invertible) return; + const Scalar invdet = Scalar(1) / determinant; + ei_compute_inverse_size2_helper(matrix, invdet, inverse); + } +}; + +/**************************** +*** Size 3 implementation *** +****************************/ template void ei_compute_inverse_size3_helper( @@ -82,40 +139,48 @@ void ei_compute_inverse_size3_helper( } template -void ei_compute_inverse_size3( - const MatrixType& matrix, - ResultType& result) +struct ei_compute_inverse { - typedef typename ResultType::Scalar Scalar; - Matrix cofactors_col0; - cofactors_col0.coeffRef(0) = matrix.minor(0,0).determinant(); - cofactors_col0.coeffRef(1) = -matrix.minor(1,0).determinant(); - cofactors_col0.coeffRef(2) = matrix.minor(2,0).determinant(); - const Scalar det = (cofactors_col0.cwise()*matrix.col(0)).sum(); - const Scalar invdet = Scalar(1) / det; - ei_compute_inverse_size3_helper(matrix, invdet, cofactors_col0, result); -} + static inline void run(const MatrixType& matrix, ResultType& result) + { + typedef typename ResultType::Scalar Scalar; + Matrix cofactors_col0; + cofactors_col0.coeffRef(0) = matrix.minor(0,0).determinant(); + cofactors_col0.coeffRef(1) = -matrix.minor(1,0).determinant(); + cofactors_col0.coeffRef(2) = matrix.minor(2,0).determinant(); + const Scalar det = (cofactors_col0.cwise()*matrix.col(0)).sum(); + const Scalar invdet = Scalar(1) / det; + ei_compute_inverse_size3_helper(matrix, invdet, cofactors_col0, result); + } +}; template -void ei_compute_inverse_and_det_size3_with_check( - const MatrixType& matrix, - const typename MatrixType::RealScalar& absDeterminantThreshold, - ResultType& inverse, - typename ResultType::Scalar& determinant, - bool& invertible - ) +struct ei_compute_inverse_and_det_with_check { - typedef typename ResultType::Scalar Scalar; - Matrix cofactors_col0; - cofactors_col0.coeffRef(0) = matrix.minor(0,0).determinant(); - cofactors_col0.coeffRef(1) = -matrix.minor(1,0).determinant(); - cofactors_col0.coeffRef(2) = matrix.minor(2,0).determinant(); - determinant = (cofactors_col0.cwise()*matrix.col(0)).sum(); - invertible = ei_abs(determinant) > absDeterminantThreshold; - if(!invertible) return; - const Scalar invdet = Scalar(1) / determinant; - ei_compute_inverse_size3_helper(matrix, invdet, cofactors_col0, inverse); -} + static inline void run( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& inverse, + typename ResultType::Scalar& determinant, + bool& invertible + ) + { + typedef typename ResultType::Scalar Scalar; + Matrix cofactors_col0; + cofactors_col0.coeffRef(0) = matrix.minor(0,0).determinant(); + cofactors_col0.coeffRef(1) = -matrix.minor(1,0).determinant(); + cofactors_col0.coeffRef(2) = matrix.minor(2,0).determinant(); + determinant = (cofactors_col0.cwise()*matrix.col(0)).sum(); + invertible = ei_abs(determinant) > absDeterminantThreshold; + if(!invertible) return; + const Scalar invdet = Scalar(1) / determinant; + ei_compute_inverse_size3_helper(matrix, invdet, cofactors_col0, inverse); + } +}; + +/**************************** +*** Size 4 implementation *** +****************************/ template void ei_compute_inverse_size4_helper(const MatrixType& matrix, ResultType& result) @@ -136,7 +201,7 @@ void ei_compute_inverse_size4_helper(const MatrixType& matrix, ResultType& resul typedef Block XprBlock22; typedef typename MatrixBase::PlainMatrixType Block22; Block22 P_inverse; - ei_compute_inverse_size2(matrix.template block<2,2>(0,0), P_inverse); + ei_compute_inverse::run(matrix.template block<2,2>(0,0), P_inverse); const Block22 Q = matrix.template block<2,2>(0,2); const Block22 P_inverse_times_Q = P_inverse * Q; const XprBlock22 R = matrix.template block<2,2>(2,0); @@ -145,7 +210,7 @@ void ei_compute_inverse_size4_helper(const MatrixType& matrix, ResultType& resul const XprBlock22 S = matrix.template block<2,2>(2,2); const Block22 X = S - R_times_P_inverse_times_Q; Block22 Y; - ei_compute_inverse_size2(X, Y); + ei_compute_inverse::run(X, Y); result.template block<2,2>(2,2) = Y; result.template block<2,2>(2,0) = - Y * R_times_P_inverse; const Block22 Z = P_inverse_times_Q * Y; @@ -153,109 +218,69 @@ void ei_compute_inverse_size4_helper(const MatrixType& matrix, ResultType& resul result.template block<2,2>(0,0) = P_inverse + Z * R_times_P_inverse; } -template -void ei_compute_inverse_size4(const MatrixType& _matrix, ResultType& result) -{ - typedef typename ResultType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; - - // we will do row permutations on the matrix. This copy should have negligible cost. - // if not, consider working in-place on the matrix (const-cast it, but then undo the permutations - // to nevertheless honor constness) - typename MatrixType::PlainMatrixType matrix(_matrix); - - // let's extract from the 2 first colums a 2x2 block whose determinant is as big as possible. - int good_row0=0, good_row1=1; - RealScalar good_absdet(-1); - // this double for loop shouldn't be too costly: only 6 iterations - for(int row0=0; row0<4; ++row0) { - for(int row1=row0+1; row1<4; ++row1) - { - RealScalar absdet = ei_abs(matrix.coeff(row0,0)*matrix.coeff(row1,1) - - matrix.coeff(row0,1)*matrix.coeff(row1,0)); - if(absdet > good_absdet) - { - good_absdet = absdet; - good_row0 = row0; - good_row1 = row1; - } - } - } - // do row permutations to move this 2x2 block to the top - matrix.row(0).swap(matrix.row(good_row0)); - matrix.row(1).swap(matrix.row(good_row1)); - // now applying our helper function is numerically stable - ei_compute_inverse_size4_helper(matrix, result); - // Since we did row permutations on the original matrix, we need to do column permutations - // in the reverse order on the inverse - result.col(1).swap(result.col(good_row1)); - result.col(0).swap(result.col(good_row0)); -} - -template -void ei_compute_inverse_and_det_size4_with_check( - const MatrixType& matrix, - const typename MatrixType::RealScalar& absDeterminantThreshold, - ResultType& result, - typename ResultType::Scalar& determinant, - bool& invertible - ) -{ - determinant = matrix.determinant(); - invertible = ei_abs(determinant) > absDeterminantThreshold; - if(invertible) ei_compute_inverse_size4(matrix, result); -} - -/*********************************************** -*** Part 2 : selectors and MatrixBase methods *** -***********************************************/ - -template -struct ei_compute_inverse -{ - static inline void run(const MatrixType& matrix, ResultType& result) - { - result = matrix.partialLu().inverse(); - } -}; - -template -struct ei_compute_inverse -{ - static inline void run(const MatrixType& matrix, ResultType& result) - { - typedef typename MatrixType::Scalar Scalar; - result.coeffRef(0,0) = Scalar(1) / matrix.coeff(0,0); - } -}; - -template -struct ei_compute_inverse -{ - static inline void run(const MatrixType& matrix, ResultType& result) - { - ei_compute_inverse_size2(matrix, result); - } -}; - -template -struct ei_compute_inverse -{ - static inline void run(const MatrixType& matrix, ResultType& result) - { - ei_compute_inverse_size3(matrix, result); - } -}; - template struct ei_compute_inverse { - static inline void run(const MatrixType& matrix, ResultType& result) + static inline void run(const MatrixType& _matrix, ResultType& result) { - ei_compute_inverse_size4(matrix, result); + typedef typename ResultType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + + // we will do row permutations on the matrix. This copy should have negligible cost. + // if not, consider working in-place on the matrix (const-cast it, but then undo the permutations + // to nevertheless honor constness) + typename MatrixType::PlainMatrixType matrix(_matrix); + + // let's extract from the 2 first colums a 2x2 block whose determinant is as big as possible. + int good_row0=0, good_row1=1; + RealScalar good_absdet(-1); + // this double for loop shouldn't be too costly: only 6 iterations + for(int row0=0; row0<4; ++row0) { + for(int row1=row0+1; row1<4; ++row1) + { + RealScalar absdet = ei_abs(matrix.coeff(row0,0)*matrix.coeff(row1,1) + - matrix.coeff(row0,1)*matrix.coeff(row1,0)); + if(absdet > good_absdet) + { + good_absdet = absdet; + good_row0 = row0; + good_row1 = row1; + } + } + } + // do row permutations to move this 2x2 block to the top + matrix.row(0).swap(matrix.row(good_row0)); + matrix.row(1).swap(matrix.row(good_row1)); + // now applying our helper function is numerically stable + ei_compute_inverse_size4_helper(matrix, result); + // Since we did row permutations on the original matrix, we need to do column permutations + // in the reverse order on the inverse + result.col(1).swap(result.col(good_row1)); + result.col(0).swap(result.col(good_row0)); } }; +template +struct ei_compute_inverse_and_det_with_check +{ + static inline void run( + const MatrixType& matrix, + const typename MatrixType::RealScalar& absDeterminantThreshold, + ResultType& inverse, + typename ResultType::Scalar& determinant, + bool& invertible + ) + { + determinant = matrix.determinant(); + invertible = ei_abs(determinant) > absDeterminantThreshold; + if(invertible) ei_compute_inverse::run(matrix, inverse); + } +}; + +/************************* +*** MatrixBase methods *** +*************************/ + /** \lu_module * * \returns the matrix inverse of this matrix. @@ -291,79 +316,6 @@ inline const typename MatrixBase::PlainMatrixType MatrixBase:: return result; } - -/******************************************** - * Compute inverse with invertibility check * - *******************************************/ - -template -struct ei_compute_inverse_and_det_with_check {}; - -template -struct ei_compute_inverse_and_det_with_check -{ - static inline void run( - const MatrixType& matrix, - const typename MatrixType::RealScalar& absDeterminantThreshold, - ResultType& result, - typename ResultType::Scalar& determinant, - bool& invertible - ) - { - determinant = matrix.coeff(0,0); - invertible = ei_abs(determinant) > absDeterminantThreshold; - if(invertible) result.coeffRef(0,0) = typename ResultType::Scalar(1) / determinant; - } -}; - -template -struct ei_compute_inverse_and_det_with_check -{ - static inline void run( - const MatrixType& matrix, - const typename MatrixType::RealScalar& absDeterminantThreshold, - ResultType& result, - typename ResultType::Scalar& determinant, - bool& invertible - ) - { - ei_compute_inverse_and_det_size2_with_check - (matrix, absDeterminantThreshold, result, determinant, invertible); - } -}; - -template -struct ei_compute_inverse_and_det_with_check -{ - static inline void run( - const MatrixType& matrix, - const typename MatrixType::RealScalar& absDeterminantThreshold, - ResultType& result, - typename ResultType::Scalar& determinant, - bool& invertible - ) - { - ei_compute_inverse_and_det_size3_with_check - (matrix, absDeterminantThreshold, result, determinant, invertible); - } -}; - -template -struct ei_compute_inverse_and_det_with_check -{ - static inline void run( - const MatrixType& matrix, - const typename MatrixType::RealScalar& absDeterminantThreshold, - ResultType& result, - typename ResultType::Scalar& determinant, - bool& invertible - ) - { - ei_compute_inverse_and_det_size4_with_check - (matrix, absDeterminantThreshold, result, determinant, invertible); - } -}; - /** \lu_module * * Computation of matrix inverse and determinant, with invertibility check. @@ -401,5 +353,4 @@ inline void MatrixBase::computeInverseAndDetWithCheck( (derived(), absDeterminantThreshold, inverse, determinant, invertible); } - #endif // EIGEN_INVERSE_H From 44cdbaba4d084b31854ed5bec58f2887f0479b81 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 26 Oct 2009 14:16:50 -0400 Subject: [PATCH 21/34] * make inverse() do a ReturnByValue * add computeInverseWithCheck * doc improvements * update test --- Eigen/src/Core/MatrixBase.h | 8 ++- Eigen/src/Core/util/ForwardDeclarations.h | 1 + Eigen/src/LU/Inverse.h | 76 +++++++++++++++++++---- Eigen/src/LU/PartialLU.h | 16 ++--- test/inverse.cpp | 9 +++ 5 files changed, 89 insertions(+), 21 deletions(-) diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index 24b3feb6f..a2b57f8ea 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -701,7 +701,7 @@ template class MatrixBase const LU lu() const; const PartialLU partialLu() const; - const PlainMatrixType inverse() const; + const ei_inverse_impl inverse() const; template void computeInverseAndDetWithCheck( ResultType& inverse, @@ -709,6 +709,12 @@ template class MatrixBase bool& invertible, const RealScalar& absDeterminantThreshold = precision() ) const; + template + void computeInverseWithCheck( + ResultType& inverse, + bool& invertible, + const RealScalar& absDeterminantThreshold = precision() + ) const; Scalar determinant() const; /////////// Cholesky module /////////// diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index 65e5ce687..01adca96d 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -116,6 +116,7 @@ template class Reverse; template class LU; template class PartialLU; +template struct ei_inverse_impl; template class HouseholderQR; template class ColPivotingHouseholderQR; template class FullPivotingHouseholderQR; diff --git a/Eigen/src/LU/Inverse.h b/Eigen/src/LU/Inverse.h index 71dabc663..965866da6 100644 --- a/Eigen/src/LU/Inverse.h +++ b/Eigen/src/LU/Inverse.h @@ -281,6 +281,38 @@ struct ei_compute_inverse_and_det_with_check *** MatrixBase methods *** *************************/ +template +struct ei_traits > +{ + typedef typename MatrixType::PlainMatrixType ReturnMatrixType; +}; + +template +struct ei_inverse_impl : public ReturnByValue > +{ + // for 2x2, it's worth giving a chance to avoid evaluating. + // for larger sizes, evaluating has negligible cost and limits code size. + typedef typename ei_meta_if< + MatrixType::RowsAtCompileTime == 2, + typename ei_nested::type, + typename ei_eval::type + >::ret MatrixTypeNested; + typedef typename ei_cleantype::type MatrixTypeNestedCleaned; + const MatrixTypeNested m_matrix; + + ei_inverse_impl(const MatrixType& matrix) + : m_matrix(matrix) + {} + + inline int rows() const { return m_matrix.rows(); } + inline int cols() const { return m_matrix.cols(); } + + template inline void evalTo(Dest& dst) const + { + ei_compute_inverse::run(m_matrix, dst); + } +}; + /** \lu_module * * \returns the matrix inverse of this matrix. @@ -299,21 +331,11 @@ struct ei_compute_inverse_and_det_with_check * \sa computeInverseAndDetWithCheck() */ template -inline const typename MatrixBase::PlainMatrixType MatrixBase::inverse() const +inline const ei_inverse_impl MatrixBase::inverse() const { EIGEN_STATIC_ASSERT(NumTraits::HasFloatingPoint,NUMERIC_TYPE_MUST_BE_FLOATING_POINT) ei_assert(rows() == cols()); - typedef typename MatrixBase::PlainMatrixType ResultType; - ResultType result(rows(), cols()); - // for 2x2, it's worth giving a chance to avoid evaluating. - // for larger sizes, evaluating has negligible cost and limits code size. - typedef typename ei_meta_if< - RowsAtCompileTime == 2, - typename ei_cleantype::type>::type, - PlainMatrixType - >::ret MatrixType; - ei_compute_inverse::run(derived(), result); - return result; + return ei_inverse_impl(derived()); } /** \lu_module @@ -329,7 +351,7 @@ inline const typename MatrixBase::PlainMatrixType MatrixBase:: * The matrix will be declared invertible if the absolute value of its * determinant is greater than this threshold. * - * \sa inverse() + * \sa inverse(), computeInverseWithCheck() */ template template @@ -353,4 +375,32 @@ inline void MatrixBase::computeInverseAndDetWithCheck( (derived(), absDeterminantThreshold, inverse, determinant, invertible); } +/** \lu_module + * + * Computation of matrix inverse, with invertibility check. + * + * This is only for fixed-size square matrices of size up to 4x4. + * + * \param inverse Reference to the matrix in which to store the inverse. + * \param invertible Reference to the bool variable in which to store whether the matrix is invertible. + * \param absDeterminantThreshold Optional parameter controlling the invertibility check. + * The matrix will be declared invertible if the absolute value of its + * determinant is greater than this threshold. + * + * \sa inverse(), computeInverseAndDetWithCheck() + */ +template +template +inline void MatrixBase::computeInverseWithCheck( + ResultType& inverse, + bool& invertible, + const RealScalar& absDeterminantThreshold + ) const +{ + RealScalar determinant; + // i'd love to put some static assertions there, but SFINAE means that they have no effect... + ei_assert(rows() == cols()); + computeInverseAndDetWithCheck(inverse,determinant,invertible,absDeterminantThreshold); +} + #endif // EIGEN_INVERSE_H diff --git a/Eigen/src/LU/PartialLU.h b/Eigen/src/LU/PartialLU.h index 30e633eda..e8d21e5ad 100644 --- a/Eigen/src/LU/PartialLU.h +++ b/Eigen/src/LU/PartialLU.h @@ -40,18 +40,20 @@ template struct ei_partiallu_solve_impl; * is decomposed as A = PLU where L is unit-lower-triangular, U is upper-triangular, and P * is a permutation matrix. * - * Typically, partial pivoting LU decomposition is only considered numerically stable for square invertible matrices. - * So in this class, we plainly require that and take advantage of that to do some simplifications and optimizations. - * This class will assert that the matrix is square, but it won't (actually it can't) check that the matrix is invertible: - * it is your task to check that you only use this decomposition on invertible matrices. + * Typically, partial pivoting LU decomposition is only considered numerically stable for square invertible + * matrices. Thus LAPACK's dgesv and dgesvx require the matrix to be square and invertible. The present class + * does the same. It will assert that the matrix is square, but it won't (actually it can't) check that the + * matrix is invertible: it is your task to check that you only use this decomposition on invertible matrices. * - * The guaranteed safe alternative, working for all matrices, is the full pivoting LU decomposition, provided by class LU. + * The guaranteed safe alternative, working for all matrices, is the full pivoting LU decomposition, provided + * by class LU. * * This is \b not a rank-revealing LU decomposition. Many features are intentionally absent from this class, * such as rank computation. If you need these features, use class LU. * - * This LU decomposition is suitable to invert invertible matrices. It is what MatrixBase::inverse() uses. On the other hand, - * it is \b not suitable to determine whether a given matrix is invertible. + * This LU decomposition is suitable to invert invertible matrices. It is what MatrixBase::inverse() uses + * in the general case. + * On the other hand, it is \b not suitable to determine whether a given matrix is invertible. * * The data of the LU decomposition can be directly accessed through the methods matrixLU(), permutationP(). * diff --git a/test/inverse.cpp b/test/inverse.cpp index b8170a738..269678fd4 100644 --- a/test/inverse.cpp +++ b/test/inverse.cpp @@ -68,17 +68,26 @@ template void inverse(const MatrixType& m) //First: an invertible matrix bool invertible; RealScalar det; + + m2.setZero(); m1.computeInverseAndDetWithCheck(m2, det, invertible); VERIFY(invertible); VERIFY_IS_APPROX(identity, m1*m2); VERIFY_IS_APPROX(det, m1.determinant()); + m2.setZero(); + m1.computeInverseWithCheck(m2, invertible); + VERIFY(invertible); + VERIFY_IS_APPROX(identity, m1*m2); + //Second: a rank one matrix (not invertible, except for 1x1 matrices) VectorType v3 = VectorType::Random(rows); MatrixType m3 = v3*v3.transpose(), m4(rows,cols); m3.computeInverseAndDetWithCheck(m4, det, invertible); VERIFY( rows==1 ? invertible : !invertible ); VERIFY_IS_APPROX(det, m3.determinant()); + m3.computeInverseWithCheck(m4, invertible); + VERIFY( rows==1 ? invertible : !invertible ); #endif } From 1f1c04cac1d8a87cbb34741d141df646b2da2827 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 26 Oct 2009 14:37:43 -0400 Subject: [PATCH 22/34] sync the documentation examples --- Eigen/src/LU/Inverse.h | 6 ++++++ doc/examples/Tutorial_PartialLU_solve.cpp | 3 +-- doc/snippets/MatrixBase_computeInverse.cpp | 5 ----- .../MatrixBase_computeInverseAndDetWithCheck.cpp | 13 +++++++++++++ doc/snippets/MatrixBase_computeInverseWithCheck.cpp | 8 +++++--- doc/snippets/PartialLU_solve.cpp | 3 +-- doc/snippets/Tutorial_solve_multiple_rhs.cpp | 2 +- doc/snippets/Tutorial_solve_reuse_decomposition.cpp | 4 ++-- doc/snippets/Tutorial_solve_singular.cpp | 2 +- 9 files changed, 30 insertions(+), 16 deletions(-) delete mode 100644 doc/snippets/MatrixBase_computeInverse.cpp create mode 100644 doc/snippets/MatrixBase_computeInverseAndDetWithCheck.cpp diff --git a/Eigen/src/LU/Inverse.h b/Eigen/src/LU/Inverse.h index 965866da6..a168d58cd 100644 --- a/Eigen/src/LU/Inverse.h +++ b/Eigen/src/LU/Inverse.h @@ -351,6 +351,9 @@ inline const ei_inverse_impl MatrixBase::inverse() const * The matrix will be declared invertible if the absolute value of its * determinant is greater than this threshold. * + * Example: \include MatrixBase_computeInverseAndDetWithCheck.cpp + * Output: \verbinclude MatrixBase_computeInverseAndDetWithCheck.out + * * \sa inverse(), computeInverseWithCheck() */ template @@ -387,6 +390,9 @@ inline void MatrixBase::computeInverseAndDetWithCheck( * The matrix will be declared invertible if the absolute value of its * determinant is greater than this threshold. * + * Example: \include MatrixBase_computeInverseWithCheck.cpp + * Output: \verbinclude MatrixBase_computeInverseWithCheck.out + * * \sa inverse(), computeInverseAndDetWithCheck() */ template diff --git a/doc/examples/Tutorial_PartialLU_solve.cpp b/doc/examples/Tutorial_PartialLU_solve.cpp index 80c393f9a..4cbd8c10d 100644 --- a/doc/examples/Tutorial_PartialLU_solve.cpp +++ b/doc/examples/Tutorial_PartialLU_solve.cpp @@ -12,7 +12,6 @@ int main(int, char *[]) b << 3, 3, 4; cout << "Here is the matrix A:" << endl << A << endl; cout << "Here is the vector b:" << endl << b << endl; - Vector3f x; - A.partialLu().solve(b, &x); + Vector3f x = A.partialLu().solve(b); cout << "The solution is:" << endl << x << endl; } diff --git a/doc/snippets/MatrixBase_computeInverse.cpp b/doc/snippets/MatrixBase_computeInverse.cpp deleted file mode 100644 index ba7377a4a..000000000 --- a/doc/snippets/MatrixBase_computeInverse.cpp +++ /dev/null @@ -1,5 +0,0 @@ -Matrix3d m = Matrix3d::Random(); -cout << "Here is the matrix m:" << endl << m << endl; -Matrix3d inv; -m.computeInverse(&inv); -cout << "Its inverse is:" << endl << inv << endl; diff --git a/doc/snippets/MatrixBase_computeInverseAndDetWithCheck.cpp b/doc/snippets/MatrixBase_computeInverseAndDetWithCheck.cpp new file mode 100644 index 000000000..a7b084fd0 --- /dev/null +++ b/doc/snippets/MatrixBase_computeInverseAndDetWithCheck.cpp @@ -0,0 +1,13 @@ +Matrix3d m = Matrix3d::Random(); +cout << "Here is the matrix m:" << endl << m << endl; +Matrix3d inverse; +bool invertible; +double determinant; +m.computeInverseAndDetWithCheck(inverse,determinant,invertible); +cout << "Its determinant is " << determinant << endl; +if(invertible) { + cout << "It is invertible, and its inverse is:" << endl << inverse << endl; +} +else { + cout << "It is not invertible." << endl; +} diff --git a/doc/snippets/MatrixBase_computeInverseWithCheck.cpp b/doc/snippets/MatrixBase_computeInverseWithCheck.cpp index 19e24c90b..873a9f870 100644 --- a/doc/snippets/MatrixBase_computeInverseWithCheck.cpp +++ b/doc/snippets/MatrixBase_computeInverseWithCheck.cpp @@ -1,8 +1,10 @@ Matrix3d m = Matrix3d::Random(); cout << "Here is the matrix m:" << endl << m << endl; -Matrix3d inv; -if(m.computeInverseWithCheck(&inv)) { - cout << "It is invertible, and its inverse is:" << endl << inv << endl; +Matrix3d inverse; +bool invertible; +m.computeInverseWithCheck(inverse,invertible); +if(invertible) { + cout << "It is invertible, and its inverse is:" << endl << inverse << endl; } else { cout << "It is not invertible." << endl; diff --git a/doc/snippets/PartialLU_solve.cpp b/doc/snippets/PartialLU_solve.cpp index d9ccbe9f0..69e788dc4 100644 --- a/doc/snippets/PartialLU_solve.cpp +++ b/doc/snippets/PartialLU_solve.cpp @@ -2,7 +2,6 @@ MatrixXd A = MatrixXd::Random(3,3); MatrixXd B = MatrixXd::Random(3,2); cout << "Here is the invertible matrix A:" << endl << A << endl; cout << "Here is the matrix B:" << endl << B << endl; -MatrixXd X; -A.partialLu().solve(B, &X); +MatrixXd X = A.partialLu().solve(B); cout << "Here is the (unique) solution X to the equation AX=B:" << endl << X << endl; cout << "Relative error: " << (A*X-B).norm() / B.norm() << endl; diff --git a/doc/snippets/Tutorial_solve_multiple_rhs.cpp b/doc/snippets/Tutorial_solve_multiple_rhs.cpp index fbb15165a..1ffcc61f0 100644 --- a/doc/snippets/Tutorial_solve_multiple_rhs.cpp +++ b/doc/snippets/Tutorial_solve_multiple_rhs.cpp @@ -3,7 +3,7 @@ A << 1,2,3, 4,5,6, 7,8,10; Matrix B; B << 3,1, 3,1, 4,1; Matrix X; -A.partialLu().solve(B, &X); +X = A.partialLu().solve(B); cout << "The solution with right-hand side (3,3,4) is:" << endl; cout << X.col(0) << endl; cout << "The solution with right-hand side (1,1,1) is:" << endl; diff --git a/doc/snippets/Tutorial_solve_reuse_decomposition.cpp b/doc/snippets/Tutorial_solve_reuse_decomposition.cpp index b4112adc4..1b8b0ae9e 100644 --- a/doc/snippets/Tutorial_solve_reuse_decomposition.cpp +++ b/doc/snippets/Tutorial_solve_reuse_decomposition.cpp @@ -4,10 +4,10 @@ PartialLU luOfA(A); // compute LU decomposition of A Vector3f b; b << 3,3,4; Vector3f x; -luOfA.solve(b, &x); +x = luOfA.solve(b); cout << "The solution with right-hand side (3,3,4) is:" << endl; cout << x << endl; b << 1,1,1; -luOfA.solve(b, &x); +x = luOfA.solve(b); cout << "The solution with right-hand side (1,1,1) is:" << endl; cout << x << endl; diff --git a/doc/snippets/Tutorial_solve_singular.cpp b/doc/snippets/Tutorial_solve_singular.cpp index da94ad445..f5f2d2f6a 100644 --- a/doc/snippets/Tutorial_solve_singular.cpp +++ b/doc/snippets/Tutorial_solve_singular.cpp @@ -5,5 +5,5 @@ b << 3, 3, 4; cout << "Here is the matrix A:" << endl << A << endl; cout << "Here is the vector b:" << endl << b << endl; Vector3f x; -A.partialLu().solve(b, &x); +x = A.partialLu().solve(b); cout << "The solution is:" << endl << x << endl; From 2840ac7e948ecb3c7b19ba8f581f829a4a4e1fea Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 28 Oct 2009 18:19:29 -0400 Subject: [PATCH 23/34] big huge changes, so i dont remember everything. * renaming, e.g. LU ---> FullPivLU * split tests framework: more robust, e.g. dont generate empty tests if a number is skipped * make all remaining tests use that splitting, as needed. * Fix 4x4 inversion (see stable branch) * Transform::inverse() and geo_transform test : adapt to new inverse() API, it was also trying to instantiate inverse() for 3x4 matrices. * CMakeLists: more robust regexp to parse the version number * misc fixes in unit tests --- CMakeLists.txt | 8 +- Eigen/LU | 4 +- Eigen/QR | 4 +- Eigen/src/Cholesky/LDLT.h | 2 +- Eigen/src/Core/MatrixBase.h | 9 ++- Eigen/src/Core/ReturnByValue.h | 9 ++- Eigen/src/Core/util/ForwardDeclarations.h | 8 +- Eigen/src/Core/util/Macros.h | 2 +- Eigen/src/Geometry/Transform.h | 20 ++++- Eigen/src/LU/Determinant.h | 2 +- Eigen/src/LU/{LU.h => FullPivLU.h} | 62 +++++++-------- Eigen/src/LU/Inverse.h | 47 +++++++----- Eigen/src/LU/{PartialLU.h => PartialPivLU.h} | 75 ++++++++++-------- ...gHouseholderQR.h => ColPivHouseholderQR.h} | 62 +++++++-------- ...HouseholderQR.h => FullPivHouseholderQR.h} | 64 ++++++++-------- Eigen/src/QR/HouseholderQR.h | 4 +- Eigen/src/SVD/JacobiSVD.h | 4 +- Eigen/src/Sparse/SparseLU.h | 2 +- bench/btl/libs/eigen2/eigen2_interface.hh | 2 +- bench/sparse_lu.cpp | 2 +- cmake/EigenTesting.cmake | 42 +++++----- doc/C05_TutorialLinearAlgebra.dox | 28 +++---- doc/examples/Tutorial_PartialLU_solve.cpp | 2 +- .../{LU_image.cpp => FullPivLU_image.cpp} | 0 .../{LU_kernel.cpp => FullPivLU_kernel.cpp} | 0 .../{LU_solve.cpp => FullPivLU_solve.cpp} | 0 doc/snippets/PartialLU_solve.cpp | 2 +- doc/snippets/Tutorial_solve_multiple_rhs.cpp | 2 +- .../Tutorial_solve_reuse_decomposition.cpp | 2 +- doc/snippets/Tutorial_solve_singular.cpp | 2 +- .../{class_LU.cpp => class_FullPivLU.cpp} | 2 +- test/adjoint.cpp | 16 ++-- test/array.cpp | 34 ++++----- test/array_replicate.cpp | 12 +-- test/array_reverse.cpp | 21 ++--- test/bandmatrix.cpp | 2 +- test/basicstuff.cpp | 20 ++--- test/cholesky.cpp | 22 +++--- test/conservative_resize.cpp | 30 ++++---- test/cwiseop.cpp | 12 +-- test/determinant.cpp | 14 ++-- test/diagonalmatrices.cpp | 18 ++--- test/dynalloc.cpp | 10 +-- test/eigensolver_complex.cpp | 4 +- test/eigensolver_generic.cpp | 20 ++--- test/eigensolver_selfadjoint.cpp | 18 ++--- test/geo_alignedbox.cpp | 6 +- test/geo_eulerangles.cpp | 4 +- test/geo_homogeneous.cpp | 6 +- test/geo_hyperplane.cpp | 12 +-- test/geo_orthomethods.cpp | 20 ++--- test/geo_parametrizedline.cpp | 8 +- test/geo_quaternion.cpp | 13 ++-- test/geo_transformations.cpp | 30 ++++---- test/householder.cpp | 12 +-- test/inverse.cpp | 12 +-- test/jacobisvd.cpp | 30 ++++---- test/linearstructure.cpp | 16 ++-- test/lu.cpp | 54 ++++++------- test/main.h | 76 ++++++++++++++----- test/map.cpp | 20 ++--- test/miscmatrices.cpp | 10 +-- test/mixingtypes.cpp | 10 +-- test/nomalloc.cpp | 6 +- test/packetmath.cpp | 12 +-- test/product_extra.cpp | 6 +- test/product_large.cpp | 10 +-- test/product_notemporary.cpp | 4 +- test/product_selfadjoint.cpp | 16 ++-- test/product_small.cpp | 10 +-- test/product_symm.cpp | 8 +- test/product_syrk.cpp | 4 +- test/product_trmm.cpp | 4 +- test/product_trmv.cpp | 12 +-- test/product_trsm.cpp | 4 +- test/qr.cpp | 30 ++++---- test/qr_colpivoting.cpp | 38 +++++----- test/qr_fullpivoting.cpp | 34 ++++----- test/redux.cpp | 18 ++--- test/regression.cpp | 8 ++ test/resize.cpp | 6 +- test/sizeof.cpp | 16 ++-- test/smallvectors.cpp | 6 +- test/sparse_basic.cpp | 8 +- test/sparse_product.cpp | 8 +- test/sparse_solvers.cpp | 10 +-- test/sparse_vector.cpp | 6 +- test/stable_norm.cpp | 10 +-- test/stdvector.cpp | 34 ++++----- test/submatrices.cpp | 16 ++-- test/svd.cpp | 20 ++--- test/swap.cpp | 8 +- test/triangular.cpp | 14 ++-- test/umeyama.cpp | 16 ++-- test/visitor.cpp | 20 ++--- .../src/MatrixFunctions/MatrixExponential.h | 2 +- unsupported/test/BVH.cpp | 6 ++ unsupported/test/alignedvector3.cpp | 2 +- unsupported/test/matrixExponential.cpp | 28 +++---- 99 files changed, 816 insertions(+), 710 deletions(-) rename Eigen/src/LU/{LU.h => FullPivLU.h} (95%) rename Eigen/src/LU/{PartialLU.h => PartialPivLU.h} (88%) rename Eigen/src/QR/{ColPivotingHouseholderQR.h => ColPivHouseholderQR.h} (85%) rename Eigen/src/QR/{FullPivotingHouseholderQR.h => FullPivHouseholderQR.h} (85%) rename doc/snippets/{LU_image.cpp => FullPivLU_image.cpp} (100%) rename doc/snippets/{LU_kernel.cpp => FullPivLU_kernel.cpp} (100%) rename doc/snippets/{LU_solve.cpp => FullPivLU_solve.cpp} (100%) rename doc/snippets/{class_LU.cpp => class_FullPivLU.cpp} (95%) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed9e6d183..bf609be66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,12 +3,12 @@ project(Eigen) cmake_minimum_required(VERSION 2.6.2) # automatically parse the version number -file(READ "${CMAKE_SOURCE_DIR}/Eigen/src/Core/util/Macros.h" _eigen2_version_header LIMIT 5000 OFFSET 1000) -string(REGEX MATCH "define *EIGEN_WORLD_VERSION ([0-9]*)" _eigen2_world_version_match "${_eigen2_version_header}") +file(READ "${CMAKE_SOURCE_DIR}/Eigen/src/Core/util/Macros.h" _eigen2_version_header) +string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen2_world_version_match "${_eigen2_version_header}") set(EIGEN2_WORLD_VERSION "${CMAKE_MATCH_1}") -string(REGEX MATCH "define *EIGEN_MAJOR_VERSION ([0-9]*)" _eigen2_major_version_match "${_eigen2_version_header}") +string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen2_major_version_match "${_eigen2_version_header}") set(EIGEN2_MAJOR_VERSION "${CMAKE_MATCH_1}") -string(REGEX MATCH "define *EIGEN_MINOR_VERSION ([0-9]*)" _eigen2_minor_version_match "${_eigen2_version_header}") +string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen2_minor_version_match "${_eigen2_version_header}") set(EIGEN2_MINOR_VERSION "${CMAKE_MATCH_1}") set(EIGEN_VERSION_NUMBER ${EIGEN2_WORLD_VERSION}.${EIGEN2_MAJOR_VERSION}.${EIGEN2_MINOR_VERSION}) diff --git a/Eigen/LU b/Eigen/LU index c63463359..9c21b4407 100644 --- a/Eigen/LU +++ b/Eigen/LU @@ -18,8 +18,8 @@ namespace Eigen { * \endcode */ -#include "src/LU/LU.h" -#include "src/LU/PartialLU.h" +#include "src/LU/FullPivLU.h" +#include "src/LU/PartialPivLU.h" #include "src/LU/Determinant.h" #include "src/LU/Inverse.h" diff --git a/Eigen/QR b/Eigen/QR index f38e96c5e..eaa99793e 100644 --- a/Eigen/QR +++ b/Eigen/QR @@ -34,8 +34,8 @@ namespace Eigen { */ #include "src/QR/HouseholderQR.h" -#include "src/QR/FullPivotingHouseholderQR.h" -#include "src/QR/ColPivotingHouseholderQR.h" +#include "src/QR/FullPivHouseholderQR.h" +#include "src/QR/ColPivHouseholderQR.h" // declare all classes for a given matrix type #define EIGEN_QR_MODULE_INSTANTIATE_TYPE(MATRIXTYPE,PREFIX) \ diff --git a/Eigen/src/Cholesky/LDLT.h b/Eigen/src/Cholesky/LDLT.h index c8d92f3c0..f95e10935 100644 --- a/Eigen/src/Cholesky/LDLT.h +++ b/Eigen/src/Cholesky/LDLT.h @@ -88,7 +88,7 @@ template class LDLT /** \returns a vector of integers, whose size is the number of rows of the matrix being decomposed, * representing the P permutation i.e. the permutation of the rows. For its precise meaning, - * see the examples given in the documentation of class LU. + * see the examples given in the documentation of class FullPivLU. */ inline const IntColVectorType& permutationP() const { diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index a2b57f8ea..729349b6f 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -699,8 +699,9 @@ template class MatrixBase /////////// LU module /////////// - const LU lu() const; - const PartialLU partialLu() const; + const FullPivLU fullPivLu() const; + const PartialPivLU partialPivLu() const; + const PartialPivLU lu() const; const ei_inverse_impl inverse() const; template void computeInverseAndDetWithCheck( @@ -725,8 +726,8 @@ template class MatrixBase /////////// QR module /////////// const HouseholderQR householderQr() const; - const ColPivotingHouseholderQR colPivotingHouseholderQr() const; - const FullPivotingHouseholderQR fullPivotingHouseholderQr() const; + const ColPivHouseholderQR colPivHouseholderQr() const; + const FullPivHouseholderQR fullPivHouseholderQr() const; EigenvaluesReturnType eigenvalues() const; RealScalar operatorNorm() const; diff --git a/Eigen/src/Core/ReturnByValue.h b/Eigen/src/Core/ReturnByValue.h index 55652db48..87b057f86 100644 --- a/Eigen/src/Core/ReturnByValue.h +++ b/Eigen/src/Core/ReturnByValue.h @@ -34,7 +34,14 @@ struct ei_traits > : public ei_traits::ReturnMatrixType> { enum { - Flags = ei_traits::ReturnMatrixType>::Flags | EvalBeforeNestingBit + // FIXME had to remove the DirectAccessBit for usage like + // matrix.inverse().block(...) + // because the Block ctor with direct access + // wants to call coeffRef() to get an address, and that fails (infinite recursion) as ReturnByValue + // doesnt implement coeffRef(). The better fix is probably rather to make Block work directly + // on the nested type, right? + Flags = (ei_traits::ReturnMatrixType>::Flags + | EvalBeforeNestingBit) & ~DirectAccessBit }; }; diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index 01adca96d..86539a64e 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -114,12 +114,12 @@ template class VectorwiseOp; template class Replicate; template class Reverse; -template class LU; -template class PartialLU; +template class FullPivLU; +template class PartialPivLU; template struct ei_inverse_impl; template class HouseholderQR; -template class ColPivotingHouseholderQR; -template class FullPivotingHouseholderQR; +template class ColPivHouseholderQR; +template class FullPivHouseholderQR; template class SVD; template class JacobiSVD; template class LLT; diff --git a/Eigen/src/Core/util/Macros.h b/Eigen/src/Core/util/Macros.h index 706b30174..66b9d52f4 100644 --- a/Eigen/src/Core/util/Macros.h +++ b/Eigen/src/Core/util/Macros.h @@ -30,7 +30,7 @@ #define EIGEN_WORLD_VERSION 2 #define EIGEN_MAJOR_VERSION 90 -#define EIGEN_MINOR_VERSION 0 +#define EIGEN_MINOR_VERSION 1 #define EIGEN_VERSION_AT_LEAST(x,y,z) (EIGEN_WORLD_VERSION>x || (EIGEN_WORLD_VERSION>=x && \ (EIGEN_MAJOR_VERSION>y || (EIGEN_MAJOR_VERSION>=y && \ diff --git a/Eigen/src/Geometry/Transform.h b/Eigen/src/Geometry/Transform.h index d03fd52fd..70204f72b 100644 --- a/Eigen/src/Geometry/Transform.h +++ b/Eigen/src/Geometry/Transform.h @@ -876,6 +876,24 @@ Transform::fromPositionOrientationScale(const MatrixBase +struct ei_projective_transform_inverse +{ + static inline void run(const TransformType&, TransformType&) + {} +}; + +template +struct ei_projective_transform_inverse +{ + static inline void run(const TransformType& m, TransformType& res) + { + res.matrix() = m.matrix().inverse(); + } +}; + + /** \nonstableyet * * \returns the inverse transformation according to some given knowledge @@ -902,7 +920,7 @@ Transform::inverse(TransformTraits hint) const Transform res; if (hint == Projective) { - res.matrix() = m_matrix.inverse(); + ei_projective_transform_inverse::run(*this, res); } else { diff --git a/Eigen/src/LU/Determinant.h b/Eigen/src/LU/Determinant.h index b587065ed..8870d9f20 100644 --- a/Eigen/src/LU/Determinant.h +++ b/Eigen/src/LU/Determinant.h @@ -53,7 +53,7 @@ template::Scalar run(const Derived& m) { - return m.partialLu().determinant(); + return m.partialPivLu().determinant(); } }; diff --git a/Eigen/src/LU/LU.h b/Eigen/src/LU/FullPivLU.h similarity index 95% rename from Eigen/src/LU/LU.h rename to Eigen/src/LU/FullPivLU.h index 4792aaf07..8743dac92 100644 --- a/Eigen/src/LU/LU.h +++ b/Eigen/src/LU/FullPivLU.h @@ -31,7 +31,7 @@ template struct ei_lu_image_impl; /** \ingroup LU_Module * - * \class LU + * \class FullPivLU * * \brief LU decomposition of a matrix with complete pivoting, and related features * @@ -54,12 +54,12 @@ template struct ei_lu_image_impl; * permutationP(), permutationQ(). * * As an exemple, here is how the original matrix can be retrieved: - * \include class_LU.cpp - * Output: \verbinclude class_LU.out + * \include class_FullPivLU.cpp + * Output: \verbinclude class_FullPivLU.out * - * \sa MatrixBase::lu(), MatrixBase::determinant(), MatrixBase::inverse() + * \sa MatrixBase::fullPivLu(), MatrixBase::determinant(), MatrixBase::inverse() */ -template class LU +template class FullPivLU { public: @@ -81,14 +81,14 @@ template class LU * The default constructor is useful in cases in which the user intends to * perform decompositions via LU::compute(const MatrixType&). */ - LU(); + FullPivLU(); /** Constructor. * * \param matrix the matrix of which to compute the LU decomposition. * It is required to be nonzero. */ - LU(const MatrixType& matrix); + FullPivLU(const MatrixType& matrix); /** Computes the LU decomposition of the given matrix. * @@ -97,11 +97,11 @@ template class LU * * \returns a reference to *this */ - LU& compute(const MatrixType& matrix); + FullPivLU& compute(const MatrixType& matrix); /** \returns the LU decomposition matrix: the upper-triangular part is U, the * unit-lower-triangular part is L (at least for square matrices; in the non-square - * case, special care is needed, see the documentation of class LU). + * case, special care is needed, see the documentation of class FullPivLU). * * \sa matrixL(), matrixU() */ @@ -131,7 +131,7 @@ template class LU /** \returns a vector of integers, whose size is the number of rows of the matrix being decomposed, * representing the P permutation i.e. the permutation of the rows. For its precise meaning, - * see the examples given in the documentation of class LU. + * see the examples given in the documentation of class FullPivLU. * * \sa permutationQ() */ @@ -143,7 +143,7 @@ template class LU /** \returns a vector of integers, whose size is the number of columns of the matrix being * decomposed, representing the Q permutation i.e. the permutation of the columns. - * For its precise meaning, see the examples given in the documentation of class LU. + * For its precise meaning, see the examples given in the documentation of class FullPivLU. * * \sa permutationP() */ @@ -162,8 +162,8 @@ template class LU * For that, it uses the threshold value that you can control by calling * setThreshold(const RealScalar&). * - * Example: \include LU_kernel.cpp - * Output: \verbinclude LU_kernel.out + * Example: \include FullPivLU_kernel.cpp + * Output: \verbinclude FullPivLU_kernel.out * * \sa image() */ @@ -187,8 +187,8 @@ template class LU * For that, it uses the threshold value that you can control by calling * setThreshold(const RealScalar&). * - * Example: \include LU_image.cpp - * Output: \verbinclude LU_image.out + * Example: \include FullPivLU_image.cpp + * Output: \verbinclude FullPivLU_image.out * * \sa kernel() */ @@ -214,8 +214,8 @@ template class LU * \note_about_arbitrary_choice_of_solution * \note_about_using_kernel_to_study_multiple_solutions * - * Example: \include LU_solve.cpp - * Output: \verbinclude LU_solve.out + * Example: \include FullPivLU_solve.cpp + * Output: \verbinclude FullPivLU_solve.out * * \sa TriangularView::solve(), kernel(), inverse() */ @@ -260,7 +260,7 @@ template class LU * * If you want to come back to the default behavior, call setThreshold(Default_t) */ - LU& setThreshold(const RealScalar& threshold) + FullPivLU& setThreshold(const RealScalar& threshold) { m_usePrescribedThreshold = true; m_prescribedThreshold = threshold; @@ -274,7 +274,7 @@ template class LU * * See the documentation of setThreshold(const RealScalar&). */ - LU& setThreshold(Default_t) + FullPivLU& setThreshold(Default_t) { m_usePrescribedThreshold = false; } @@ -383,20 +383,20 @@ template class LU }; template -LU::LU() +FullPivLU::FullPivLU() : m_isInitialized(false), m_usePrescribedThreshold(false) { } template -LU::LU(const MatrixType& matrix) +FullPivLU::FullPivLU(const MatrixType& matrix) : m_isInitialized(false), m_usePrescribedThreshold(false) { compute(matrix); } template -LU& LU::compute(const MatrixType& matrix) +FullPivLU& FullPivLU::compute(const MatrixType& matrix) { m_isInitialized = true; m_lu = matrix; @@ -483,7 +483,7 @@ LU& LU::compute(const MatrixType& matrix) } template -typename ei_traits::Scalar LU::determinant() const +typename ei_traits::Scalar FullPivLU::determinant() const { ei_assert(m_isInitialized && "LU is not initialized."); ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the determinant of a non-square matrix!"); @@ -511,7 +511,7 @@ struct ei_traits > template struct ei_lu_kernel_impl : public ReturnByValue > { - typedef LU LUType; + typedef FullPivLU LUType; typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; const LUType& m_lu; @@ -615,7 +615,7 @@ struct ei_traits > template struct ei_lu_image_impl : public ReturnByValue > { - typedef LU LUType; + typedef FullPivLU LUType; typedef typename MatrixType::RealScalar RealScalar; const LUType& m_lu; int m_rank, m_cols; @@ -670,7 +670,7 @@ template struct ei_lu_solve_impl : public ReturnByValue > { typedef typename ei_cleantype::type RhsNested; - typedef LU LUType; + typedef FullPivLU LUType; const LUType& m_lu; const typename Rhs::Nested m_rhs; @@ -739,15 +739,15 @@ struct ei_lu_solve_impl : public ReturnByValue /** \lu_module * - * \return the LU decomposition of \c *this. + * \return the full-pivoting LU decomposition of \c *this. * - * \sa class LU + * \sa class FullPivLU */ template -inline const LU::PlainMatrixType> -MatrixBase::lu() const +inline const FullPivLU::PlainMatrixType> +MatrixBase::fullPivLu() const { - return LU(eval()); + return FullPivLU(eval()); } #endif // EIGEN_LU_H diff --git a/Eigen/src/LU/Inverse.h b/Eigen/src/LU/Inverse.h index a168d58cd..306b5f60a 100644 --- a/Eigen/src/LU/Inverse.h +++ b/Eigen/src/LU/Inverse.h @@ -34,7 +34,7 @@ struct ei_compute_inverse { static inline void run(const MatrixType& matrix, ResultType& result) { - result = matrix.partialLu().inverse(); + result = matrix.partialPivLu().inverse(); } }; @@ -232,22 +232,31 @@ struct ei_compute_inverse typename MatrixType::PlainMatrixType matrix(_matrix); // let's extract from the 2 first colums a 2x2 block whose determinant is as big as possible. - int good_row0=0, good_row1=1; - RealScalar good_absdet(-1); - // this double for loop shouldn't be too costly: only 6 iterations - for(int row0=0; row0<4; ++row0) { - for(int row1=row0+1; row1<4; ++row1) - { - RealScalar absdet = ei_abs(matrix.coeff(row0,0)*matrix.coeff(row1,1) - - matrix.coeff(row0,1)*matrix.coeff(row1,0)); - if(absdet > good_absdet) - { - good_absdet = absdet; - good_row0 = row0; - good_row1 = row1; - } - } - } + int good_row0, good_row1, good_i; + Matrix absdet; + + // any 2x2 block with determinant above this threshold will be considered good enough + RealScalar d = (matrix.col(0).squaredNorm()+matrix.col(1).squaredNorm()) * RealScalar(1e-2); + #define ei_inv_size4_helper_macro(i,row0,row1) \ + absdet[i] = ei_abs(matrix.coeff(row0,0)*matrix.coeff(row1,1) \ + - matrix.coeff(row0,1)*matrix.coeff(row1,0)); \ + if(absdet[i] > d) { good_row0=row0; good_row1=row1; goto good; } + ei_inv_size4_helper_macro(0,0,1) + ei_inv_size4_helper_macro(1,0,2) + ei_inv_size4_helper_macro(2,0,3) + ei_inv_size4_helper_macro(3,1,2) + ei_inv_size4_helper_macro(4,1,3) + ei_inv_size4_helper_macro(5,2,3) + + // no 2x2 block has determinant bigger than the threshold. So just take the one that + // has the biggest determinant + absdet.maxCoeff(&good_i); + good_row0 = good_i <= 2 ? 0 : good_i <= 4 ? 1 : 2; + good_row1 = good_i <= 2 ? good_i+1 : good_i <= 4 ? good_i-1 : 3; + + // now good_row0 and good_row1 are correctly set + good: + // do row permutations to move this 2x2 block to the top matrix.row(0).swap(matrix.row(good_row0)); matrix.row(1).swap(matrix.row(good_row1)); @@ -318,12 +327,12 @@ struct ei_inverse_impl : public ReturnByValue > * \returns the matrix inverse of this matrix. * * For small fixed sizes up to 4x4, this method uses ad-hoc methods (cofactors up to 3x3, Euler's trick for 4x4). - * In the general case, this method uses class PartialLU. + * In the general case, this method uses class PartialPivLU. * * \note This matrix must be invertible, otherwise the result is undefined. If you need an * invertibility check, do the following: * \li for fixed sizes up to 4x4, use computeInverseAndDetWithCheck(). - * \li for the general case, use class LU. + * \li for the general case, use class FullPivLU. * * Example: \include MatrixBase_inverse.cpp * Output: \verbinclude MatrixBase_inverse.out diff --git a/Eigen/src/LU/PartialLU.h b/Eigen/src/LU/PartialPivLU.h similarity index 88% rename from Eigen/src/LU/PartialLU.h rename to Eigen/src/LU/PartialPivLU.h index e8d21e5ad..647ada38f 100644 --- a/Eigen/src/LU/PartialLU.h +++ b/Eigen/src/LU/PartialPivLU.h @@ -30,7 +30,7 @@ template struct ei_partiallu_solve_impl; /** \ingroup LU_Module * - * \class PartialLU + * \class PartialPivLU * * \brief LU decomposition of a matrix with partial pivoting, and related features * @@ -46,10 +46,10 @@ template struct ei_partiallu_solve_impl; * matrix is invertible: it is your task to check that you only use this decomposition on invertible matrices. * * The guaranteed safe alternative, working for all matrices, is the full pivoting LU decomposition, provided - * by class LU. + * by class FullPivLU. * * This is \b not a rank-revealing LU decomposition. Many features are intentionally absent from this class, - * such as rank computation. If you need these features, use class LU. + * such as rank computation. If you need these features, use class FullPivLU. * * This LU decomposition is suitable to invert invertible matrices. It is what MatrixBase::inverse() uses * in the general case. @@ -57,9 +57,9 @@ template struct ei_partiallu_solve_impl; * * The data of the LU decomposition can be directly accessed through the methods matrixLU(), permutationP(). * - * \sa MatrixBase::partialLu(), MatrixBase::determinant(), MatrixBase::inverse(), MatrixBase::computeInverse(), class LU + * \sa MatrixBase::partialPivLu(), MatrixBase::determinant(), MatrixBase::inverse(), MatrixBase::computeInverse(), class FullPivLU */ -template class PartialLU +template class PartialPivLU { public: @@ -79,40 +79,40 @@ template class PartialLU * \brief Default Constructor. * * The default constructor is useful in cases in which the user intends to - * perform decompositions via PartialLU::compute(const MatrixType&). + * perform decompositions via PartialPivLU::compute(const MatrixType&). */ - PartialLU(); + PartialPivLU(); /** Constructor. * * \param matrix the matrix of which to compute the LU decomposition. * * \warning The matrix should have full rank (e.g. if it's square, it should be invertible). - * If you need to deal with non-full rank, use class LU instead. + * If you need to deal with non-full rank, use class FullPivLU instead. */ - PartialLU(const MatrixType& matrix); + PartialPivLU(const MatrixType& matrix); - PartialLU& compute(const MatrixType& matrix); + PartialPivLU& compute(const MatrixType& matrix); /** \returns the LU decomposition matrix: the upper-triangular part is U, the * unit-lower-triangular part is L (at least for square matrices; in the non-square - * case, special care is needed, see the documentation of class LU). + * case, special care is needed, see the documentation of class FullPivLU). * * \sa matrixL(), matrixU() */ inline const MatrixType& matrixLU() const { - ei_assert(m_isInitialized && "PartialLU is not initialized."); + ei_assert(m_isInitialized && "PartialPivLU is not initialized."); return m_lu; } /** \returns a vector of integers, whose size is the number of rows of the matrix being decomposed, * representing the P permutation i.e. the permutation of the rows. For its precise meaning, - * see the examples given in the documentation of class LU. + * see the examples given in the documentation of class FullPivLU. */ inline const IntColVectorType& permutationP() const { - ei_assert(m_isInitialized && "PartialLU is not initialized."); + ei_assert(m_isInitialized && "PartialPivLU is not initialized."); return m_p; } @@ -125,10 +125,10 @@ template class PartialLU * * \returns the solution. * - * Example: \include PartialLU_solve.cpp - * Output: \verbinclude PartialLU_solve.out + * Example: \include PartialPivLU_solve.cpp + * Output: \verbinclude PartialPivLU_solve.out * - * Since this PartialLU class assumes anyway that the matrix A is invertible, the solution + * Since this PartialPivLU class assumes anyway that the matrix A is invertible, the solution * theoretically exists and is unique regardless of b. * * \note_about_checking_solutions @@ -146,7 +146,7 @@ template class PartialLU /** \returns the inverse of the matrix of which *this is the LU decomposition. * * \warning The matrix being decomposed here is assumed to be invertible. If you need to check for - * invertibility, use class LU instead. + * invertibility, use class FullPivLU instead. * * \sa MatrixBase::inverse(), LU::inverse() */ @@ -180,7 +180,7 @@ template class PartialLU }; template -PartialLU::PartialLU() +PartialPivLU::PartialPivLU() : m_lu(), m_p(), m_det_p(0), @@ -189,7 +189,7 @@ PartialLU::PartialLU() } template -PartialLU::PartialLU(const MatrixType& matrix) +PartialPivLU::PartialPivLU(const MatrixType& matrix) : m_lu(), m_p(), m_det_p(0), @@ -378,12 +378,12 @@ void ei_partial_lu_inplace(MatrixType& lu, IntVector& row_transpositions, int& n } template -PartialLU& PartialLU::compute(const MatrixType& matrix) +PartialPivLU& PartialPivLU::compute(const MatrixType& matrix) { m_lu = matrix; m_p.resize(matrix.rows()); - ei_assert(matrix.rows() == matrix.cols() && "PartialLU is only for square (and moreover invertible) matrices"); + ei_assert(matrix.rows() == matrix.cols() && "PartialPivLU is only for square (and moreover invertible) matrices"); const int size = matrix.rows(); IntColVectorType rows_transpositions(size); @@ -401,9 +401,9 @@ PartialLU& PartialLU::compute(const MatrixType& matrix) } template -typename ei_traits::Scalar PartialLU::determinant() const +typename ei_traits::Scalar PartialPivLU::determinant() const { - ei_assert(m_isInitialized && "PartialLU is not initialized."); + ei_assert(m_isInitialized && "PartialPivLU is not initialized."); return Scalar(m_det_p) * m_lu.diagonal().prod(); } @@ -424,7 +424,7 @@ template struct ei_partiallu_solve_impl : public ReturnByValue > { typedef typename ei_cleantype::type RhsNested; - typedef PartialLU LUType; + typedef PartialPivLU LUType; const LUType& m_lu; const typename Rhs::Nested m_rhs; @@ -464,15 +464,30 @@ struct ei_partiallu_solve_impl : public ReturnByValue -inline const PartialLU::PlainMatrixType> -MatrixBase::partialLu() const +inline const PartialPivLU::PlainMatrixType> +MatrixBase::partialPivLu() const { - return PartialLU(eval()); + return PartialPivLU(eval()); +} + +/** \lu_module + * + * Synonym of partialPivLu(). + * + * \return the partial-pivoting LU decomposition of \c *this. + * + * \sa class PartialPivLU + */ +template +inline const PartialPivLU::PlainMatrixType> +MatrixBase::lu() const +{ + return PartialPivLU(eval()); } #endif // EIGEN_PARTIALLU_H diff --git a/Eigen/src/QR/ColPivotingHouseholderQR.h b/Eigen/src/QR/ColPivHouseholderQR.h similarity index 85% rename from Eigen/src/QR/ColPivotingHouseholderQR.h rename to Eigen/src/QR/ColPivHouseholderQR.h index b141da0aa..05287ff3c 100644 --- a/Eigen/src/QR/ColPivotingHouseholderQR.h +++ b/Eigen/src/QR/ColPivHouseholderQR.h @@ -29,7 +29,7 @@ /** \ingroup QR_Module * \nonstableyet * - * \class ColPivotingHouseholderQR + * \class ColPivHouseholderQR * * \brief Householder rank-revealing QR decomposition of a matrix with column-pivoting * @@ -38,11 +38,11 @@ * This class performs a rank-revealing QR decomposition using Householder transformations. * * This decomposition performs column pivoting in order to be rank-revealing and improve - * numerical stability. It is slower than HouseholderQR, and faster than FullPivotingHouseholderQR. + * numerical stability. It is slower than HouseholderQR, and faster than FullPivHouseholderQR. * - * \sa MatrixBase::colPivotingHouseholderQr() + * \sa MatrixBase::colPivHouseholderQr() */ -template class ColPivotingHouseholderQR +template class ColPivHouseholderQR { public: @@ -68,11 +68,11 @@ template class ColPivotingHouseholderQR * \brief Default Constructor. * * The default constructor is useful in cases in which the user intends to - * perform decompositions via ColPivotingHouseholderQR::compute(const MatrixType&). + * perform decompositions via ColPivHouseholderQR::compute(const MatrixType&). */ - ColPivotingHouseholderQR() : m_qr(), m_hCoeffs(), m_isInitialized(false) {} + ColPivHouseholderQR() : m_qr(), m_hCoeffs(), m_isInitialized(false) {} - ColPivotingHouseholderQR(const MatrixType& matrix) + ColPivHouseholderQR(const MatrixType& matrix) : m_qr(matrix.rows(), matrix.cols()), m_hCoeffs(std::min(matrix.rows(),matrix.cols())), m_isInitialized(false) @@ -94,8 +94,8 @@ template class ColPivotingHouseholderQR * \note The case where b is a matrix is not yet implemented. Also, this * code is space inefficient. * - * Example: \include ColPivotingHouseholderQR_solve.cpp - * Output: \verbinclude ColPivotingHouseholderQR_solve.out + * Example: \include ColPivHouseholderQR_solve.cpp + * Output: \verbinclude ColPivHouseholderQR_solve.out */ template bool solve(const MatrixBase& b, ResultType *result) const; @@ -106,15 +106,15 @@ template class ColPivotingHouseholderQR */ const MatrixType& matrixQR() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); return m_qr; } - ColPivotingHouseholderQR& compute(const MatrixType& matrix); + ColPivHouseholderQR& compute(const MatrixType& matrix); const IntRowVectorType& colsPermutation() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); return m_cols_permutation; } @@ -154,7 +154,7 @@ template class ColPivotingHouseholderQR */ inline int rank() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); return m_rank; } @@ -165,7 +165,7 @@ template class ColPivotingHouseholderQR */ inline int dimensionOfKernel() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); return m_qr.cols() - m_rank; } @@ -177,7 +177,7 @@ template class ColPivotingHouseholderQR */ inline bool isInjective() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); return m_rank == m_qr.cols(); } @@ -189,7 +189,7 @@ template class ColPivotingHouseholderQR */ inline bool isSurjective() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); return m_rank == m_qr.rows(); } @@ -200,7 +200,7 @@ template class ColPivotingHouseholderQR */ inline bool isInvertible() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); return isInjective() && isSurjective(); } @@ -215,7 +215,7 @@ template class ColPivotingHouseholderQR */ inline void computeInverse(MatrixType *result) const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); ei_assert(m_qr.rows() == m_qr.cols() && "You can't take the inverse of a non-square matrix!"); solve(MatrixType::Identity(m_qr.rows(), m_qr.cols()), result); } @@ -247,23 +247,23 @@ template class ColPivotingHouseholderQR #ifndef EIGEN_HIDE_HEAVY_CODE template -typename MatrixType::RealScalar ColPivotingHouseholderQR::absDeterminant() const +typename MatrixType::RealScalar ColPivHouseholderQR::absDeterminant() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); ei_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!"); return ei_abs(m_qr.diagonal().prod()); } template -typename MatrixType::RealScalar ColPivotingHouseholderQR::logAbsDeterminant() const +typename MatrixType::RealScalar ColPivHouseholderQR::logAbsDeterminant() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); ei_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!"); return m_qr.diagonal().cwise().abs().cwise().log().sum(); } template -ColPivotingHouseholderQR& ColPivotingHouseholderQR::compute(const MatrixType& matrix) +ColPivHouseholderQR& ColPivHouseholderQR::compute(const MatrixType& matrix) { int rows = matrix.rows(); int cols = matrix.cols(); @@ -333,12 +333,12 @@ ColPivotingHouseholderQR& ColPivotingHouseholderQR::comp template template -bool ColPivotingHouseholderQR::solve( +bool ColPivHouseholderQR::solve( const MatrixBase& b, ResultType *result ) const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); result->resize(m_qr.cols(), b.cols()); if(m_rank==0) { @@ -378,9 +378,9 @@ bool ColPivotingHouseholderQR::solve( /** \returns the matrix Q as a sequence of householder transformations */ template -typename ColPivotingHouseholderQR::HouseholderSequenceType ColPivotingHouseholderQR::matrixQ() const +typename ColPivHouseholderQR::HouseholderSequenceType ColPivHouseholderQR::matrixQ() const { - ei_assert(m_isInitialized && "ColPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); return HouseholderSequenceType(m_qr, m_hCoeffs.conjugate()); } @@ -388,13 +388,13 @@ typename ColPivotingHouseholderQR::HouseholderSequenceType ColPivoti /** \return the column-pivoting Householder QR decomposition of \c *this. * - * \sa class ColPivotingHouseholderQR + * \sa class ColPivHouseholderQR */ template -const ColPivotingHouseholderQR::PlainMatrixType> -MatrixBase::colPivotingHouseholderQr() const +const ColPivHouseholderQR::PlainMatrixType> +MatrixBase::colPivHouseholderQr() const { - return ColPivotingHouseholderQR(eval()); + return ColPivHouseholderQR(eval()); } diff --git a/Eigen/src/QR/FullPivotingHouseholderQR.h b/Eigen/src/QR/FullPivHouseholderQR.h similarity index 85% rename from Eigen/src/QR/FullPivotingHouseholderQR.h rename to Eigen/src/QR/FullPivHouseholderQR.h index 9fee77803..07ec343a5 100644 --- a/Eigen/src/QR/FullPivotingHouseholderQR.h +++ b/Eigen/src/QR/FullPivHouseholderQR.h @@ -29,7 +29,7 @@ /** \ingroup QR_Module * \nonstableyet * - * \class FullPivotingHouseholderQR + * \class FullPivHouseholderQR * * \brief Householder rank-revealing QR decomposition of a matrix with full pivoting * @@ -38,11 +38,11 @@ * This class performs a rank-revealing QR decomposition using Householder transformations. * * This decomposition performs a very prudent full pivoting in order to be rank-revealing and achieve optimal - * numerical stability. The trade-off is that it is slower than HouseholderQR and ColPivotingHouseholderQR. + * numerical stability. The trade-off is that it is slower than HouseholderQR and ColPivHouseholderQR. * - * \sa MatrixBase::fullPivotingHouseholderQr() + * \sa MatrixBase::fullPivHouseholderQr() */ -template class FullPivotingHouseholderQR +template class FullPivHouseholderQR { public: @@ -65,11 +65,11 @@ template class FullPivotingHouseholderQR /** \brief Default Constructor. * * The default constructor is useful in cases in which the user intends to - * perform decompositions via FullPivotingHouseholderQR::compute(const MatrixType&). + * perform decompositions via FullPivHouseholderQR::compute(const MatrixType&). */ - FullPivotingHouseholderQR() : m_isInitialized(false) {} + FullPivHouseholderQR() : m_isInitialized(false) {} - FullPivotingHouseholderQR(const MatrixType& matrix) + FullPivHouseholderQR(const MatrixType& matrix) : m_isInitialized(false) { compute(matrix); @@ -89,8 +89,8 @@ template class FullPivotingHouseholderQR * \note The case where b is a matrix is not yet implemented. Also, this * code is space inefficient. * - * Example: \include FullPivotingHouseholderQR_solve.cpp - * Output: \verbinclude FullPivotingHouseholderQR_solve.out + * Example: \include FullPivHouseholderQR_solve.cpp + * Output: \verbinclude FullPivHouseholderQR_solve.out */ template bool solve(const MatrixBase& b, ResultType *result) const; @@ -101,21 +101,21 @@ template class FullPivotingHouseholderQR */ const MatrixType& matrixQR() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); return m_qr; } - FullPivotingHouseholderQR& compute(const MatrixType& matrix); + FullPivHouseholderQR& compute(const MatrixType& matrix); const IntRowVectorType& colsPermutation() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); return m_cols_permutation; } const IntColVectorType& rowsTranspositions() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); return m_rows_transpositions; } @@ -155,7 +155,7 @@ template class FullPivotingHouseholderQR */ inline int rank() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); return m_rank; } @@ -166,7 +166,7 @@ template class FullPivotingHouseholderQR */ inline int dimensionOfKernel() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); return m_qr.cols() - m_rank; } @@ -178,7 +178,7 @@ template class FullPivotingHouseholderQR */ inline bool isInjective() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); return m_rank == m_qr.cols(); } @@ -190,7 +190,7 @@ template class FullPivotingHouseholderQR */ inline bool isSurjective() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); return m_rank == m_qr.rows(); } @@ -201,7 +201,7 @@ template class FullPivotingHouseholderQR */ inline bool isInvertible() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); return isInjective() && isSurjective(); } @@ -216,7 +216,7 @@ template class FullPivotingHouseholderQR */ inline void computeInverse(MatrixType *result) const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); ei_assert(m_qr.rows() == m_qr.cols() && "You can't take the inverse of a non-square matrix!"); solve(MatrixType::Identity(m_qr.rows(), m_qr.cols()), result); } @@ -249,23 +249,23 @@ template class FullPivotingHouseholderQR #ifndef EIGEN_HIDE_HEAVY_CODE template -typename MatrixType::RealScalar FullPivotingHouseholderQR::absDeterminant() const +typename MatrixType::RealScalar FullPivHouseholderQR::absDeterminant() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); ei_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!"); return ei_abs(m_qr.diagonal().prod()); } template -typename MatrixType::RealScalar FullPivotingHouseholderQR::logAbsDeterminant() const +typename MatrixType::RealScalar FullPivHouseholderQR::logAbsDeterminant() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); ei_assert(m_qr.rows() == m_qr.cols() && "You can't take the determinant of a non-square matrix!"); return m_qr.diagonal().cwise().abs().cwise().log().sum(); } template -FullPivotingHouseholderQR& FullPivotingHouseholderQR::compute(const MatrixType& matrix) +FullPivHouseholderQR& FullPivHouseholderQR::compute(const MatrixType& matrix) { int rows = matrix.rows(); int cols = matrix.cols(); @@ -342,12 +342,12 @@ FullPivotingHouseholderQR& FullPivotingHouseholderQR::co template template -bool FullPivotingHouseholderQR::solve( +bool FullPivHouseholderQR::solve( const MatrixBase& b, ResultType *result ) const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); result->resize(m_qr.cols(), b.cols()); if(m_rank==0) { @@ -393,9 +393,9 @@ bool FullPivotingHouseholderQR::solve( /** \returns the matrix Q */ template -typename FullPivotingHouseholderQR::MatrixQType FullPivotingHouseholderQR::matrixQ() const +typename FullPivHouseholderQR::MatrixQType FullPivHouseholderQR::matrixQ() const { - ei_assert(m_isInitialized && "FullPivotingHouseholderQR is not initialized."); + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); // compute the product H'_0 H'_1 ... H'_n-1, // where H_k is the k-th Householder transformation I - h_k v_k v_k' // and v_k is the k-th Householder vector [1,m_qr(k+1,k), m_qr(k+2,k), ...] @@ -417,13 +417,13 @@ typename FullPivotingHouseholderQR::MatrixQType FullPivotingHousehol /** \return the full-pivoting Householder QR decomposition of \c *this. * - * \sa class FullPivotingHouseholderQR + * \sa class FullPivHouseholderQR */ template -const FullPivotingHouseholderQR::PlainMatrixType> -MatrixBase::fullPivotingHouseholderQr() const +const FullPivHouseholderQR::PlainMatrixType> +MatrixBase::fullPivHouseholderQr() const { - return FullPivotingHouseholderQR(eval()); + return FullPivHouseholderQR(eval()); } #endif // EIGEN_FULLPIVOTINGHOUSEHOLDERQR_H diff --git a/Eigen/src/QR/HouseholderQR.h b/Eigen/src/QR/HouseholderQR.h index 01cd2adb5..a32aa4eaf 100644 --- a/Eigen/src/QR/HouseholderQR.h +++ b/Eigen/src/QR/HouseholderQR.h @@ -39,10 +39,10 @@ * stored in a compact way compatible with LAPACK. * * Note that no pivoting is performed. This is \b not a rank-revealing decomposition. - * If you want that feature, use FullPivotingHouseholderQR or ColPivotingHouseholderQR instead. + * If you want that feature, use FullPivHouseholderQR or ColPivHouseholderQR instead. * * This Householder QR decomposition is faster, but less numerically stable and less feature-full than - * FullPivotingHouseholderQR or ColPivotingHouseholderQR. + * FullPivHouseholderQR or ColPivHouseholderQR. * * \sa MatrixBase::householderQr() */ diff --git a/Eigen/src/SVD/JacobiSVD.h b/Eigen/src/SVD/JacobiSVD.h index 6a0597893..927ef6591 100644 --- a/Eigen/src/SVD/JacobiSVD.h +++ b/Eigen/src/SVD/JacobiSVD.h @@ -233,7 +233,7 @@ struct ei_svd_precondition_if_more_rows_than_cols int diagSize = cols; if(rows > cols) { - FullPivotingHouseholderQR qr(matrix); + FullPivHouseholderQR qr(matrix); work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView(); if(ComputeU) svd.m_matrixU = qr.matrixQ(); if(ComputeV) @@ -278,7 +278,7 @@ struct ei_svd_precondition_if_more_cols_than_rows typedef Matrix TransposeTypeWithSameStorageOrder; - FullPivotingHouseholderQR qr(matrix.adjoint()); + FullPivHouseholderQR qr(matrix.adjoint()); work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView().adjoint(); if(ComputeV) svd.m_matrixV = qr.matrixQ(); if(ComputeU) diff --git a/Eigen/src/Sparse/SparseLU.h b/Eigen/src/Sparse/SparseLU.h index e7191b7ab..3f8d0f8db 100644 --- a/Eigen/src/Sparse/SparseLU.h +++ b/Eigen/src/Sparse/SparseLU.h @@ -39,7 +39,7 @@ enum { * * \param MatrixType the type of the matrix of which we are computing the LU factorization * - * \sa class LU, class SparseLLT + * \sa class FullPivLU, class SparseLLT */ template class SparseLU diff --git a/bench/btl/libs/eigen2/eigen2_interface.hh b/bench/btl/libs/eigen2/eigen2_interface.hh index 1a5f89834..f93ccad58 100644 --- a/bench/btl/libs/eigen2/eigen2_interface.hh +++ b/bench/btl/libs/eigen2/eigen2_interface.hh @@ -218,7 +218,7 @@ public : } static inline void partial_lu_decomp(const gene_matrix & X, gene_matrix & C, int N){ - C = X.partialLu().matrixLU(); + C = X.partialPivLu().matrixLU(); } static inline void tridiagonalization(const gene_matrix & X, gene_matrix & C, int N){ diff --git a/bench/sparse_lu.cpp b/bench/sparse_lu.cpp index bb73d481d..5c7500182 100644 --- a/bench/sparse_lu.cpp +++ b/bench/sparse_lu.cpp @@ -98,7 +98,7 @@ int main(int argc, char *argv[]) BenchTimer timer; timer.start(); - LU lu(m1); + FullPivLU lu(m1); timer.stop(); std::cout << "Eigen/dense:\t" << timer.value() << endl; diff --git a/cmake/EigenTesting.cmake b/cmake/EigenTesting.cmake index 22aea3d0d..bb272a8cc 100644 --- a/cmake/EigenTesting.cmake +++ b/cmake/EigenTesting.cmake @@ -70,6 +70,10 @@ macro(ei_add_test_internal testname testname_with_suffix) # for the debug target, add full debug options if(CMAKE_COMPILER_IS_GNUCXX) + # disable debug symbols: i get 2 GB of them! + # the user can still use the debug targets as needed. + # for MSVC, the equivalent (release mode) does the same. + ei_add_target_property(${targetname} COMPILE_FLAGS "-g0") # O0 is in principle redundant here, but doesn't hurt ei_add_target_property(${debug_targetname} COMPILE_FLAGS "-O0 -g3") elseif(MSVC) @@ -139,7 +143,7 @@ endmacro(ei_add_test_internal) # B. Multi-part behavior # # If the source file matches the regexp -# CALL_SUBTEST[0-9]+|EIGEN_TEST_PART_[0-9]+ +# CALL_SUBTEST(-9]+|EIGEN_TEST_PART_[0-9]+ # then it is interpreted as a multi-part test. The behavior then depends on the # CMake option EIGEN_SPLIT_LARGE_TESTS, which is ON by default. # @@ -162,33 +166,31 @@ endmacro(ei_add_test_internal) macro(ei_add_test testname) file(READ "${testname}.cpp" test_source) set(parts 0) - string(REGEX MATCHALL "CALL_SUBTEST[0-9]+|EIGEN_TEST_PART_[0-9]+" occurences "${test_source}") - foreach(occurence ${occurences}) - string(REGEX MATCH "([0-9]+)" _number_in_occurence "${occurence}") - set(number ${CMAKE_MATCH_1}) - if(${number} GREATER ${parts}) - set(parts ${number}) - endif(${number} GREATER ${parts}) - endforeach(occurence) - if(EIGEN_SPLIT_LARGE_TESTS AND (parts GREATER 0)) + string(REGEX MATCHALL "CALL_SUBTEST_[0-9]+|EIGEN_TEST_PART_[0-9]+" + occurences "${test_source}") + string(REGEX REPLACE "CALL_SUBTEST_|EIGEN_TEST_PART_" "" suffixes "${occurences}") + list(REMOVE_DUPLICATES suffixes) + if(EIGEN_SPLIT_LARGE_TESTS AND suffixes) add_custom_target(test_${testname}) if(NOT MSVC_IDE) add_custom_target(debug_${testname}) endif(NOT MSVC_IDE) - foreach(part RANGE 1 ${parts}) - ei_add_test_internal(${testname} ${testname}_${part} "${ARGV1} -DEIGEN_TEST_PART_${part}" "${ARGV2}") - add_dependencies(test_${testname} test_${testname}_${part}) + foreach(suffix ${suffixes}) + ei_add_test_internal(${testname} ${testname}_${suffix} + "${ARGV1} -DEIGEN_TEST_PART_${suffix}=1" "${ARGV2}") + add_dependencies(test_${testname} test_${testname}_${suffix}) if(NOT MSVC_IDE) - add_dependencies(debug_${testname} debug_${testname}_${part}) + add_dependencies(debug_${testname} debug_${testname}_${suffix}) endif(NOT MSVC_IDE) - endforeach(part) - else(EIGEN_SPLIT_LARGE_TESTS AND (parts GREATER 0)) + endforeach(suffix) + else(EIGEN_SPLIT_LARGE_TESTS AND suffixes) set(symbols_to_enable_all_parts "") - foreach(part RANGE 1 ${parts}) - set(symbols_to_enable_all_parts "${symbols_to_enable_all_parts} -DEIGEN_TEST_PART_${part}") - endforeach(part) + foreach(suffix ${suffixes}) + set(symbols_to_enable_all_parts + "${symbols_to_enable_all_parts} -DEIGEN_TEST_PART_${suffix}=1") + endforeach(suffix) ei_add_test_internal(${testname} ${testname} "${ARGV1} ${symbols_to_enable_all_parts}" "${ARGV2}") - endif(EIGEN_SPLIT_LARGE_TESTS AND (parts GREATER 0)) + endif(EIGEN_SPLIT_LARGE_TESTS AND suffixes) endmacro(ei_add_test) # print a summary of the different options diff --git a/doc/C05_TutorialLinearAlgebra.dox b/doc/C05_TutorialLinearAlgebra.dox index fbf809d58..c50e9c6bc 100644 --- a/doc/C05_TutorialLinearAlgebra.dox +++ b/doc/C05_TutorialLinearAlgebra.dox @@ -55,8 +55,8 @@ matrix with a vector or another matrix: \f$ A^{-1} \mathbf{v} \f$ or \f$ A^{-1} This is a general-purpose algorithm which performs well in most cases (provided the matrix \f$ A \f$ is invertible), so if you are unsure about which algorithm to pick, choose this. The method proceeds in two steps. First, the %LU decomposition with partial pivoting is computed using the -MatrixBase::partialLu() function. This yields an object of the class PartialLU. Then, the -PartialLU::solve() method is called to compute a solution. +MatrixBase::partialPivLu() function. This yields an object of the class PartialPivLU. Then, the +PartialPivLU::solve() method is called to compute a solution. As an example, suppose we want to solve the following system of linear equations: @@ -69,9 +69,9 @@ As an example, suppose we want to solve the following system of linear equations The following program solves this system:
-\include Tutorial_PartialLU_solve.cpp +\include Tutorial_PartialPivLU_solve.cpp -output: \include Tutorial_PartialLU_solve.out +output: \include Tutorial_PartialPivLU_solve.out
There are many situations in which we want to solve the same system of equations with different @@ -91,7 +91,7 @@ problem, and whether you want to solve it at all, after you solved the first pro case, it's best to save the %LU decomposition and reuse it to solve the second problem. This is worth the effort because computing the %LU decomposition is much more expensive than using it to solve the equation. Here is some code to illustrate the procedure. It uses the constructor -PartialLU::PartialLU(const MatrixType&) to compute the %LU decomposition. +PartialPivLU::PartialPivLU(const MatrixType&) to compute the %LU decomposition.
\include Tutorial_solve_reuse_decomposition.cpp @@ -102,7 +102,7 @@ output: \include Tutorial_solve_reuse_decomposition.out \b Warning: All this code presumes that the matrix \f$ A \f$ is invertible, so that the system \f$ A \mathbf{x} = \mathbf{b} \f$ has a unique solution. If the matrix \f$ A \f$ is not invertible, then the system \f$ A \mathbf{x} = \mathbf{b} \f$ has either zero or infinitely many solutions. In -both cases, PartialLU::solve() will give nonsense results. For example, suppose that we want to +both cases, PartialPivLU::solve() will give nonsense results. For example, suppose that we want to solve the same system as above, but with the 10 in the last equation replaced by 9. Then the system of equations is inconsistent: adding the first and the third equation gives \f$ 8x + 10y + 12z = 7 \f$, which implies \f$ 4x + 5y + 6z = 3\frac12 \f$, in contradiction with the second equation. If we try @@ -114,10 +114,10 @@ to solve this inconsistent system with Eigen, we find: output: \include Tutorial_solve_singular.out
-The %LU decomposition with \b full pivoting (class LU) and the singular value decomposition (class +The %LU decomposition with \b full pivoting (class FullPivLU) and the singular value decomposition (class SVD) may be helpful in this case, as explained in the section \ref TutorialAdvSolvers_Misc below. -\sa LU_Module, MatrixBase::partialLu(), PartialLU::solve(), class PartialLU. +\sa LU_Module, MatrixBase::partialPivLu(), PartialPivLU::solve(), class PartialPivLU. \subsection TutorialAdvSolvers_Cholesky Cholesky decomposition @@ -228,7 +228,7 @@ Note that the function inverse() is defined in the \ref LU_Module. Finally, Eigen also offer solvers based on a singular value decomposition (%SVD) or the %LU decomposition with full pivoting. These have the same API as the solvers based on the %LU -decomposition with partial pivoting (PartialLU). +decomposition with partial pivoting (PartialPivLU). The solver based on the %SVD uses the class SVD. It can handle singular matrices. Here is an example of its use: @@ -245,7 +245,7 @@ svdOfA.solve(b, &x); \endcode %LU decomposition with full pivoting has better numerical stability than %LU decomposition with -partial pivoting. It is defined in the class LU. The solver can also handle singular matrices. +partial pivoting. It is defined in the class FullPivLU. The solver can also handle singular matrices. \code #include @@ -254,13 +254,13 @@ MatrixXf A = MatrixXf::Random(20,20); VectorXf b = VectorXf::Random(20); VectorXf x; A.lu().solve(b, &x); -LU luOfA(A); +FullPivLU luOfA(A); luOfA.solve(b, &x); \endcode See the section \ref TutorialAdvLU below. -\sa class SVD, SVD::solve(), SVD_Module, class LU, LU::solve(), LU_Module. +\sa class SVD, SVD::solve(), SVD_Module, class FullPivLU, LU::solve(), LU_Module. @@ -281,7 +281,7 @@ Alternatively, you can construct a named LU decomposition, which allows you to r \code #include MatrixXf A = MatrixXf::Random(20,20); -Eigen::LU lu(A); +Eigen::FullPivLU lu(A); cout << "The rank of A is" << lu.rank() << endl; if(lu.isInvertible()) { cout << "A is invertible, its inverse is:" << endl << lu.inverse() << endl; @@ -292,7 +292,7 @@ else { } \endcode -\sa LU_Module, LU::solve(), class LU +\sa LU_Module, LU::solve(), class FullPivLU top\section TutorialAdvCholesky Cholesky todo diff --git a/doc/examples/Tutorial_PartialLU_solve.cpp b/doc/examples/Tutorial_PartialLU_solve.cpp index 4cbd8c10d..98f9d6dac 100644 --- a/doc/examples/Tutorial_PartialLU_solve.cpp +++ b/doc/examples/Tutorial_PartialLU_solve.cpp @@ -12,6 +12,6 @@ int main(int, char *[]) b << 3, 3, 4; cout << "Here is the matrix A:" << endl << A << endl; cout << "Here is the vector b:" << endl << b << endl; - Vector3f x = A.partialLu().solve(b); + Vector3f x = A.lu().solve(b); cout << "The solution is:" << endl << x << endl; } diff --git a/doc/snippets/LU_image.cpp b/doc/snippets/FullPivLU_image.cpp similarity index 100% rename from doc/snippets/LU_image.cpp rename to doc/snippets/FullPivLU_image.cpp diff --git a/doc/snippets/LU_kernel.cpp b/doc/snippets/FullPivLU_kernel.cpp similarity index 100% rename from doc/snippets/LU_kernel.cpp rename to doc/snippets/FullPivLU_kernel.cpp diff --git a/doc/snippets/LU_solve.cpp b/doc/snippets/FullPivLU_solve.cpp similarity index 100% rename from doc/snippets/LU_solve.cpp rename to doc/snippets/FullPivLU_solve.cpp diff --git a/doc/snippets/PartialLU_solve.cpp b/doc/snippets/PartialLU_solve.cpp index 69e788dc4..fa3570ab8 100644 --- a/doc/snippets/PartialLU_solve.cpp +++ b/doc/snippets/PartialLU_solve.cpp @@ -2,6 +2,6 @@ MatrixXd A = MatrixXd::Random(3,3); MatrixXd B = MatrixXd::Random(3,2); cout << "Here is the invertible matrix A:" << endl << A << endl; cout << "Here is the matrix B:" << endl << B << endl; -MatrixXd X = A.partialLu().solve(B); +MatrixXd X = A.lu().solve(B); cout << "Here is the (unique) solution X to the equation AX=B:" << endl << X << endl; cout << "Relative error: " << (A*X-B).norm() / B.norm() << endl; diff --git a/doc/snippets/Tutorial_solve_multiple_rhs.cpp b/doc/snippets/Tutorial_solve_multiple_rhs.cpp index 1ffcc61f0..72dab9075 100644 --- a/doc/snippets/Tutorial_solve_multiple_rhs.cpp +++ b/doc/snippets/Tutorial_solve_multiple_rhs.cpp @@ -3,7 +3,7 @@ A << 1,2,3, 4,5,6, 7,8,10; Matrix B; B << 3,1, 3,1, 4,1; Matrix X; -X = A.partialLu().solve(B); +X = A.lu().solve(B); cout << "The solution with right-hand side (3,3,4) is:" << endl; cout << X.col(0) << endl; cout << "The solution with right-hand side (1,1,1) is:" << endl; diff --git a/doc/snippets/Tutorial_solve_reuse_decomposition.cpp b/doc/snippets/Tutorial_solve_reuse_decomposition.cpp index 1b8b0ae9e..3ca06453a 100644 --- a/doc/snippets/Tutorial_solve_reuse_decomposition.cpp +++ b/doc/snippets/Tutorial_solve_reuse_decomposition.cpp @@ -1,6 +1,6 @@ Matrix3f A(3,3); A << 1,2,3, 4,5,6, 7,8,10; -PartialLU luOfA(A); // compute LU decomposition of A +PartialPivLU luOfA(A); // compute LU decomposition of A Vector3f b; b << 3,3,4; Vector3f x; diff --git a/doc/snippets/Tutorial_solve_singular.cpp b/doc/snippets/Tutorial_solve_singular.cpp index f5f2d2f6a..abff1ef73 100644 --- a/doc/snippets/Tutorial_solve_singular.cpp +++ b/doc/snippets/Tutorial_solve_singular.cpp @@ -5,5 +5,5 @@ b << 3, 3, 4; cout << "Here is the matrix A:" << endl << A << endl; cout << "Here is the vector b:" << endl << b << endl; Vector3f x; -x = A.partialLu().solve(b); +x = A.lu().solve(b); cout << "The solution is:" << endl << x << endl; diff --git a/doc/snippets/class_LU.cpp b/doc/snippets/class_FullPivLU.cpp similarity index 95% rename from doc/snippets/class_LU.cpp rename to doc/snippets/class_FullPivLU.cpp index 9958368f1..40d76e8e6 100644 --- a/doc/snippets/class_LU.cpp +++ b/doc/snippets/class_FullPivLU.cpp @@ -2,7 +2,7 @@ typedef Matrix Matrix5x3; typedef Matrix Matrix5x5; Matrix5x3 m = Matrix5x3::Random(); cout << "Here is the matrix m:" << endl << m << endl; -Eigen::LU lu(m); +Eigen::FullPivLU lu(m); cout << "Here is, up to permutations, its LU decomposition matrix:" << endl << lu.matrixLU() << endl; cout << "Here is the L part:" << endl; diff --git a/test/adjoint.cpp b/test/adjoint.cpp index bebf47ac3..344399257 100644 --- a/test/adjoint.cpp +++ b/test/adjoint.cpp @@ -108,16 +108,17 @@ template void adjoint(const MatrixType& m) void test_adjoint() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( adjoint(Matrix()) ); - CALL_SUBTEST( adjoint(Matrix3d()) ); - CALL_SUBTEST( adjoint(Matrix4f()) ); - CALL_SUBTEST( adjoint(MatrixXcf(4, 4)) ); - CALL_SUBTEST( adjoint(MatrixXi(8, 12)) ); - CALL_SUBTEST( adjoint(MatrixXf(21, 21)) ); + CALL_SUBTEST_1( adjoint(Matrix()) ); + CALL_SUBTEST_2( adjoint(Matrix3d()) ); + CALL_SUBTEST_3( adjoint(Matrix4f()) ); + CALL_SUBTEST_4( adjoint(MatrixXcf(4, 4)) ); + CALL_SUBTEST_5( adjoint(MatrixXi(8, 12)) ); + CALL_SUBTEST_6( adjoint(MatrixXf(21, 21)) ); } // test a large matrix only once - CALL_SUBTEST( adjoint(Matrix()) ); + CALL_SUBTEST_7( adjoint(Matrix()) ); +#ifdef EIGEN_TEST_PART_4 { MatrixXcf a(10,10), b(10,10); VERIFY_RAISES_ASSERT(a = a.transpose()); @@ -128,5 +129,6 @@ void test_adjoint() VERIFY_RAISES_ASSERT(a = a.adjoint() + b); VERIFY_RAISES_ASSERT(a = b + a.adjoint()); } +#endif } diff --git a/test/array.cpp b/test/array.cpp index a308f7366..a18724d3a 100644 --- a/test/array.cpp +++ b/test/array.cpp @@ -146,26 +146,26 @@ template void lpNorm(const VectorType& v) void test_array() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( array(Matrix()) ); - CALL_SUBTEST( array(Matrix2f()) ); - CALL_SUBTEST( array(Matrix4d()) ); - CALL_SUBTEST( array(MatrixXcf(3, 3)) ); - CALL_SUBTEST( array(MatrixXf(8, 12)) ); - CALL_SUBTEST( array(MatrixXi(8, 12)) ); + CALL_SUBTEST_1( array(Matrix()) ); + CALL_SUBTEST_2( array(Matrix2f()) ); + CALL_SUBTEST_3( array(Matrix4d()) ); + CALL_SUBTEST_4( array(MatrixXcf(3, 3)) ); + CALL_SUBTEST_5( array(MatrixXf(8, 12)) ); + CALL_SUBTEST_6( array(MatrixXi(8, 12)) ); } for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( comparisons(Matrix()) ); - CALL_SUBTEST( comparisons(Matrix2f()) ); - CALL_SUBTEST( comparisons(Matrix4d()) ); - CALL_SUBTEST( comparisons(MatrixXf(8, 12)) ); - CALL_SUBTEST( comparisons(MatrixXi(8, 12)) ); + CALL_SUBTEST_1( comparisons(Matrix()) ); + CALL_SUBTEST_2( comparisons(Matrix2f()) ); + CALL_SUBTEST_3( comparisons(Matrix4d()) ); + CALL_SUBTEST_5( comparisons(MatrixXf(8, 12)) ); + CALL_SUBTEST_6( comparisons(MatrixXi(8, 12)) ); } for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( lpNorm(Matrix()) ); - CALL_SUBTEST( lpNorm(Vector2f()) ); - CALL_SUBTEST( lpNorm(Vector3d()) ); - CALL_SUBTEST( lpNorm(Vector4f()) ); - CALL_SUBTEST( lpNorm(VectorXf(16)) ); - CALL_SUBTEST( lpNorm(VectorXcd(10)) ); + CALL_SUBTEST_1( lpNorm(Matrix()) ); + CALL_SUBTEST_2( lpNorm(Vector2f()) ); + CALL_SUBTEST_7( lpNorm(Vector3d()) ); + CALL_SUBTEST_8( lpNorm(Vector4f()) ); + CALL_SUBTEST_5( lpNorm(VectorXf(16)) ); + CALL_SUBTEST_4( lpNorm(VectorXcf(10)) ); } } diff --git a/test/array_replicate.cpp b/test/array_replicate.cpp index cd0f65f26..a23ac7c6f 100644 --- a/test/array_replicate.cpp +++ b/test/array_replicate.cpp @@ -76,11 +76,11 @@ template void replicate(const MatrixType& m) void test_array_replicate() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( replicate(Matrix()) ); - CALL_SUBTEST( replicate(Vector2f()) ); - CALL_SUBTEST( replicate(Vector3d()) ); - CALL_SUBTEST( replicate(Vector4f()) ); - CALL_SUBTEST( replicate(VectorXf(16)) ); - CALL_SUBTEST( replicate(VectorXcd(10)) ); + CALL_SUBTEST_1( replicate(Matrix()) ); + CALL_SUBTEST_2( replicate(Vector2f()) ); + CALL_SUBTEST_3( replicate(Vector3d()) ); + CALL_SUBTEST_4( replicate(Vector4f()) ); + CALL_SUBTEST_5( replicate(VectorXf(16)) ); + CALL_SUBTEST_6( replicate(VectorXcd(10)) ); } } diff --git a/test/array_reverse.cpp b/test/array_reverse.cpp index e77842c7d..ccf8dcc87 100644 --- a/test/array_reverse.cpp +++ b/test/array_reverse.cpp @@ -163,19 +163,20 @@ template void reverse(const MatrixType& m) void test_array_reverse() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( reverse(Matrix()) ); - CALL_SUBTEST( reverse(Matrix2f()) ); - CALL_SUBTEST( reverse(Matrix4f()) ); - CALL_SUBTEST( reverse(Matrix4d()) ); - CALL_SUBTEST( reverse(MatrixXcf(3, 3)) ); - CALL_SUBTEST( reverse(MatrixXi(6, 3)) ); - CALL_SUBTEST( reverse(MatrixXcd(20, 20)) ); - CALL_SUBTEST( reverse(Matrix()) ); - CALL_SUBTEST( reverse(Matrix(6,3)) ); + CALL_SUBTEST_1( reverse(Matrix()) ); + CALL_SUBTEST_2( reverse(Matrix2f()) ); + CALL_SUBTEST_3( reverse(Matrix4f()) ); + CALL_SUBTEST_4( reverse(Matrix4d()) ); + CALL_SUBTEST_5( reverse(MatrixXcf(3, 3)) ); + CALL_SUBTEST_6( reverse(MatrixXi(6, 3)) ); + CALL_SUBTEST_7( reverse(MatrixXcd(20, 20)) ); + CALL_SUBTEST_8( reverse(Matrix()) ); + CALL_SUBTEST_9( reverse(Matrix(6,3)) ); } +#ifdef EIGEN_TEST_PART_3 Vector4f x; x << 1, 2, 3, 4; Vector4f y; y << 4, 3, 2, 1; VERIFY(x.reverse()[1] == 3); VERIFY(x.reverse() == y); - +#endif } diff --git a/test/bandmatrix.cpp b/test/bandmatrix.cpp index 2bdc67e28..ecb7304db 100644 --- a/test/bandmatrix.cpp +++ b/test/bandmatrix.cpp @@ -79,6 +79,6 @@ void test_bandmatrix() int cols = ei_random(1,10); int sups = ei_random(0,cols-1); int subs = ei_random(0,rows-1); - CALL_SUBTEST( bandmatrix(BandMatrix(rows,cols,sups,subs)) ); + CALL_SUBTEST(bandmatrix(BandMatrix(rows,cols,sups,subs)) ); } } diff --git a/test/basicstuff.cpp b/test/basicstuff.cpp index 29df99f9e..4678439d5 100644 --- a/test/basicstuff.cpp +++ b/test/basicstuff.cpp @@ -146,17 +146,17 @@ void casting() void test_basicstuff() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( basicStuff(Matrix()) ); - CALL_SUBTEST( basicStuff(Matrix4d()) ); - CALL_SUBTEST( basicStuff(MatrixXcf(3, 3)) ); - CALL_SUBTEST( basicStuff(MatrixXi(8, 12)) ); - CALL_SUBTEST( basicStuff(MatrixXcd(20, 20)) ); - CALL_SUBTEST( basicStuff(Matrix()) ); - CALL_SUBTEST( basicStuff(Matrix(10,10)) ); + CALL_SUBTEST_1( basicStuff(Matrix()) ); + CALL_SUBTEST_2( basicStuff(Matrix4d()) ); + CALL_SUBTEST_3( basicStuff(MatrixXcf(3, 3)) ); + CALL_SUBTEST_4( basicStuff(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( basicStuff(MatrixXcd(20, 20)) ); + CALL_SUBTEST_6( basicStuff(Matrix()) ); + CALL_SUBTEST_7( basicStuff(Matrix(10,10)) ); - CALL_SUBTEST( basicStuffComplex(MatrixXcf(21, 17)) ); - CALL_SUBTEST( basicStuffComplex(MatrixXcd(2, 3)) ); + CALL_SUBTEST_3( basicStuffComplex(MatrixXcf(21, 17)) ); + CALL_SUBTEST_5( basicStuffComplex(MatrixXcd(2, 3)) ); } - CALL_SUBTEST(casting()); + CALL_SUBTEST_2(casting()); } diff --git a/test/cholesky.cpp b/test/cholesky.cpp index 526a9f9d0..77d025e59 100644 --- a/test/cholesky.cpp +++ b/test/cholesky.cpp @@ -148,17 +148,17 @@ template void cholesky_verify_assert() void test_cholesky() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( cholesky(Matrix()) ); - CALL_SUBTEST( cholesky(MatrixXd(1,1)) ); - CALL_SUBTEST( cholesky(Matrix2d()) ); - CALL_SUBTEST( cholesky(Matrix3f()) ); - CALL_SUBTEST( cholesky(Matrix4d()) ); - CALL_SUBTEST( cholesky(MatrixXd(200,200)) ); - CALL_SUBTEST( cholesky(MatrixXcd(100,100)) ); + CALL_SUBTEST_1( cholesky(Matrix()) ); + CALL_SUBTEST_2( cholesky(MatrixXd(1,1)) ); + CALL_SUBTEST_3( cholesky(Matrix2d()) ); + CALL_SUBTEST_4( cholesky(Matrix3f()) ); + CALL_SUBTEST_5( cholesky(Matrix4d()) ); + CALL_SUBTEST_2( cholesky(MatrixXd(200,200)) ); + CALL_SUBTEST_6( cholesky(MatrixXcd(100,100)) ); } - CALL_SUBTEST( cholesky_verify_assert() ); - CALL_SUBTEST( cholesky_verify_assert() ); - CALL_SUBTEST( cholesky_verify_assert() ); - CALL_SUBTEST( cholesky_verify_assert() ); + CALL_SUBTEST_4( cholesky_verify_assert() ); + CALL_SUBTEST_7( cholesky_verify_assert() ); + CALL_SUBTEST_8( cholesky_verify_assert() ); + CALL_SUBTEST_2( cholesky_verify_assert() ); } diff --git a/test/conservative_resize.cpp b/test/conservative_resize.cpp index b92dd5449..a7b1f78ff 100644 --- a/test/conservative_resize.cpp +++ b/test/conservative_resize.cpp @@ -110,20 +110,20 @@ void run_vector_tests() void test_conservative_resize() { - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests, Eigen::RowMajor>(); - run_matrix_tests, Eigen::ColMajor>(); - run_matrix_tests, Eigen::RowMajor>(); - run_matrix_tests, Eigen::ColMajor>(); + CALL_SUBTEST_1((run_matrix_tests())); + CALL_SUBTEST_1((run_matrix_tests())); + CALL_SUBTEST_2((run_matrix_tests())); + CALL_SUBTEST_2((run_matrix_tests())); + CALL_SUBTEST_3((run_matrix_tests())); + CALL_SUBTEST_3((run_matrix_tests())); + CALL_SUBTEST_4((run_matrix_tests, Eigen::RowMajor>())); + CALL_SUBTEST_4((run_matrix_tests, Eigen::ColMajor>())); + CALL_SUBTEST_5((run_matrix_tests, Eigen::RowMajor>())); + CALL_SUBTEST_6((run_matrix_tests, Eigen::ColMajor>())); - run_vector_tests(); - run_vector_tests(); - run_vector_tests(); - run_vector_tests >(); - run_vector_tests >(); + CALL_SUBTEST_1((run_vector_tests())); + CALL_SUBTEST_2((run_vector_tests())); + CALL_SUBTEST_3((run_vector_tests())); + CALL_SUBTEST_4((run_vector_tests >())); + CALL_SUBTEST_5((run_vector_tests >())); } diff --git a/test/cwiseop.cpp b/test/cwiseop.cpp index 10784b0f5..7574bcc4a 100644 --- a/test/cwiseop.cpp +++ b/test/cwiseop.cpp @@ -163,11 +163,11 @@ template void cwiseops(const MatrixType& m) void test_cwiseop() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST( cwiseops(Matrix()) ); - CALL_SUBTEST( cwiseops(Matrix4d()) ); - CALL_SUBTEST( cwiseops(MatrixXf(3, 3)) ); - CALL_SUBTEST( cwiseops(MatrixXf(22, 22)) ); - CALL_SUBTEST( cwiseops(MatrixXi(8, 12)) ); - CALL_SUBTEST( cwiseops(MatrixXd(20, 20)) ); + CALL_SUBTEST_1( cwiseops(Matrix()) ); + CALL_SUBTEST_2( cwiseops(Matrix4d()) ); + CALL_SUBTEST_3( cwiseops(MatrixXf(3, 3)) ); + CALL_SUBTEST_4( cwiseops(MatrixXf(22, 22)) ); + CALL_SUBTEST_5( cwiseops(MatrixXi(8, 12)) ); + CALL_SUBTEST_6( cwiseops(MatrixXd(20, 20)) ); } } diff --git a/test/determinant.cpp b/test/determinant.cpp index d9de5f6ce..7aa9a870d 100644 --- a/test/determinant.cpp +++ b/test/determinant.cpp @@ -65,12 +65,12 @@ template void determinant(const MatrixType& m) void test_determinant() { for(int i = 0; i < g_repeat; i++) { - 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)) ); + CALL_SUBTEST_1( determinant(Matrix()) ); + CALL_SUBTEST_2( determinant(Matrix()) ); + CALL_SUBTEST_3( determinant(Matrix()) ); + CALL_SUBTEST_4( determinant(Matrix()) ); + CALL_SUBTEST_5( determinant(Matrix, 10, 10>()) ); + CALL_SUBTEST_6( determinant(MatrixXd(20, 20)) ); } - CALL_SUBTEST( determinant(MatrixXd(200, 200)) ); + CALL_SUBTEST_6( determinant(MatrixXd(200, 200)) ); } diff --git a/test/diagonalmatrices.cpp b/test/diagonalmatrices.cpp index 9eb0f10f2..c24c66d03 100644 --- a/test/diagonalmatrices.cpp +++ b/test/diagonalmatrices.cpp @@ -95,14 +95,14 @@ template void diagonalmatrices(const MatrixType& m) void test_diagonalmatrices() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( diagonalmatrices(Matrix()) ); - CALL_SUBTEST( diagonalmatrices(Matrix3f()) ); - CALL_SUBTEST( diagonalmatrices(Matrix()) ); - CALL_SUBTEST( diagonalmatrices(Matrix4d()) ); - CALL_SUBTEST( diagonalmatrices(Matrix()) ); - CALL_SUBTEST( diagonalmatrices(MatrixXcf(3, 5)) ); - CALL_SUBTEST( diagonalmatrices(MatrixXi(10, 8)) ); - CALL_SUBTEST( diagonalmatrices(Matrix(20, 20)) ); - CALL_SUBTEST( diagonalmatrices(MatrixXf(21, 24)) ); + CALL_SUBTEST_1( diagonalmatrices(Matrix()) ); + CALL_SUBTEST_2( diagonalmatrices(Matrix3f()) ); + CALL_SUBTEST_3( diagonalmatrices(Matrix()) ); + CALL_SUBTEST_4( diagonalmatrices(Matrix4d()) ); + CALL_SUBTEST_5( diagonalmatrices(Matrix()) ); + CALL_SUBTEST_6( diagonalmatrices(MatrixXcf(3, 5)) ); + CALL_SUBTEST_7( diagonalmatrices(MatrixXi(10, 8)) ); + CALL_SUBTEST_8( diagonalmatrices(Matrix(20, 20)) ); + CALL_SUBTEST_9( diagonalmatrices(MatrixXf(21, 24)) ); } } diff --git a/test/dynalloc.cpp b/test/dynalloc.cpp index 08fc0369d..e0a9f9f86 100644 --- a/test/dynalloc.cpp +++ b/test/dynalloc.cpp @@ -112,11 +112,11 @@ void test_dynalloc() for (int i=0; i() ); - CALL_SUBTEST( check_dynaligned() ); - CALL_SUBTEST( check_dynaligned() ); - CALL_SUBTEST( check_dynaligned() ); - CALL_SUBTEST( check_dynaligned() ); + CALL_SUBTEST(check_dynaligned() ); + CALL_SUBTEST(check_dynaligned() ); + CALL_SUBTEST(check_dynaligned() ); + CALL_SUBTEST(check_dynaligned() ); + CALL_SUBTEST(check_dynaligned() ); } // check static allocation, who knows ? diff --git a/test/eigensolver_complex.cpp b/test/eigensolver_complex.cpp index a8e6c709e..86e1b200a 100644 --- a/test/eigensolver_complex.cpp +++ b/test/eigensolver_complex.cpp @@ -54,8 +54,8 @@ template void eigensolver(const MatrixType& m) void test_eigensolver_complex() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST1( eigensolver(Matrix4cf()) ); - CALL_SUBTEST2( eigensolver(MatrixXcd(14,14)) ); + CALL_SUBTEST_1( eigensolver(Matrix4cf()) ); + CALL_SUBTEST_2( eigensolver(MatrixXcd(14,14)) ); } } diff --git a/test/eigensolver_generic.cpp b/test/eigensolver_generic.cpp index 6c91ebabe..5dc18f141 100644 --- a/test/eigensolver_generic.cpp +++ b/test/eigensolver_generic.cpp @@ -75,18 +75,18 @@ template void eigensolver_verify_assert() void test_eigensolver_generic() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST1( eigensolver(Matrix4f()) ); - CALL_SUBTEST2( eigensolver(MatrixXd(17,17)) ); + CALL_SUBTEST_1( eigensolver(Matrix4f()) ); + CALL_SUBTEST_2( eigensolver(MatrixXd(17,17)) ); // some trivial but implementation-wise tricky cases - CALL_SUBTEST2( eigensolver(MatrixXd(1,1)) ); - CALL_SUBTEST2( eigensolver(MatrixXd(2,2)) ); - CALL_SUBTEST3( eigensolver(Matrix()) ); - CALL_SUBTEST4( eigensolver(Matrix2d()) ); + CALL_SUBTEST_2( eigensolver(MatrixXd(1,1)) ); + CALL_SUBTEST_2( eigensolver(MatrixXd(2,2)) ); + CALL_SUBTEST_3( eigensolver(Matrix()) ); + CALL_SUBTEST_4( eigensolver(Matrix2d()) ); } - CALL_SUBTEST1( eigensolver_verify_assert() ); - CALL_SUBTEST2( eigensolver_verify_assert() ); - CALL_SUBTEST4( eigensolver_verify_assert() ); - CALL_SUBTEST5( eigensolver_verify_assert() ); + CALL_SUBTEST_1( eigensolver_verify_assert() ); + CALL_SUBTEST_2( eigensolver_verify_assert() ); + CALL_SUBTEST_4( eigensolver_verify_assert() ); + CALL_SUBTEST_5( eigensolver_verify_assert() ); } diff --git a/test/eigensolver_selfadjoint.cpp b/test/eigensolver_selfadjoint.cpp index 27a5f2246..632c1d2e7 100644 --- a/test/eigensolver_selfadjoint.cpp +++ b/test/eigensolver_selfadjoint.cpp @@ -117,17 +117,17 @@ void test_eigensolver_selfadjoint() { for(int i = 0; i < g_repeat; i++) { // very important to test a 3x3 matrix since we provide a special path for it - CALL_SUBTEST1( selfadjointeigensolver(Matrix3f()) ); - CALL_SUBTEST2( selfadjointeigensolver(Matrix4d()) ); - CALL_SUBTEST3( selfadjointeigensolver(MatrixXf(10,10)) ); - CALL_SUBTEST4( selfadjointeigensolver(MatrixXd(19,19)) ); - CALL_SUBTEST5( selfadjointeigensolver(MatrixXcd(17,17)) ); + CALL_SUBTEST_1( selfadjointeigensolver(Matrix3f()) ); + CALL_SUBTEST_2( selfadjointeigensolver(Matrix4d()) ); + CALL_SUBTEST_3( selfadjointeigensolver(MatrixXf(10,10)) ); + CALL_SUBTEST_4( selfadjointeigensolver(MatrixXd(19,19)) ); + CALL_SUBTEST_5( selfadjointeigensolver(MatrixXcd(17,17)) ); // some trivial but implementation-wise tricky cases - CALL_SUBTEST4( selfadjointeigensolver(MatrixXd(1,1)) ); - CALL_SUBTEST4( selfadjointeigensolver(MatrixXd(2,2)) ); - CALL_SUBTEST6( selfadjointeigensolver(Matrix()) ); - CALL_SUBTEST7( selfadjointeigensolver(Matrix()) ); + CALL_SUBTEST_4( selfadjointeigensolver(MatrixXd(1,1)) ); + CALL_SUBTEST_4( selfadjointeigensolver(MatrixXd(2,2)) ); + CALL_SUBTEST_6( selfadjointeigensolver(Matrix()) ); + CALL_SUBTEST_7( selfadjointeigensolver(Matrix()) ); } } diff --git a/test/geo_alignedbox.cpp b/test/geo_alignedbox.cpp index 07b3e59e6..d496578da 100644 --- a/test/geo_alignedbox.cpp +++ b/test/geo_alignedbox.cpp @@ -75,8 +75,8 @@ template void alignedbox(const BoxType& _box) void test_geo_alignedbox() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( alignedbox(AlignedBox()) ); - CALL_SUBTEST( alignedbox(AlignedBox()) ); - CALL_SUBTEST( alignedbox(AlignedBox()) ); + CALL_SUBTEST_1( alignedbox(AlignedBox()) ); + CALL_SUBTEST_2( alignedbox(AlignedBox()) ); + CALL_SUBTEST_3( alignedbox(AlignedBox()) ); } } diff --git a/test/geo_eulerangles.cpp b/test/geo_eulerangles.cpp index f11c8e8ef..98cdf6a86 100644 --- a/test/geo_eulerangles.cpp +++ b/test/geo_eulerangles.cpp @@ -64,7 +64,7 @@ template void eulerangles(void) void test_geo_eulerangles() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( eulerangles() ); - CALL_SUBTEST( eulerangles() ); + CALL_SUBTEST_1( eulerangles() ); + CALL_SUBTEST_2( eulerangles() ); } } diff --git a/test/geo_homogeneous.cpp b/test/geo_homogeneous.cpp index 4b66e488c..48d8cbcdf 100644 --- a/test/geo_homogeneous.cpp +++ b/test/geo_homogeneous.cpp @@ -104,8 +104,8 @@ template void homogeneous(void) void test_geo_homogeneous() { for(int i = 0; i < g_repeat; i++) { -// CALL_SUBTEST(( homogeneous() )); - CALL_SUBTEST(( homogeneous() )); -// CALL_SUBTEST(( homogeneous() )); + CALL_SUBTEST_1(( homogeneous() )); + CALL_SUBTEST_2(( homogeneous() )); + CALL_SUBTEST_3(( homogeneous() )); } } diff --git a/test/geo_hyperplane.cpp b/test/geo_hyperplane.cpp index f8281a16b..f1d3b016f 100644 --- a/test/geo_hyperplane.cpp +++ b/test/geo_hyperplane.cpp @@ -130,11 +130,11 @@ template void lines() void test_geo_hyperplane() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( hyperplane(Hyperplane()) ); - CALL_SUBTEST( hyperplane(Hyperplane()) ); - CALL_SUBTEST( hyperplane(Hyperplane()) ); - CALL_SUBTEST( hyperplane(Hyperplane,5>()) ); - CALL_SUBTEST( lines() ); - CALL_SUBTEST( lines() ); + CALL_SUBTEST_1( hyperplane(Hyperplane()) ); + CALL_SUBTEST_2( hyperplane(Hyperplane()) ); + CALL_SUBTEST_3( hyperplane(Hyperplane()) ); + CALL_SUBTEST_4( hyperplane(Hyperplane,5>()) ); + CALL_SUBTEST_1( lines() ); + CALL_SUBTEST_2( lines() ); } } diff --git a/test/geo_orthomethods.cpp b/test/geo_orthomethods.cpp index 540a63b82..54a6febab 100644 --- a/test/geo_orthomethods.cpp +++ b/test/geo_orthomethods.cpp @@ -113,15 +113,15 @@ template void orthomethods(int size=Size) void test_geo_orthomethods() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( orthomethods_3() ); - CALL_SUBTEST( orthomethods_3() ); - CALL_SUBTEST( (orthomethods()) ); - CALL_SUBTEST( (orthomethods()) ); - CALL_SUBTEST( (orthomethods()) ); - CALL_SUBTEST( (orthomethods()) ); - CALL_SUBTEST( (orthomethods()) ); - CALL_SUBTEST( (orthomethods,8>()) ); - CALL_SUBTEST( (orthomethods(36)) ); - CALL_SUBTEST( (orthomethods(35)) ); + CALL_SUBTEST_1( orthomethods_3() ); + CALL_SUBTEST_2( orthomethods_3() ); + CALL_SUBTEST_1( (orthomethods()) ); + CALL_SUBTEST_2( (orthomethods()) ); + CALL_SUBTEST_1( (orthomethods()) ); + CALL_SUBTEST_2( (orthomethods()) ); + CALL_SUBTEST_3( (orthomethods()) ); + CALL_SUBTEST_4( (orthomethods,8>()) ); + CALL_SUBTEST_5( (orthomethods(36)) ); + CALL_SUBTEST_6( (orthomethods(35)) ); } } diff --git a/test/geo_parametrizedline.cpp b/test/geo_parametrizedline.cpp index c90d59d3e..137324a98 100644 --- a/test/geo_parametrizedline.cpp +++ b/test/geo_parametrizedline.cpp @@ -69,9 +69,9 @@ template void parametrizedline(const LineType& _line) void test_geo_parametrizedline() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( parametrizedline(ParametrizedLine()) ); - CALL_SUBTEST( parametrizedline(ParametrizedLine()) ); - CALL_SUBTEST( parametrizedline(ParametrizedLine()) ); - CALL_SUBTEST( parametrizedline(ParametrizedLine,5>()) ); + CALL_SUBTEST_1( parametrizedline(ParametrizedLine()) ); + CALL_SUBTEST_2( parametrizedline(ParametrizedLine()) ); + CALL_SUBTEST_3( parametrizedline(ParametrizedLine()) ); + CALL_SUBTEST_4( parametrizedline(ParametrizedLine,5>()) ); } } diff --git a/test/geo_quaternion.cpp b/test/geo_quaternion.cpp index 762743fba..7dbf890f4 100644 --- a/test/geo_quaternion.cpp +++ b/test/geo_quaternion.cpp @@ -92,9 +92,12 @@ template void quaternion(void) VERIFY_IS_APPROX( v2.normalized(),(q2.setFromTwoVectors(v1, v2)*v1).normalized()); VERIFY_IS_APPROX( v1.normalized(),(q2.setFromTwoVectors(v1, v1)*v1).normalized()); VERIFY_IS_APPROX(-v1.normalized(),(q2.setFromTwoVectors(v1,-v1)*v1).normalized()); - v3 = v1.cwise()+eps; - VERIFY_IS_APPROX( v3.normalized(),(q2.setFromTwoVectors(v1, v3)*v1).normalized()); - VERIFY_IS_APPROX(-v3.normalized(),(q2.setFromTwoVectors(v1,-v3)*v1).normalized()); + if (ei_is_same_type::ret) + { + v3 = v1.cwise()+eps; + VERIFY_IS_APPROX( v3.normalized(),(q2.setFromTwoVectors(v1, v3)*v1).normalized()); + VERIFY_IS_APPROX(-v3.normalized(),(q2.setFromTwoVectors(v1,-v3)*v1).normalized()); + } // inverse and conjugate VERIFY_IS_APPROX(q1 * (q1.inverse() * v1), v1); @@ -110,7 +113,7 @@ template void quaternion(void) void test_geo_quaternion() { for(int i = 0; i < g_repeat; i++) { -// CALL_SUBTEST( quaternion() ); - CALL_SUBTEST( quaternion() ); + CALL_SUBTEST_1( quaternion() ); + CALL_SUBTEST_2( quaternion() ); } } diff --git a/test/geo_transformations.cpp b/test/geo_transformations.cpp index 23b297314..f1d068b83 100644 --- a/test/geo_transformations.cpp +++ b/test/geo_transformations.cpp @@ -298,16 +298,19 @@ template void transformations(void) VERIFY_IS_APPROX((t0 * v1).template start<3>(), AlignedScaling3(v0) * v1); // test transform inversion - if(Mode!=AffineCompact) - { - t0.setIdentity(); - t0.translate(v0); - t0.linear().setRandom(); - VERIFY_IS_APPROX(t0.inverse(Affine).matrix(), t0.matrix().inverse()); - t0.setIdentity(); - t0.translate(v0).rotate(q1); - VERIFY_IS_APPROX(t0.inverse(Isometry).matrix(), t0.matrix().inverse()); - } + t0.setIdentity(); + t0.translate(v0); + t0.linear().setRandom(); + Matrix4 t044 = Matrix4::Zero(); + t044(3,3) = 1; + t044.block(0,0,t0.matrix().rows(),4) = t0.matrix(); + VERIFY_IS_APPROX(t0.inverse(Affine).matrix(), t044.inverse().block(0,0,t0.matrix().rows(),4)); + t0.setIdentity(); + t0.translate(v0).rotate(q1); + t044 = Matrix4::Zero(); + t044(3,3) = 1; + t044.block(0,0,t0.matrix().rows(),4) = t0.matrix(); + VERIFY_IS_APPROX(t0.inverse(Isometry).matrix(), t044.inverse().block(0,0,t0.matrix().rows(),4)); // test extract rotation and aligned scaling // t0.setIdentity(); @@ -354,9 +357,8 @@ template void transformations(void) void test_geo_transformations() { for(int i = 0; i < g_repeat; i++) { -// CALL_SUBTEST( transformations() ); - CALL_SUBTEST(( transformations() )); - CALL_SUBTEST(( transformations() )); - CALL_SUBTEST(( transformations() )); + CALL_SUBTEST_1(( transformations() )); + CALL_SUBTEST_2(( transformations() )); + CALL_SUBTEST_3(( transformations() )); } } diff --git a/test/householder.cpp b/test/householder.cpp index b27279479..c9b07622d 100644 --- a/test/householder.cpp +++ b/test/householder.cpp @@ -92,12 +92,12 @@ template void householder(const MatrixType& m) void test_householder() { for(int i = 0; i < 2*g_repeat; i++) { - CALL_SUBTEST( householder(Matrix()) ); - CALL_SUBTEST( householder(Matrix()) ); - CALL_SUBTEST( householder(Matrix()) ); - CALL_SUBTEST( householder(Matrix()) ); - CALL_SUBTEST( householder(MatrixXd(10,12)) ); - CALL_SUBTEST( householder(MatrixXcf(16,17)) ); + CALL_SUBTEST_1( householder(Matrix()) ); + CALL_SUBTEST_2( householder(Matrix()) ); + CALL_SUBTEST_3( householder(Matrix()) ); + CALL_SUBTEST_4( householder(Matrix()) ); + CALL_SUBTEST_5( householder(MatrixXd(10,12)) ); + CALL_SUBTEST_6( householder(MatrixXcf(16,17)) ); } } diff --git a/test/inverse.cpp b/test/inverse.cpp index 269678fd4..3ed61d356 100644 --- a/test/inverse.cpp +++ b/test/inverse.cpp @@ -95,14 +95,14 @@ void test_inverse() { int s; for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST1( inverse(Matrix()) ); - CALL_SUBTEST2( inverse(Matrix2d()) ); - CALL_SUBTEST3( inverse(Matrix3f()) ); - CALL_SUBTEST4( inverse(Matrix4f()) ); + CALL_SUBTEST_1( inverse(Matrix()) ); + CALL_SUBTEST_2( inverse(Matrix2d()) ); + CALL_SUBTEST_3( inverse(Matrix3f()) ); + CALL_SUBTEST_4( inverse(Matrix4f()) ); s = ei_random(50,320); - CALL_SUBTEST5( inverse(MatrixXf(s,s)) ); + CALL_SUBTEST_5( inverse(MatrixXf(s,s)) ); s = ei_random(25,100); - CALL_SUBTEST6( inverse(MatrixXcd(s,s)) ); + CALL_SUBTEST_6( inverse(MatrixXcd(s,s)) ); } #ifdef EIGEN_TEST_PART_4 diff --git a/test/jacobisvd.cpp b/test/jacobisvd.cpp index 2e3f089a0..f3a143e3c 100644 --- a/test/jacobisvd.cpp +++ b/test/jacobisvd.cpp @@ -80,27 +80,27 @@ void test_jacobisvd() Matrix2cd m; m << 0, 1, 0, 1; - CALL_SUBTEST(( svd(m, false) )); + CALL_SUBTEST_1(( svd(m, false) )); m << 1, 0, 1, 0; - CALL_SUBTEST(( svd(m, false) )); + CALL_SUBTEST_1(( svd(m, false) )); Matrix2d n; n << 1, 1, 1, -1; - CALL_SUBTEST(( svd(n, false) )); - CALL_SUBTEST(( svd() )); - CALL_SUBTEST(( svd() )); - CALL_SUBTEST(( svd , AtLeastAsManyColsAsRows>() )); - CALL_SUBTEST(( svd , AtLeastAsManyRowsAsCols>(Matrix(10,2)) )); + CALL_SUBTEST_2(( svd(n, false) )); + CALL_SUBTEST_3(( svd() )); + CALL_SUBTEST_4(( svd() )); + CALL_SUBTEST_5(( svd , AtLeastAsManyColsAsRows>() )); + CALL_SUBTEST_6(( svd , AtLeastAsManyRowsAsCols>(Matrix(10,2)) )); - CALL_SUBTEST(( svd(MatrixXf(50,50)) )); - CALL_SUBTEST(( svd(MatrixXcd(14,7)) )); + CALL_SUBTEST_7(( svd(MatrixXf(50,50)) )); + CALL_SUBTEST_8(( svd(MatrixXcd(14,7)) )); } - CALL_SUBTEST(( svd(MatrixXf(300,200)) )); - CALL_SUBTEST(( svd(MatrixXcd(100,150)) )); + CALL_SUBTEST_9(( svd(MatrixXf(300,200)) )); + CALL_SUBTEST_10(( svd(MatrixXcd(100,150)) )); - CALL_SUBTEST(( svd_verify_assert() )); - CALL_SUBTEST(( svd_verify_assert() )); - CALL_SUBTEST(( svd_verify_assert() )); - CALL_SUBTEST(( svd_verify_assert() )); + CALL_SUBTEST_3(( svd_verify_assert() )); + CALL_SUBTEST_3(( svd_verify_assert() )); + CALL_SUBTEST_9(( svd_verify_assert() )); + CALL_SUBTEST_11(( svd_verify_assert() )); } diff --git a/test/linearstructure.cpp b/test/linearstructure.cpp index 8827c40d1..f2ffc33bd 100644 --- a/test/linearstructure.cpp +++ b/test/linearstructure.cpp @@ -87,13 +87,13 @@ template void linearStructure(const MatrixType& m) void test_linearstructure() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( linearStructure(Matrix()) ); - CALL_SUBTEST( linearStructure(Matrix2f()) ); - CALL_SUBTEST( linearStructure(Vector3d()) ); - CALL_SUBTEST( linearStructure(Matrix4d()) ); - CALL_SUBTEST( linearStructure(MatrixXcf(3, 3)) ); - CALL_SUBTEST( linearStructure(MatrixXf(8, 12)) ); - CALL_SUBTEST( linearStructure(MatrixXi(8, 12)) ); - CALL_SUBTEST( linearStructure(MatrixXcd(20, 20)) ); + CALL_SUBTEST_1( linearStructure(Matrix()) ); + CALL_SUBTEST_2( linearStructure(Matrix2f()) ); + CALL_SUBTEST_3( linearStructure(Vector3d()) ); + CALL_SUBTEST_4( linearStructure(Matrix4d()) ); + CALL_SUBTEST_5( linearStructure(MatrixXcf(3, 3)) ); + CALL_SUBTEST_6( linearStructure(MatrixXf(8, 12)) ); + CALL_SUBTEST_7( linearStructure(MatrixXi(8, 12)) ); + CALL_SUBTEST_8( linearStructure(MatrixXcd(20, 20)) ); } } diff --git a/test/lu.cpp b/test/lu.cpp index 1813172cd..b7214ae12 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -58,13 +58,13 @@ template void lu_non_invertible() int rank = ei_random(1, std::min(rows, cols)-1); // The image of the zero matrix should consist of a single (zero) column vector - VERIFY((MatrixType::Zero(rows,cols).lu().image(MatrixType::Zero(rows,cols)).cols() == 1)); + VERIFY((MatrixType::Zero(rows,cols).fullPivLu().image(MatrixType::Zero(rows,cols)).cols() == 1)); MatrixType m1(rows, cols), m3(rows, cols2); CMatrixType m2(cols, cols2); createRandomMatrixOfRank(rank, rows, cols, m1); - LU lu(m1); + FullPivLU lu(m1); KernelMatrixType m1kernel = lu.kernel(); ImageMatrixType m1image = lu.image(m1); @@ -75,20 +75,16 @@ template void lu_non_invertible() VERIFY(!lu.isInvertible()); VERIFY(!lu.isSurjective()); VERIFY((m1 * m1kernel).isMuchSmallerThan(m1)); - VERIFY(m1image.lu().rank() == rank); + VERIFY(m1image.fullPivLu().rank() == rank); DynamicMatrixType sidebyside(m1.rows(), m1.cols() + m1image.cols()); sidebyside << m1, m1image; - VERIFY(sidebyside.lu().rank() == rank); + VERIFY(sidebyside.fullPivLu().rank() == rank); m2 = CMatrixType::Random(cols,cols2); m3 = m1*m2; m2 = CMatrixType::Random(cols,cols2); // test that the code, which does resize(), may be applied to an xpr m2.block(0,0,m2.rows(),m2.cols()) = lu.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); - - CMatrixType m4(cols, cols), m5(cols, cols); - createRandomMatrixOfRank(cols/2, cols, cols, m4); - VERIFY(!m4.computeInverseWithCheck(&m5)); } template void lu_invertible() @@ -109,14 +105,14 @@ template void lu_invertible() m1 += a * a.adjoint(); } - LU lu(m1); + FullPivLU lu(m1); VERIFY(0 == lu.dimensionOfKernel()); VERIFY(lu.kernel().cols() == 1); // the kernel() should consist of a single (zero) column vector VERIFY(size == lu.rank()); VERIFY(lu.isInjective()); VERIFY(lu.isSurjective()); VERIFY(lu.isInvertible()); - VERIFY(lu.image(m1).lu().isInvertible()); + VERIFY(lu.image(m1).fullPivLu().isInvertible()); m3 = MatrixType::Random(size,size); m2 = lu.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); @@ -127,7 +123,7 @@ template void lu_verify_assert() { MatrixType tmp; - LU lu; + FullPivLU lu; VERIFY_RAISES_ASSERT(lu.matrixLU()) VERIFY_RAISES_ASSERT(lu.permutationP()) VERIFY_RAISES_ASSERT(lu.permutationQ()) @@ -142,10 +138,10 @@ template void lu_verify_assert() VERIFY_RAISES_ASSERT(lu.isInvertible()) VERIFY_RAISES_ASSERT(lu.inverse()) - PartialLU plu; + PartialPivLU plu; VERIFY_RAISES_ASSERT(plu.matrixLU()) VERIFY_RAISES_ASSERT(plu.permutationP()) - VERIFY_RAISES_ASSERT(plu.solve(tmp,&tmp)) + VERIFY_RAISES_ASSERT(plu.solve(tmp)) VERIFY_RAISES_ASSERT(plu.determinant()) VERIFY_RAISES_ASSERT(plu.inverse()) } @@ -153,26 +149,26 @@ template void lu_verify_assert() void test_lu() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST1( lu_non_invertible() ); - CALL_SUBTEST1( lu_verify_assert() ); + CALL_SUBTEST_1( lu_non_invertible() ); + CALL_SUBTEST_1( lu_verify_assert() ); - CALL_SUBTEST2( (lu_non_invertible >()) ); - CALL_SUBTEST2( (lu_verify_assert >()) ); + CALL_SUBTEST_2( (lu_non_invertible >()) ); + CALL_SUBTEST_2( (lu_verify_assert >()) ); - CALL_SUBTEST3( lu_non_invertible() ); - CALL_SUBTEST3( lu_invertible() ); - CALL_SUBTEST3( lu_verify_assert() ); + CALL_SUBTEST_3( lu_non_invertible() ); + CALL_SUBTEST_3( lu_invertible() ); + CALL_SUBTEST_3( lu_verify_assert() ); - CALL_SUBTEST4( lu_non_invertible() ); - CALL_SUBTEST4( lu_invertible() ); - CALL_SUBTEST4( lu_verify_assert() ); + CALL_SUBTEST_4( lu_non_invertible() ); + CALL_SUBTEST_4( lu_invertible() ); + CALL_SUBTEST_4( lu_verify_assert() ); - CALL_SUBTEST5( lu_non_invertible() ); - CALL_SUBTEST5( lu_invertible() ); - CALL_SUBTEST5( lu_verify_assert() ); + CALL_SUBTEST_5( lu_non_invertible() ); + CALL_SUBTEST_5( lu_invertible() ); + CALL_SUBTEST_5( lu_verify_assert() ); - CALL_SUBTEST6( lu_non_invertible() ); - CALL_SUBTEST6( lu_invertible() ); - CALL_SUBTEST6( lu_verify_assert() ); + CALL_SUBTEST_6( lu_non_invertible() ); + CALL_SUBTEST_6( lu_invertible() ); + CALL_SUBTEST_6( lu_verify_assert() ); } } diff --git a/test/main.h b/test/main.h index 15ae3d645..0b9b0bc4c 100644 --- a/test/main.h +++ b/test/main.h @@ -171,63 +171,99 @@ namespace Eigen } while (0) #ifdef EIGEN_TEST_PART_1 -#define CALL_SUBTEST1(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_1(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST1(FUNC) +#define CALL_SUBTEST_1(FUNC) #endif #ifdef EIGEN_TEST_PART_2 -#define CALL_SUBTEST2(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_2(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST2(FUNC) +#define CALL_SUBTEST_2(FUNC) #endif #ifdef EIGEN_TEST_PART_3 -#define CALL_SUBTEST3(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_3(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST3(FUNC) +#define CALL_SUBTEST_3(FUNC) #endif #ifdef EIGEN_TEST_PART_4 -#define CALL_SUBTEST4(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_4(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST4(FUNC) +#define CALL_SUBTEST_4(FUNC) #endif #ifdef EIGEN_TEST_PART_5 -#define CALL_SUBTEST5(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_5(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST5(FUNC) +#define CALL_SUBTEST_5(FUNC) #endif #ifdef EIGEN_TEST_PART_6 -#define CALL_SUBTEST6(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_6(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST6(FUNC) +#define CALL_SUBTEST_6(FUNC) #endif #ifdef EIGEN_TEST_PART_7 -#define CALL_SUBTEST7(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_7(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST7(FUNC) +#define CALL_SUBTEST_7(FUNC) #endif #ifdef EIGEN_TEST_PART_8 -#define CALL_SUBTEST8(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_8(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST8(FUNC) +#define CALL_SUBTEST_8(FUNC) #endif #ifdef EIGEN_TEST_PART_9 -#define CALL_SUBTEST9(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_9(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST9(FUNC) +#define CALL_SUBTEST_9(FUNC) #endif #ifdef EIGEN_TEST_PART_10 -#define CALL_SUBTEST10(FUNC) CALL_SUBTEST(FUNC) +#define CALL_SUBTEST_10(FUNC) CALL_SUBTEST(FUNC) #else -#define CALL_SUBTEST10(FUNC) +#define CALL_SUBTEST_10(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_11 +#define CALL_SUBTEST_11(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_11(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_12 +#define CALL_SUBTEST_12(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_12(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_13 +#define CALL_SUBTEST_13(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_13(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_14 +#define CALL_SUBTEST_14(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_14(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_15 +#define CALL_SUBTEST_15(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_15(FUNC) +#endif + +#ifdef EIGEN_TEST_PART_16 +#define CALL_SUBTEST_16(FUNC) CALL_SUBTEST(FUNC) +#else +#define CALL_SUBTEST_16(FUNC) #endif namespace Eigen { diff --git a/test/map.cpp b/test/map.cpp index 62e727304..5c0ec3137 100644 --- a/test/map.cpp +++ b/test/map.cpp @@ -80,16 +80,16 @@ template void map_static_methods(const VectorType& m) void test_map() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( map_class(Matrix()) ); - CALL_SUBTEST( map_class(Vector4d()) ); - CALL_SUBTEST( map_class(RowVector4f()) ); - CALL_SUBTEST( map_class(VectorXcf(8)) ); - CALL_SUBTEST( map_class(VectorXi(12)) ); + CALL_SUBTEST_1( map_class(Matrix()) ); + CALL_SUBTEST_2( map_class(Vector4d()) ); + CALL_SUBTEST_3( map_class(RowVector4f()) ); + CALL_SUBTEST_4( map_class(VectorXcf(8)) ); + CALL_SUBTEST_5( map_class(VectorXi(12)) ); - CALL_SUBTEST( map_static_methods(Matrix()) ); - CALL_SUBTEST( map_static_methods(Vector3f()) ); - CALL_SUBTEST( map_static_methods(RowVector3d()) ); - CALL_SUBTEST( map_static_methods(VectorXcd(8)) ); - CALL_SUBTEST( map_static_methods(VectorXf(12)) ); + CALL_SUBTEST_6( map_static_methods(Matrix()) ); + CALL_SUBTEST_7( map_static_methods(Vector3f()) ); + CALL_SUBTEST_8( map_static_methods(RowVector3d()) ); + CALL_SUBTEST_9( map_static_methods(VectorXcd(8)) ); + CALL_SUBTEST_10( map_static_methods(VectorXf(12)) ); } } diff --git a/test/miscmatrices.cpp b/test/miscmatrices.cpp index bf885e252..0adccf5ce 100644 --- a/test/miscmatrices.cpp +++ b/test/miscmatrices.cpp @@ -54,10 +54,10 @@ template void miscMatrices(const MatrixType& m) void test_miscmatrices() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( miscMatrices(Matrix()) ); - CALL_SUBTEST( miscMatrices(Matrix4d()) ); - CALL_SUBTEST( miscMatrices(MatrixXcf(3, 3)) ); - CALL_SUBTEST( miscMatrices(MatrixXi(8, 12)) ); - CALL_SUBTEST( miscMatrices(MatrixXcd(20, 20)) ); + CALL_SUBTEST_1( miscMatrices(Matrix()) ); + CALL_SUBTEST_2( miscMatrices(Matrix4d()) ); + CALL_SUBTEST_3( miscMatrices(MatrixXcf(3, 3)) ); + CALL_SUBTEST_4( miscMatrices(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( miscMatrices(MatrixXcd(20, 20)) ); } } diff --git a/test/mixingtypes.cpp b/test/mixingtypes.cpp index 3e322c7fe..1dcd77a8a 100644 --- a/test/mixingtypes.cpp +++ b/test/mixingtypes.cpp @@ -174,10 +174,10 @@ template void mixingtypes_small() void test_mixingtypes() { // check that our operator new is indeed called: - CALL_SUBTEST(mixingtypes<3>()); - CALL_SUBTEST(mixingtypes<4>()); - CALL_SUBTEST(mixingtypes(20)); + CALL_SUBTEST_1(mixingtypes<3>()); + CALL_SUBTEST_2(mixingtypes<4>()); + CALL_SUBTEST_3(mixingtypes(20)); - CALL_SUBTEST(mixingtypes_small<4>()); - CALL_SUBTEST(mixingtypes_large(20)); + CALL_SUBTEST_4(mixingtypes_small<4>()); + CALL_SUBTEST_5(mixingtypes_large(20)); } diff --git a/test/nomalloc.cpp b/test/nomalloc.cpp index a96bb23da..1a917192b 100644 --- a/test/nomalloc.cpp +++ b/test/nomalloc.cpp @@ -77,7 +77,7 @@ void test_nomalloc() { // check that our operator new is indeed called: VERIFY_RAISES_ASSERT(MatrixXd dummy = MatrixXd::Random(3,3)); - CALL_SUBTEST( nomalloc(Matrix()) ); - CALL_SUBTEST( nomalloc(Matrix4d()) ); - CALL_SUBTEST( nomalloc(Matrix()) ); + CALL_SUBTEST(nomalloc(Matrix()) ); + CALL_SUBTEST(nomalloc(Matrix4d()) ); + CALL_SUBTEST(nomalloc(Matrix()) ); } diff --git a/test/packetmath.cpp b/test/packetmath.cpp index 1745ae5c6..7d863e616 100644 --- a/test/packetmath.cpp +++ b/test/packetmath.cpp @@ -233,12 +233,12 @@ template void packetmath_real() void test_packetmath() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( packetmath() ); - CALL_SUBTEST( packetmath() ); - CALL_SUBTEST( packetmath() ); - CALL_SUBTEST( packetmath >() ); + CALL_SUBTEST_1( packetmath() ); + CALL_SUBTEST_2( packetmath() ); + CALL_SUBTEST_3( packetmath() ); + CALL_SUBTEST_1( packetmath >() ); - CALL_SUBTEST( packetmath_real() ); - CALL_SUBTEST( packetmath_real() ); + CALL_SUBTEST_1( packetmath_real() ); + CALL_SUBTEST_2( packetmath_real() ); } } diff --git a/test/product_extra.cpp b/test/product_extra.cpp index aeaf04d83..e4c853461 100644 --- a/test/product_extra.cpp +++ b/test/product_extra.cpp @@ -119,8 +119,8 @@ template void product_extra(const MatrixType& m) void test_product_extra() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST1( product_extra(MatrixXf(ei_random(2,320), ei_random(2,320))) ); - CALL_SUBTEST2( product_extra(MatrixXcf(ei_random(50,50), ei_random(50,50))) ); - CALL_SUBTEST3( product_extra(Matrix,Dynamic,Dynamic,RowMajor>(ei_random(2,50), ei_random(2,50))) ); + CALL_SUBTEST_1( product_extra(MatrixXf(ei_random(2,320), ei_random(2,320))) ); + CALL_SUBTEST_2( product_extra(MatrixXcf(ei_random(50,50), ei_random(50,50))) ); + CALL_SUBTEST_3( product_extra(Matrix,Dynamic,Dynamic,RowMajor>(ei_random(2,50), ei_random(2,50))) ); } } diff --git a/test/product_large.cpp b/test/product_large.cpp index a64775f6c..519213236 100644 --- a/test/product_large.cpp +++ b/test/product_large.cpp @@ -27,11 +27,11 @@ void test_product_large() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST1( product(MatrixXf(ei_random(1,320), ei_random(1,320))) ); - CALL_SUBTEST2( product(MatrixXd(ei_random(1,320), ei_random(1,320))) ); - CALL_SUBTEST3( product(MatrixXi(ei_random(1,320), ei_random(1,320))) ); - CALL_SUBTEST4( product(MatrixXcf(ei_random(1,50), ei_random(1,50))) ); - CALL_SUBTEST5( product(Matrix(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST_1( product(MatrixXf(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST_2( product(MatrixXd(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST_3( product(MatrixXi(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST_4( product(MatrixXcf(ei_random(1,50), ei_random(1,50))) ); + CALL_SUBTEST_5( product(Matrix(ei_random(1,320), ei_random(1,320))) ); } #if defined EIGEN_TEST_PART_6 diff --git a/test/product_notemporary.cpp b/test/product_notemporary.cpp index e9efeaaae..e592afff7 100644 --- a/test/product_notemporary.cpp +++ b/test/product_notemporary.cpp @@ -117,8 +117,8 @@ void test_product_notemporary() int s; for(int i = 0; i < g_repeat; i++) { s = ei_random(16,320); - CALL_SUBTEST1( product_notemporary(MatrixXf(s, s)) ); + CALL_SUBTEST_1( product_notemporary(MatrixXf(s, s)) ); s = ei_random(16,120); - CALL_SUBTEST2( product_notemporary(MatrixXcd(s,s)) ); + CALL_SUBTEST_2( product_notemporary(MatrixXcd(s,s)) ); } } diff --git a/test/product_selfadjoint.cpp b/test/product_selfadjoint.cpp index 065d4ad8c..aa8da37bd 100644 --- a/test/product_selfadjoint.cpp +++ b/test/product_selfadjoint.cpp @@ -78,13 +78,13 @@ template void product_selfadjoint(const MatrixType& m) void test_product_selfadjoint() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST1( product_selfadjoint(Matrix()) ); - CALL_SUBTEST2( product_selfadjoint(Matrix()) ); - CALL_SUBTEST3( product_selfadjoint(Matrix3d()) ); - CALL_SUBTEST4( product_selfadjoint(MatrixXcf(4, 4)) ); - CALL_SUBTEST5( product_selfadjoint(MatrixXcd(21,21)) ); - CALL_SUBTEST6( product_selfadjoint(MatrixXd(14,14)) ); - CALL_SUBTEST7( product_selfadjoint(Matrix(17,17)) ); - CALL_SUBTEST8( product_selfadjoint(Matrix,Dynamic,Dynamic,RowMajor>(19, 19)) ); + CALL_SUBTEST_1( product_selfadjoint(Matrix()) ); + CALL_SUBTEST_2( product_selfadjoint(Matrix()) ); + CALL_SUBTEST_3( product_selfadjoint(Matrix3d()) ); + CALL_SUBTEST_4( product_selfadjoint(MatrixXcf(4, 4)) ); + CALL_SUBTEST_5( product_selfadjoint(MatrixXcd(21,21)) ); + CALL_SUBTEST_6( product_selfadjoint(MatrixXd(14,14)) ); + CALL_SUBTEST_7( product_selfadjoint(Matrix(17,17)) ); + CALL_SUBTEST_8( product_selfadjoint(Matrix,Dynamic,Dynamic,RowMajor>(19, 19)) ); } } diff --git a/test/product_small.cpp b/test/product_small.cpp index 3a667a5dc..d7f1c09ff 100644 --- a/test/product_small.cpp +++ b/test/product_small.cpp @@ -28,11 +28,11 @@ void test_product_small() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST1( product(Matrix()) ); - CALL_SUBTEST2( product(Matrix()) ); - CALL_SUBTEST3( product(Matrix3d()) ); - CALL_SUBTEST4( product(Matrix4d()) ); - CALL_SUBTEST5( product(Matrix4f()) ); + CALL_SUBTEST_1( product(Matrix()) ); + CALL_SUBTEST_2( product(Matrix()) ); + CALL_SUBTEST_3( product(Matrix3d()) ); + CALL_SUBTEST_4( product(Matrix4d()) ); + CALL_SUBTEST_5( product(Matrix4f()) ); } #ifdef EIGEN_TEST_PART_6 diff --git a/test/product_symm.cpp b/test/product_symm.cpp index ecd46c78e..a9e055bd3 100644 --- a/test/product_symm.cpp +++ b/test/product_symm.cpp @@ -108,10 +108,10 @@ void test_product_symm() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST1(( symm(ei_random(10,320),ei_random(10,320)) )); - CALL_SUBTEST2(( symm,Dynamic,Dynamic>(ei_random(10,320),ei_random(10,320)) )); + CALL_SUBTEST_1(( symm(ei_random(10,320),ei_random(10,320)) )); + CALL_SUBTEST_2(( symm,Dynamic,Dynamic>(ei_random(10,320),ei_random(10,320)) )); - CALL_SUBTEST3(( symm(ei_random(10,320)) )); - CALL_SUBTEST4(( symm,Dynamic,1>(ei_random(10,320)) )); + CALL_SUBTEST_3(( symm(ei_random(10,320)) )); + CALL_SUBTEST_4(( symm,Dynamic,1>(ei_random(10,320)) )); } } diff --git a/test/product_syrk.cpp b/test/product_syrk.cpp index 78b67bb1a..37d54bf16 100644 --- a/test/product_syrk.cpp +++ b/test/product_syrk.cpp @@ -75,8 +75,8 @@ void test_product_syrk() { int s; s = ei_random(10,320); - CALL_SUBTEST1( syrk(MatrixXf(s, s)) ); + CALL_SUBTEST_1( syrk(MatrixXf(s, s)) ); s = ei_random(10,320); - CALL_SUBTEST2( syrk(MatrixXcd(s, s)) ); + CALL_SUBTEST_2( syrk(MatrixXcd(s, s)) ); } } diff --git a/test/product_trmm.cpp b/test/product_trmm.cpp index c3234ba7e..5f92391e6 100644 --- a/test/product_trmm.cpp +++ b/test/product_trmm.cpp @@ -63,7 +63,7 @@ void test_product_trmm() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST1((trmm(ei_random(1,320),ei_random(1,320)))); - CALL_SUBTEST2((trmm >(ei_random(1,320),ei_random(1,320)))); + CALL_SUBTEST_1((trmm(ei_random(1,320),ei_random(1,320)))); + CALL_SUBTEST_2((trmm >(ei_random(1,320),ei_random(1,320)))); } } diff --git a/test/product_trmv.cpp b/test/product_trmv.cpp index 602cdca03..5016a5b1f 100644 --- a/test/product_trmv.cpp +++ b/test/product_trmv.cpp @@ -82,11 +82,11 @@ template void trmv(const MatrixType& m) void test_product_trmv() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST1( trmv(Matrix()) ); - CALL_SUBTEST2( trmv(Matrix()) ); - CALL_SUBTEST3( trmv(Matrix3d()) ); - CALL_SUBTEST4( trmv(Matrix,23, 23>()) ); - CALL_SUBTEST5( trmv(MatrixXcd(17,17)) ); - CALL_SUBTEST6( trmv(Matrix(19, 19)) ); + CALL_SUBTEST_1( trmv(Matrix()) ); + CALL_SUBTEST_2( trmv(Matrix()) ); + CALL_SUBTEST_3( trmv(Matrix3d()) ); + CALL_SUBTEST_4( trmv(Matrix,23, 23>()) ); + CALL_SUBTEST_5( trmv(MatrixXcd(17,17)) ); + CALL_SUBTEST_6( trmv(Matrix(19, 19)) ); } } diff --git a/test/product_trsm.cpp b/test/product_trsm.cpp index 2bd2e5e02..f850e031a 100644 --- a/test/product_trsm.cpp +++ b/test/product_trsm.cpp @@ -59,7 +59,7 @@ void test_product_trsm() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST1((trsm(ei_random(1,320),ei_random(1,320)))); - CALL_SUBTEST2((trsm >(ei_random(1,320),ei_random(1,320)))); + CALL_SUBTEST_1((trsm(ei_random(1,320),ei_random(1,320)))); + CALL_SUBTEST_2((trsm >(ei_random(1,320),ei_random(1,320)))); } } diff --git a/test/qr.cpp b/test/qr.cpp index 864828750..cbb23c4ca 100644 --- a/test/qr.cpp +++ b/test/qr.cpp @@ -115,24 +115,24 @@ template void qr_verify_assert() void test_qr() { for(int i = 0; i < 1; i++) { - CALL_SUBTEST( qr(MatrixXf(47,40)) ); - CALL_SUBTEST( qr(MatrixXcd(17,7)) ); - CALL_SUBTEST(( qr_fixedsize, 2 >() )); - CALL_SUBTEST(( qr_fixedsize, 4 >() )); - CALL_SUBTEST(( qr_fixedsize, 7 >() )); + CALL_SUBTEST_1( qr(MatrixXf(47,40)) ); + CALL_SUBTEST_2( qr(MatrixXcd(17,7)) ); + CALL_SUBTEST_3(( qr_fixedsize, 2 >() )); + CALL_SUBTEST_4(( qr_fixedsize, 4 >() )); + CALL_SUBTEST_5(( qr_fixedsize, 7 >() )); } for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); + CALL_SUBTEST_1( qr_invertible() ); + CALL_SUBTEST_6( qr_invertible() ); + CALL_SUBTEST_7( qr_invertible() ); + CALL_SUBTEST_8( qr_invertible() ); } - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); + CALL_SUBTEST_9(qr_verify_assert()); + CALL_SUBTEST_10(qr_verify_assert()); + CALL_SUBTEST_1(qr_verify_assert()); + CALL_SUBTEST_6(qr_verify_assert()); + CALL_SUBTEST_7(qr_verify_assert()); + CALL_SUBTEST_8(qr_verify_assert()); } diff --git a/test/qr_colpivoting.cpp b/test/qr_colpivoting.cpp index 5c5c5d259..406be597d 100644 --- a/test/qr_colpivoting.cpp +++ b/test/qr_colpivoting.cpp @@ -36,7 +36,7 @@ template void qr() typedef Matrix VectorType; MatrixType m1; createRandomMatrixOfRank(rank,rows,cols,m1); - ColPivotingHouseholderQR qr(m1); + ColPivHouseholderQR qr(m1); VERIFY_IS_APPROX(rank, qr.rank()); VERIFY(cols - qr.rank() == qr.dimensionOfKernel()); VERIFY(!qr.isInjective()); @@ -74,7 +74,7 @@ template void qr_fixedsize() int rank = ei_random(1, std::min(int(Rows), int(Cols))-1); Matrix m1; createRandomMatrixOfRank(rank,Rows,Cols,m1); - ColPivotingHouseholderQR > qr(m1); + ColPivHouseholderQR > qr(m1); VERIFY_IS_APPROX(rank, qr.rank()); VERIFY(Cols - qr.rank() == qr.dimensionOfKernel()); VERIFY(!qr.isInjective()); @@ -118,7 +118,7 @@ template void qr_invertible() m1 += a * a.adjoint(); } - ColPivotingHouseholderQR qr(m1); + ColPivHouseholderQR qr(m1); m3 = MatrixType::Random(size,size); qr.solve(m3, &m2); VERIFY_IS_APPROX(m3, m1*m2); @@ -138,7 +138,7 @@ template void qr_verify_assert() { MatrixType tmp; - ColPivotingHouseholderQR qr; + ColPivHouseholderQR qr; VERIFY_RAISES_ASSERT(qr.matrixQR()) VERIFY_RAISES_ASSERT(qr.solve(tmp,&tmp)) VERIFY_RAISES_ASSERT(qr.matrixQ()) @@ -155,24 +155,24 @@ template void qr_verify_assert() void test_qr_colpivoting() { for(int i = 0; i < 1; i++) { - CALL_SUBTEST( qr() ); - CALL_SUBTEST( qr() ); - CALL_SUBTEST( qr() ); - CALL_SUBTEST(( qr_fixedsize, 4 >() )); - CALL_SUBTEST(( qr_fixedsize, 3 >() )); + CALL_SUBTEST_1( qr() ); + CALL_SUBTEST_2( qr() ); + CALL_SUBTEST_3( qr() ); + CALL_SUBTEST_4(( qr_fixedsize, 4 >() )); + CALL_SUBTEST_5(( qr_fixedsize, 3 >() )); } for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); + CALL_SUBTEST_1( qr_invertible() ); + CALL_SUBTEST_2( qr_invertible() ); + CALL_SUBTEST_6( qr_invertible() ); + CALL_SUBTEST_3( qr_invertible() ); } - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); + CALL_SUBTEST_7(qr_verify_assert()); + CALL_SUBTEST_8(qr_verify_assert()); + CALL_SUBTEST_1(qr_verify_assert()); + CALL_SUBTEST_2(qr_verify_assert()); + CALL_SUBTEST_6(qr_verify_assert()); + CALL_SUBTEST_3(qr_verify_assert()); } diff --git a/test/qr_fullpivoting.cpp b/test/qr_fullpivoting.cpp index 891c2a527..38ee4eac1 100644 --- a/test/qr_fullpivoting.cpp +++ b/test/qr_fullpivoting.cpp @@ -36,7 +36,7 @@ template void qr() typedef Matrix VectorType; MatrixType m1; createRandomMatrixOfRank(rank,rows,cols,m1); - FullPivotingHouseholderQR qr(m1); + FullPivHouseholderQR qr(m1); VERIFY_IS_APPROX(rank, qr.rank()); VERIFY(cols - qr.rank() == qr.dimensionOfKernel()); VERIFY(!qr.isInjective()); @@ -84,7 +84,7 @@ template void qr_invertible() m1 += a * a.adjoint(); } - FullPivotingHouseholderQR qr(m1); + FullPivHouseholderQR qr(m1); VERIFY(qr.isInjective()); VERIFY(qr.isInvertible()); VERIFY(qr.isSurjective()); @@ -108,7 +108,7 @@ template void qr_verify_assert() { MatrixType tmp; - FullPivotingHouseholderQR qr; + FullPivHouseholderQR qr; VERIFY_RAISES_ASSERT(qr.matrixQR()) VERIFY_RAISES_ASSERT(qr.solve(tmp,&tmp)) VERIFY_RAISES_ASSERT(qr.matrixQ()) @@ -126,23 +126,23 @@ void test_qr_fullpivoting() { for(int i = 0; i < 1; i++) { // FIXME : very weird bug here -// CALL_SUBTEST( qr(Matrix2f()) ); - CALL_SUBTEST( qr() ); - CALL_SUBTEST( qr() ); - CALL_SUBTEST( qr() ); +// CALL_SUBTEST(qr(Matrix2f()) ); + CALL_SUBTEST_1( qr() ); + CALL_SUBTEST_2( qr() ); + CALL_SUBTEST_3( qr() ); } for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); - CALL_SUBTEST( qr_invertible() ); + CALL_SUBTEST_1( qr_invertible() ); + CALL_SUBTEST_2( qr_invertible() ); + CALL_SUBTEST_4( qr_invertible() ); + CALL_SUBTEST_3( qr_invertible() ); } - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); - CALL_SUBTEST(qr_verify_assert()); + CALL_SUBTEST_5(qr_verify_assert()); + CALL_SUBTEST_6(qr_verify_assert()); + CALL_SUBTEST_1(qr_verify_assert()); + CALL_SUBTEST_2(qr_verify_assert()); + CALL_SUBTEST_4(qr_verify_assert()); + CALL_SUBTEST_3(qr_verify_assert()); } diff --git a/test/redux.cpp b/test/redux.cpp index 951b34bca..c075c1393 100644 --- a/test/redux.cpp +++ b/test/redux.cpp @@ -112,16 +112,16 @@ template void vectorRedux(const VectorType& w) void test_redux() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( matrixRedux(Matrix()) ); - CALL_SUBTEST( matrixRedux(Matrix2f()) ); - CALL_SUBTEST( matrixRedux(Matrix4d()) ); - CALL_SUBTEST( matrixRedux(MatrixXcf(3, 3)) ); - CALL_SUBTEST( matrixRedux(MatrixXd(8, 12)) ); - CALL_SUBTEST( matrixRedux(MatrixXi(8, 12)) ); + CALL_SUBTEST_1( matrixRedux(Matrix()) ); + CALL_SUBTEST_2( matrixRedux(Matrix2f()) ); + CALL_SUBTEST_3( matrixRedux(Matrix4d()) ); + CALL_SUBTEST_4( matrixRedux(MatrixXcf(3, 3)) ); + CALL_SUBTEST_5( matrixRedux(MatrixXd(8, 12)) ); + CALL_SUBTEST_6( matrixRedux(MatrixXi(8, 12)) ); } for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( vectorRedux(Vector4f()) ); - CALL_SUBTEST( vectorRedux(VectorXd(10)) ); - CALL_SUBTEST( vectorRedux(VectorXf(33)) ); + CALL_SUBTEST_7( vectorRedux(Vector4f()) ); + CALL_SUBTEST_5( vectorRedux(VectorXd(10)) ); + CALL_SUBTEST_8( vectorRedux(VectorXf(33)) ); } } diff --git a/test/regression.cpp b/test/regression.cpp index 28b6356b3..0e2323bf6 100644 --- a/test/regression.cpp +++ b/test/regression.cpp @@ -95,6 +95,7 @@ void test_regression() { for(int i = 0; i < g_repeat; i++) { +#ifdef EIGEN_TEST_PART_1 { Vector2f points2f [1000]; Vector2f *points2f_ptrs [1000]; @@ -108,7 +109,9 @@ void test_regression() CALL_SUBTEST(check_linearRegression(100, points2f_ptrs, coeffs2f, 0.01f)); CALL_SUBTEST(check_linearRegression(1000, points2f_ptrs, coeffs2f, 0.002f)); } +#endif +#ifdef EIGEN_TEST_PART_2 { Vector2f points2f [1000]; Vector2f *points2f_ptrs [1000]; @@ -119,7 +122,9 @@ void test_regression() CALL_SUBTEST(check_fitHyperplane(100, points2f_ptrs, coeffs3f, 0.01f)); CALL_SUBTEST(check_fitHyperplane(1000, points2f_ptrs, coeffs3f, 0.002f)); } +#endif +#ifdef EIGEN_TEST_PART_3 { Vector4d points4d [1000]; Vector4d *points4d_ptrs [1000]; @@ -130,7 +135,9 @@ void test_regression() CALL_SUBTEST(check_fitHyperplane(100, points4d_ptrs, coeffs5d, 0.01)); CALL_SUBTEST(check_fitHyperplane(1000, points4d_ptrs, coeffs5d, 0.002)); } +#endif +#ifdef EIGEN_TEST_PART_4 { VectorXcd *points11cd_ptrs[1000]; for(int i = 0; i < 1000; i++) points11cd_ptrs[i] = new VectorXcd(11); @@ -141,5 +148,6 @@ void test_regression() delete coeffs12cd; for(int i = 0; i < 1000; i++) delete points11cd_ptrs[i]; } +#endif } } diff --git a/test/resize.cpp b/test/resize.cpp index 3c8f21ee1..dfe3bda17 100644 --- a/test/resize.cpp +++ b/test/resize.cpp @@ -50,7 +50,7 @@ void resizeLikeTest31() { resizeLikeTest<3,1>(); } void test_resize() { - CALL_SUBTEST( resizeLikeTest12() ); - CALL_SUBTEST( resizeLikeTest1020() ); - CALL_SUBTEST( resizeLikeTest31() ); + CALL_SUBTEST(resizeLikeTest12() ); + CALL_SUBTEST(resizeLikeTest1020() ); + CALL_SUBTEST(resizeLikeTest31() ); } diff --git a/test/sizeof.cpp b/test/sizeof.cpp index 8dc9ca7ef..a7243591a 100644 --- a/test/sizeof.cpp +++ b/test/sizeof.cpp @@ -35,14 +35,14 @@ template void verifySizeOf(const MatrixType&) void test_sizeof() { - CALL_SUBTEST( verifySizeOf(Matrix()) ); - CALL_SUBTEST( verifySizeOf(Matrix4d()) ); - CALL_SUBTEST( verifySizeOf(Matrix()) ); - CALL_SUBTEST( verifySizeOf(Matrix()) ); - CALL_SUBTEST( verifySizeOf(MatrixXcf(3, 3)) ); - CALL_SUBTEST( verifySizeOf(MatrixXi(8, 12)) ); - CALL_SUBTEST( verifySizeOf(MatrixXcd(20, 20)) ); - CALL_SUBTEST( verifySizeOf(Matrix()) ); + CALL_SUBTEST(verifySizeOf(Matrix()) ); + CALL_SUBTEST(verifySizeOf(Matrix4d()) ); + CALL_SUBTEST(verifySizeOf(Matrix()) ); + CALL_SUBTEST(verifySizeOf(Matrix()) ); + CALL_SUBTEST(verifySizeOf(MatrixXcf(3, 3)) ); + CALL_SUBTEST(verifySizeOf(MatrixXi(8, 12)) ); + CALL_SUBTEST(verifySizeOf(MatrixXcd(20, 20)) ); + CALL_SUBTEST(verifySizeOf(Matrix()) ); VERIFY(sizeof(std::complex) == 2*sizeof(float)); VERIFY(sizeof(std::complex) == 2*sizeof(double)); diff --git a/test/smallvectors.cpp b/test/smallvectors.cpp index f0807c796..d6dc8e97c 100644 --- a/test/smallvectors.cpp +++ b/test/smallvectors.cpp @@ -50,8 +50,8 @@ template void smallVectors() void test_smallvectors() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( smallVectors() ); - CALL_SUBTEST( smallVectors() ); - CALL_SUBTEST( smallVectors() ); + CALL_SUBTEST(smallVectors() ); + CALL_SUBTEST(smallVectors() ); + CALL_SUBTEST(smallVectors() ); } } diff --git a/test/sparse_basic.cpp b/test/sparse_basic.cpp index 666eea872..050b14995 100644 --- a/test/sparse_basic.cpp +++ b/test/sparse_basic.cpp @@ -344,10 +344,10 @@ template void sparse_basic(const SparseMatrixType& re void test_sparse_basic() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( sparse_basic(SparseMatrix(8, 8)) ); - CALL_SUBTEST( sparse_basic(SparseMatrix >(16, 16)) ); - CALL_SUBTEST( sparse_basic(SparseMatrix(33, 33)) ); + CALL_SUBTEST_1( sparse_basic(SparseMatrix(8, 8)) ); + CALL_SUBTEST_2( sparse_basic(SparseMatrix >(16, 16)) ); + CALL_SUBTEST_1( sparse_basic(SparseMatrix(33, 33)) ); - CALL_SUBTEST( sparse_basic(DynamicSparseMatrix(8, 8)) ); + CALL_SUBTEST_3( sparse_basic(DynamicSparseMatrix(8, 8)) ); } } diff --git a/test/sparse_product.cpp b/test/sparse_product.cpp index 743273f65..f2a4e8bb9 100644 --- a/test/sparse_product.cpp +++ b/test/sparse_product.cpp @@ -123,10 +123,10 @@ template void sparse_product(const SparseMatrixType& void test_sparse_product() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( sparse_product(SparseMatrix(8, 8)) ); - CALL_SUBTEST( sparse_product(SparseMatrix >(16, 16)) ); - CALL_SUBTEST( sparse_product(SparseMatrix(33, 33)) ); + CALL_SUBTEST_1( sparse_product(SparseMatrix(8, 8)) ); + CALL_SUBTEST_2( sparse_product(SparseMatrix >(16, 16)) ); + CALL_SUBTEST_1( sparse_product(SparseMatrix(33, 33)) ); - CALL_SUBTEST( sparse_product(DynamicSparseMatrix(8, 8)) ); + CALL_SUBTEST_3( sparse_product(DynamicSparseMatrix(8, 8)) ); } } diff --git a/test/sparse_solvers.cpp b/test/sparse_solvers.cpp index 09ae1ad60..6c1bb9b33 100644 --- a/test/sparse_solvers.cpp +++ b/test/sparse_solvers.cpp @@ -172,8 +172,8 @@ template void sparse_solvers(int rows, int cols) initSparse(density, refMat2, m2, ForceNonZeroDiag, &zeroCoords, &nonzeroCoords); - LU refLu(refMat2); - refLu.solve(b, &refX); + FullPivLU refLu(refMat2); + refX = refLu.solve(b); #if defined(EIGEN_SUPERLU_SUPPORT) || defined(EIGEN_UMFPACK_SUPPORT) Scalar refDet = refLu.determinant(); #endif @@ -229,8 +229,8 @@ template void sparse_solvers(int rows, int cols) void test_sparse_solvers() { for(int i = 0; i < g_repeat; i++) { -// CALL_SUBTEST( sparse_solvers(8, 8) ); - CALL_SUBTEST( sparse_solvers >(16, 16) ); -// CALL_SUBTEST( sparse_solvers(100, 100) ); +// CALL_SUBTEST(sparse_solvers(8, 8) ); + CALL_SUBTEST(sparse_solvers >(16, 16) ); +// CALL_SUBTEST(sparse_solvers(100, 100) ); } } diff --git a/test/sparse_vector.cpp b/test/sparse_vector.cpp index 236e14f1b..5c6dadc00 100644 --- a/test/sparse_vector.cpp +++ b/test/sparse_vector.cpp @@ -91,9 +91,9 @@ template void sparse_vector(int rows, int cols) void test_sparse_vector() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( sparse_vector(8, 8) ); - CALL_SUBTEST( sparse_vector >(16, 16) ); - CALL_SUBTEST( sparse_vector(299, 535) ); + CALL_SUBTEST_1( sparse_vector(8, 8) ); + CALL_SUBTEST_2( sparse_vector >(16, 16) ); + CALL_SUBTEST_1( sparse_vector(299, 535) ); } } diff --git a/test/stable_norm.cpp b/test/stable_norm.cpp index ed72bb7a7..7661fc893 100644 --- a/test/stable_norm.cpp +++ b/test/stable_norm.cpp @@ -84,10 +84,10 @@ template void stable_norm(const MatrixType& m) void test_stable_norm() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( stable_norm(Matrix()) ); - CALL_SUBTEST( stable_norm(Vector4d()) ); - CALL_SUBTEST( stable_norm(VectorXd(ei_random(10,2000))) ); - CALL_SUBTEST( stable_norm(VectorXf(ei_random(10,2000))) ); - CALL_SUBTEST( stable_norm(VectorXcd(ei_random(10,2000))) ); + CALL_SUBTEST_1( stable_norm(Matrix()) ); + CALL_SUBTEST_2( stable_norm(Vector4d()) ); + CALL_SUBTEST_3( stable_norm(VectorXd(ei_random(10,2000))) ); + CALL_SUBTEST_4( stable_norm(VectorXf(ei_random(10,2000))) ); + CALL_SUBTEST_5( stable_norm(VectorXcd(ei_random(10,2000))) ); } } diff --git a/test/stdvector.cpp b/test/stdvector.cpp index a976f0cf1..bdca826f4 100644 --- a/test/stdvector.cpp +++ b/test/stdvector.cpp @@ -135,29 +135,29 @@ void check_stdvector_quaternion(const QuaternionType&) void test_stdvector() { // some non vectorizable fixed sizes - CALL_SUBTEST(check_stdvector_matrix(Vector2f())); - CALL_SUBTEST(check_stdvector_matrix(Matrix3f())); - CALL_SUBTEST(check_stdvector_matrix(Matrix3d())); + CALL_SUBTEST_1(check_stdvector_matrix(Vector2f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix3f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix3d())); // some vectorizable fixed sizes - CALL_SUBTEST(check_stdvector_matrix(Matrix2f())); - CALL_SUBTEST(check_stdvector_matrix(Vector4f())); - CALL_SUBTEST(check_stdvector_matrix(Matrix4f())); - CALL_SUBTEST(check_stdvector_matrix(Matrix4d())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix2f())); + CALL_SUBTEST_1(check_stdvector_matrix(Vector4f())); + CALL_SUBTEST_1(check_stdvector_matrix(Matrix4f())); + CALL_SUBTEST_2(check_stdvector_matrix(Matrix4d())); // some dynamic sizes - CALL_SUBTEST(check_stdvector_matrix(MatrixXd(1,1))); - CALL_SUBTEST(check_stdvector_matrix(VectorXd(20))); - CALL_SUBTEST(check_stdvector_matrix(RowVectorXf(20))); - CALL_SUBTEST(check_stdvector_matrix(MatrixXcf(10,10))); + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXd(1,1))); + CALL_SUBTEST_3(check_stdvector_matrix(VectorXd(20))); + CALL_SUBTEST_3(check_stdvector_matrix(RowVectorXf(20))); + CALL_SUBTEST_3(check_stdvector_matrix(MatrixXcf(10,10))); // some Transform - CALL_SUBTEST(check_stdvector_transform(Transform2f())); - CALL_SUBTEST(check_stdvector_transform(Transform3f())); - CALL_SUBTEST(check_stdvector_transform(Transform3d())); - //CALL_SUBTEST(check_stdvector_transform(Transform4d())); + CALL_SUBTEST_4(check_stdvector_transform(Transform2f())); + CALL_SUBTEST_4(check_stdvector_transform(Transform3f())); + CALL_SUBTEST_4(check_stdvector_transform(Transform3d())); + //CALL_SUBTEST(heck_stdvector_transform(Transform4d())); // some Quaternion - CALL_SUBTEST(check_stdvector_quaternion(Quaternionf())); - CALL_SUBTEST(check_stdvector_quaternion(Quaterniond())); + CALL_SUBTEST_5(check_stdvector_quaternion(Quaternionf())); + CALL_SUBTEST_5(check_stdvector_quaternion(Quaterniond())); } diff --git a/test/submatrices.cpp b/test/submatrices.cpp index 6fe86c281..75b0fde4b 100644 --- a/test/submatrices.cpp +++ b/test/submatrices.cpp @@ -215,14 +215,14 @@ void data_and_stride(const MatrixType& m) void test_submatrices() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( submatrices(Matrix()) ); - CALL_SUBTEST( submatrices(Matrix4d()) ); - CALL_SUBTEST( submatrices(MatrixXcf(3, 3)) ); - CALL_SUBTEST( submatrices(MatrixXi(8, 12)) ); - CALL_SUBTEST( submatrices(MatrixXcd(20, 20)) ); - CALL_SUBTEST( submatrices(MatrixXf(20, 20)) ); + CALL_SUBTEST_1( submatrices(Matrix()) ); + CALL_SUBTEST_2( submatrices(Matrix4d()) ); + CALL_SUBTEST_3( submatrices(MatrixXcf(3, 3)) ); + CALL_SUBTEST_4( submatrices(MatrixXi(8, 12)) ); + CALL_SUBTEST_5( submatrices(MatrixXcd(20, 20)) ); + CALL_SUBTEST_6( submatrices(MatrixXf(20, 20)) ); - CALL_SUBTEST( data_and_stride(MatrixXf(ei_random(5,50), ei_random(5,50))) ); - CALL_SUBTEST( data_and_stride(Matrix(ei_random(5,50), ei_random(5,50))) ); + CALL_SUBTEST_6( data_and_stride(MatrixXf(ei_random(5,50), ei_random(5,50))) ); + CALL_SUBTEST_7( data_and_stride(Matrix(ei_random(5,50), ei_random(5,50))) ); } } diff --git a/test/svd.cpp b/test/svd.cpp index e6a32bd3f..188698a0b 100644 --- a/test/svd.cpp +++ b/test/svd.cpp @@ -100,17 +100,17 @@ template void svd_verify_assert() void test_svd() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( svd(Matrix3f()) ); - CALL_SUBTEST( svd(Matrix4d()) ); - CALL_SUBTEST( svd(MatrixXf(7,7)) ); - CALL_SUBTEST( svd(MatrixXd(14,7)) ); + CALL_SUBTEST_1( svd(Matrix3f()) ); + CALL_SUBTEST_2( svd(Matrix4d()) ); + CALL_SUBTEST_3( svd(MatrixXf(7,7)) ); + CALL_SUBTEST_4( svd(MatrixXd(14,7)) ); // complex are not implemented yet -// CALL_SUBTEST( svd(MatrixXcd(6,6)) ); -// CALL_SUBTEST( svd(MatrixXcf(3,3)) ); +// CALL_SUBTEST(svd(MatrixXcd(6,6)) ); +// CALL_SUBTEST(svd(MatrixXcf(3,3)) ); } - CALL_SUBTEST( svd_verify_assert() ); - CALL_SUBTEST( svd_verify_assert() ); - CALL_SUBTEST( svd_verify_assert() ); - CALL_SUBTEST( svd_verify_assert() ); + CALL_SUBTEST_1( svd_verify_assert() ); + CALL_SUBTEST_2( svd_verify_assert() ); + CALL_SUBTEST_3( svd_verify_assert() ); + CALL_SUBTEST_4( svd_verify_assert() ); } diff --git a/test/swap.cpp b/test/swap.cpp index 8b325992c..c11f0fec0 100644 --- a/test/swap.cpp +++ b/test/swap.cpp @@ -91,8 +91,8 @@ template void swap(const MatrixType& m) void test_swap() { - CALL_SUBTEST( swap(Matrix3f()) ); // fixed size, no vectorization - CALL_SUBTEST( swap(Matrix4d()) ); // fixed size, possible vectorization - CALL_SUBTEST( swap(MatrixXd(3,3)) ); // dyn size, no vectorization - CALL_SUBTEST( swap(MatrixXf(30,30)) ); // dyn size, possible vectorization + CALL_SUBTEST_1( swap(Matrix3f()) ); // fixed size, no vectorization + CALL_SUBTEST_2( swap(Matrix4d()) ); // fixed size, possible vectorization + CALL_SUBTEST_3( swap(MatrixXd(3,3)) ); // dyn size, no vectorization + CALL_SUBTEST_4( swap(MatrixXf(30,30)) ); // dyn size, possible vectorization } diff --git a/test/triangular.cpp b/test/triangular.cpp index 1e0782523..ee02c0022 100644 --- a/test/triangular.cpp +++ b/test/triangular.cpp @@ -137,12 +137,12 @@ template void triangular(const MatrixType& m) void test_triangular() { for(int i = 0; i < g_repeat ; i++) { - CALL_SUBTEST( triangular(Matrix()) ); - CALL_SUBTEST( triangular(Matrix()) ); - CALL_SUBTEST( triangular(Matrix3d()) ); - CALL_SUBTEST( triangular(MatrixXcf(4, 4)) ); - CALL_SUBTEST( triangular(Matrix,8, 8>()) ); - CALL_SUBTEST( triangular(MatrixXcd(17,17)) ); - CALL_SUBTEST( triangular(Matrix(5, 5)) ); + CALL_SUBTEST_1( triangular(Matrix()) ); + CALL_SUBTEST_2( triangular(Matrix()) ); + CALL_SUBTEST_3( triangular(Matrix3d()) ); + CALL_SUBTEST_4( triangular(MatrixXcf(4, 4)) ); + CALL_SUBTEST_5( triangular(Matrix,8, 8>()) ); + CALL_SUBTEST_6( triangular(MatrixXcd(17,17)) ); + CALL_SUBTEST_7( triangular(Matrix(5, 5)) ); } } diff --git a/test/umeyama.cpp b/test/umeyama.cpp index 0999c59c9..09db27c1b 100644 --- a/test/umeyama.cpp +++ b/test/umeyama.cpp @@ -181,17 +181,17 @@ void test_umeyama() // works also for dimensions bigger than 3... for (int dim=2; dim<8; ++dim) { - CALL_SUBTEST(run_test(dim, num_elements)); - CALL_SUBTEST(run_test(dim, num_elements)); + CALL_SUBTEST_1(run_test(dim, num_elements)); + CALL_SUBTEST_2(run_test(dim, num_elements)); } - CALL_SUBTEST((run_fixed_size_test(num_elements))); - CALL_SUBTEST((run_fixed_size_test(num_elements))); - CALL_SUBTEST((run_fixed_size_test(num_elements))); + CALL_SUBTEST_3((run_fixed_size_test(num_elements))); + CALL_SUBTEST_4((run_fixed_size_test(num_elements))); + CALL_SUBTEST_5((run_fixed_size_test(num_elements))); - CALL_SUBTEST((run_fixed_size_test(num_elements))); - CALL_SUBTEST((run_fixed_size_test(num_elements))); - CALL_SUBTEST((run_fixed_size_test(num_elements))); + CALL_SUBTEST_6((run_fixed_size_test(num_elements))); + CALL_SUBTEST_7((run_fixed_size_test(num_elements))); + CALL_SUBTEST_8((run_fixed_size_test(num_elements))); } // Those two calls don't compile and result in meaningful error messages! diff --git a/test/visitor.cpp b/test/visitor.cpp index 6ec442bc8..65ee60ba4 100644 --- a/test/visitor.cpp +++ b/test/visitor.cpp @@ -115,17 +115,17 @@ template void vectorVisitor(const VectorType& w) void test_visitor() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( matrixVisitor(Matrix()) ); - CALL_SUBTEST( matrixVisitor(Matrix2f()) ); - CALL_SUBTEST( matrixVisitor(Matrix4d()) ); - CALL_SUBTEST( matrixVisitor(MatrixXd(8, 12)) ); - CALL_SUBTEST( matrixVisitor(Matrix(20, 20)) ); - CALL_SUBTEST( matrixVisitor(MatrixXi(8, 12)) ); + CALL_SUBTEST_1( matrixVisitor(Matrix()) ); + CALL_SUBTEST_2( matrixVisitor(Matrix2f()) ); + CALL_SUBTEST_3( matrixVisitor(Matrix4d()) ); + CALL_SUBTEST_4( matrixVisitor(MatrixXd(8, 12)) ); + CALL_SUBTEST_5( matrixVisitor(Matrix(20, 20)) ); + CALL_SUBTEST_6( matrixVisitor(MatrixXi(8, 12)) ); } for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( vectorVisitor(Vector4f()) ); - CALL_SUBTEST( vectorVisitor(VectorXd(10)) ); - CALL_SUBTEST( vectorVisitor(RowVectorXd(10)) ); - CALL_SUBTEST( vectorVisitor(VectorXf(33)) ); + CALL_SUBTEST_7( vectorVisitor(Vector4f()) ); + CALL_SUBTEST_8( vectorVisitor(VectorXd(10)) ); + CALL_SUBTEST_9( vectorVisitor(RowVectorXd(10)) ); + CALL_SUBTEST_10( vectorVisitor(VectorXf(33)) ); } } diff --git a/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h b/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h index 36d13b7eb..636f37655 100644 --- a/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h +++ b/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h @@ -192,7 +192,7 @@ MatrixExponential::MatrixExponential(const MatrixType &M, MatrixType computeUV(RealScalar()); m_tmp1 = m_U + m_V; // numerator of Pade approximant m_tmp2 = -m_U + m_V; // denominator of Pade approximant - m_tmp2.partialLu().solve(m_tmp1, result); + *result = m_tmp2.partialPivLu().solve(m_tmp1); for (int i=0; i test2; CALL_SUBTEST(test2.testIntersect1()); CALL_SUBTEST(test2.testMinimize1()); CALL_SUBTEST(test2.testIntersect2()); CALL_SUBTEST(test2.testMinimize2()); +#endif +#ifdef EIGEN_TEST_PART_2 TreeTest<3> test3; CALL_SUBTEST(test3.testIntersect1()); CALL_SUBTEST(test3.testMinimize1()); CALL_SUBTEST(test3.testIntersect2()); CALL_SUBTEST(test3.testMinimize2()); +#endif +#ifdef EIGEN_TEST_PART_3 TreeTest<4> test4; CALL_SUBTEST(test4.testIntersect1()); CALL_SUBTEST(test4.testMinimize1()); CALL_SUBTEST(test4.testIntersect2()); CALL_SUBTEST(test4.testMinimize2()); +#endif } } diff --git a/unsupported/test/alignedvector3.cpp b/unsupported/test/alignedvector3.cpp index 52d39ce50..f4b6dd4d9 100644 --- a/unsupported/test/alignedvector3.cpp +++ b/unsupported/test/alignedvector3.cpp @@ -69,6 +69,6 @@ void alignedvector3() void test_alignedvector3() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST(( alignedvector3() )); + CALL_SUBTEST( alignedvector3() ); } } diff --git a/unsupported/test/matrixExponential.cpp b/unsupported/test/matrixExponential.cpp index 7d65a701a..f7ee71768 100644 --- a/unsupported/test/matrixExponential.cpp +++ b/unsupported/test/matrixExponential.cpp @@ -110,18 +110,18 @@ void randomTest(const MatrixType& m, double tol) void test_matrixExponential() { - CALL_SUBTEST(test2dRotation(1e-14)); - CALL_SUBTEST(test2dRotation(1e-5)); - CALL_SUBTEST(test2dHyperbolicRotation(1e-14)); - CALL_SUBTEST(test2dHyperbolicRotation(1e-5)); - CALL_SUBTEST(testPascal(1e-5)); - CALL_SUBTEST(testPascal(1e-14)); - CALL_SUBTEST(randomTest(Matrix2d(), 1e-13)); - CALL_SUBTEST(randomTest(Matrix(), 1e-13)); - CALL_SUBTEST(randomTest(Matrix4cd(), 1e-13)); - CALL_SUBTEST(randomTest(MatrixXd(8,8), 1e-13)); - CALL_SUBTEST(randomTest(Matrix2f(), 1e-4)); - CALL_SUBTEST(randomTest(Matrix3cf(), 1e-4)); - CALL_SUBTEST(randomTest(Matrix4f(), 1e-4)); - CALL_SUBTEST(randomTest(MatrixXf(8,8), 1e-4)); + CALL_SUBTEST_2(test2dRotation(1e-14)); + CALL_SUBTEST_1(test2dRotation(1e-5)); + CALL_SUBTEST_2(test2dHyperbolicRotation(1e-14)); + CALL_SUBTEST_1(test2dHyperbolicRotation(1e-5)); + CALL_SUBTEST_1(testPascal(1e-5)); + CALL_SUBTEST_2(testPascal(1e-14)); + CALL_SUBTEST_2(randomTest(Matrix2d(), 1e-13)); + CALL_SUBTEST_2(randomTest(Matrix(), 1e-13)); + CALL_SUBTEST_3(randomTest(Matrix4cd(), 1e-13)); + CALL_SUBTEST_4(randomTest(MatrixXd(8,8), 1e-13)); + CALL_SUBTEST_1(randomTest(Matrix2f(), 1e-4)); + CALL_SUBTEST_5(randomTest(Matrix3cf(), 1e-4)); + CALL_SUBTEST_1(randomTest(Matrix4f(), 1e-4)); + CALL_SUBTEST_6(randomTest(MatrixXf(8,8), 1e-4)); } From 6b48e932e9b68159d2b0cc9d0d14c4025808327c Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Thu, 29 Oct 2009 21:11:05 -0400 Subject: [PATCH 24/34] *port the Cholesky module to the new solve() API *improve documentation --- Eigen/src/Cholesky/LDLT.h | 88 ++++++++++++++++++++++++++----------- Eigen/src/Cholesky/LLT.h | 86 ++++++++++++++++++++++++++---------- Eigen/src/LU/FullPivLU.h | 40 ++++++++--------- Eigen/src/LU/PartialPivLU.h | 26 +++++------ doc/Doxyfile.in | 2 +- doc/snippets/LLT_solve.cpp | 4 +- test/cholesky.cpp | 16 +++---- test/lu.cpp | 4 +- 8 files changed, 169 insertions(+), 97 deletions(-) diff --git a/Eigen/src/Cholesky/LDLT.h b/Eigen/src/Cholesky/LDLT.h index f95e10935..04c02d4fc 100644 --- a/Eigen/src/Cholesky/LDLT.h +++ b/Eigen/src/Cholesky/LDLT.h @@ -27,6 +27,8 @@ #ifndef EIGEN_LDLT_H #define EIGEN_LDLT_H +template struct ei_ldlt_solve_impl; + /** \ingroup cholesky_Module * * \class LDLT @@ -43,8 +45,8 @@ * zeros in the bottom right rank(A) - n submatrix. Avoiding the square root * on D also stabilizes the computation. * - * Remember that Cholesky decompositions are not rank-revealing. Also, do not use a Cholesky decomposition to determine - * whether a system of equations has a solution. + * Remember that Cholesky decompositions are not rank-revealing. Also, do not use a Cholesky + * decomposition to determine whether a system of equations has a solution. * * \sa MatrixBase::ldlt(), class LLT */ @@ -117,14 +119,37 @@ template class LDLT return m_sign == -1; } - template - bool solve(const MatrixBase &b, ResultType *result) const; - + /** \returns a solution x of \f$ A x = b \f$ using the current decomposition of A. + * + * \note_about_checking_solutions + * + * \sa solveInPlace(), MatrixBase::ldlt() + */ + template + inline const ei_ldlt_solve_impl + solve(const MatrixBase& b) const + { + ei_assert(m_isInitialized && "LDLT is not initialized."); + ei_assert(m_matrix.rows()==b.rows() + && "LDLT::solve(): invalid number of rows of the right hand side matrix b"); + return ei_ldlt_solve_impl(*this, b.derived()); + } + template bool solveInPlace(MatrixBase &bAndX) const; LDLT& compute(const MatrixType& matrix); + /** \returns the LDLT decomposition matrix + * + * TODO: document the storage layout + */ + inline const MatrixType& matrixLDLT() const + { + ei_assert(m_isInitialized && "LDLT is not initialized."); + return m_matrix; + } + protected: /** \internal * Used to compute and store the Cholesky decomposition A = L D L^* = U^* D U. @@ -134,7 +159,7 @@ template class LDLT */ MatrixType m_matrix; IntColVectorType m_p; - IntColVectorType m_transpositions; + IntColVectorType m_transpositions; // FIXME do we really need to store permanently the transpositions? int m_sign; bool m_isInitialized; }; @@ -238,27 +263,38 @@ LDLT& LDLT::compute(const MatrixType& a) return *this; } -/** Computes the solution x of \f$ A x = b \f$ using the current decomposition of A. - * The result is stored in \a result - * - * \returns true always! If you need to check for existence of solutions, use another decomposition like LU, QR, or SVD. - * - * In other words, it computes \f$ b = A^{-1} b \f$ with - * \f$ P^T{L^{*}}^{-1} D^{-1} L^{-1} P b \f$ from right to left. - * - * \sa LDLT::solveInPlace(), MatrixBase::ldlt() - */ -template -template -bool LDLT -::solve(const MatrixBase &b, ResultType *result) const +template +struct ei_traits > { - ei_assert(m_isInitialized && "LDLT is not initialized."); - const int size = m_matrix.rows(); - ei_assert(size==b.rows() && "LDLT::solve(): invalid number of rows of the right hand side matrix b"); - *result = b; - return solveInPlace(*result); -} + typedef Matrix ReturnMatrixType; +}; + +template +struct ei_ldlt_solve_impl : public ReturnByValue > +{ + typedef typename ei_cleantype::type RhsNested; + typedef LDLT LDLTType; + const LDLTType& m_ldlt; + const typename Rhs::Nested m_rhs; + + ei_ldlt_solve_impl(const LDLTType& ldlt, const Rhs& rhs) + : m_ldlt(ldlt), m_rhs(rhs) + {} + + inline int rows() const { return m_ldlt.matrixLDLT().cols(); } + inline int cols() const { return m_rhs.cols(); } + + template void evalTo(Dest& dst) const + { + dst = m_rhs; + m_ldlt.solveInPlace(dst); + } +}; /** This is the \em in-place version of solve(). * diff --git a/Eigen/src/Cholesky/LLT.h b/Eigen/src/Cholesky/LLT.h index ec7b8123c..d6fd514cc 100644 --- a/Eigen/src/Cholesky/LLT.h +++ b/Eigen/src/Cholesky/LLT.h @@ -26,6 +26,7 @@ #define EIGEN_LLT_H template struct LLT_Traits; +template struct ei_llt_solve_impl; /** \ingroup cholesky_Module * @@ -99,14 +100,41 @@ template class LLT return Traits::getL(m_matrix); } - template - bool solve(const MatrixBase &b, ResultType *result) const; - + /** \returns the solution x of \f$ A x = b \f$ using the current decomposition of A. + * + * Since this LLT class assumes anyway that the matrix A is invertible, the solution + * theoretically exists and is unique regardless of b. + * + * Example: \include LLT_solve.cpp + * Output: \verbinclude LLT_solve.out + * + * \sa solveInPlace(), MatrixBase::llt() + */ + template + inline const ei_llt_solve_impl + solve(const MatrixBase& b) const + { + ei_assert(m_isInitialized && "LLT is not initialized."); + ei_assert(m_matrix.rows()==b.rows() + && "LLT::solve(): invalid number of rows of the right hand side matrix b"); + return ei_llt_solve_impl(*this, b.derived()); + } + template bool solveInPlace(MatrixBase &bAndX) const; LLT& compute(const MatrixType& matrix); + /** \returns the LLT decomposition matrix + * + * TODO: document the storage layout + */ + inline const MatrixType& matrixLLT() const + { + ei_assert(m_isInitialized && "LLT is not initialized."); + return m_matrix; + } + protected: /** \internal * Used to compute and store L @@ -229,28 +257,38 @@ LLT& LLT::compute(const MatrixType& a) return *this; } -/** Computes the solution x of \f$ A x = b \f$ using the current decomposition of A. - * The result is stored in \a result - * - * \returns true always! If you need to check for existence of solutions, use another decomposition like LU, QR, or SVD. - * - * In other words, it computes \f$ b = A^{-1} b \f$ with - * \f$ {L^{*}}^{-1} L^{-1} b \f$ from right to left. - * - * Example: \include LLT_solve.cpp - * Output: \verbinclude LLT_solve.out - * - * \sa LLT::solveInPlace(), MatrixBase::llt() - */ -template -template -bool LLT::solve(const MatrixBase &b, ResultType *result) const +template +struct ei_traits > { - ei_assert(m_isInitialized && "LLT is not initialized."); - const int size = m_matrix.rows(); - ei_assert(size==b.rows() && "LLT::solve(): invalid number of rows of the right hand side matrix b"); - return solveInPlace((*result) = b); -} + typedef Matrix ReturnMatrixType; +}; + +template +struct ei_llt_solve_impl : public ReturnByValue > +{ + typedef typename ei_cleantype::type RhsNested; + typedef LLT LLTType; + const LLTType& m_llt; + const typename Rhs::Nested m_rhs; + + ei_llt_solve_impl(const LLTType& llt, const Rhs& rhs) + : m_llt(llt), m_rhs(rhs) + {} + + inline int rows() const { return m_llt.matrixLLT().cols(); } + inline int cols() const { return m_rhs.cols(); } + + template void evalTo(Dest& dst) const + { + dst = m_rhs; + m_llt.solveInPlace(dst); + } +}; /** This is the \em in-place version of solve(). * diff --git a/Eigen/src/LU/FullPivLU.h b/Eigen/src/LU/FullPivLU.h index 8743dac92..a28a536b6 100644 --- a/Eigen/src/LU/FullPivLU.h +++ b/Eigen/src/LU/FullPivLU.h @@ -25,9 +25,9 @@ #ifndef EIGEN_LU_H #define EIGEN_LU_H -template struct ei_lu_solve_impl; -template struct ei_lu_kernel_impl; -template struct ei_lu_image_impl; +template struct ei_fullpivlu_solve_impl; +template struct ei_fullpivlu_kernel_impl; +template struct ei_fullpivlu_image_impl; /** \ingroup LU_Module * @@ -167,10 +167,10 @@ template class FullPivLU * * \sa image() */ - inline const ei_lu_kernel_impl kernel() const + inline const ei_fullpivlu_kernel_impl kernel() const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_lu_kernel_impl(*this); + return ei_fullpivlu_kernel_impl(*this); } /** \returns the image of the matrix, also called its column-space. The columns of the returned matrix @@ -193,11 +193,11 @@ template class FullPivLU * \sa kernel() */ template - inline const ei_lu_image_impl + inline const ei_fullpivlu_image_impl image(const MatrixBase& originalMatrix) const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_lu_image_impl(*this, originalMatrix.derived()); + return ei_fullpivlu_image_impl(*this, originalMatrix.derived()); } /** This method returns a solution x to the equation Ax=b, where A is the matrix of which @@ -220,11 +220,11 @@ template class FullPivLU * \sa TriangularView::solve(), kernel(), inverse() */ template - inline const ei_lu_solve_impl + inline const ei_fullpivlu_solve_impl solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_lu_solve_impl(*this, b.derived()); + return ei_fullpivlu_solve_impl(*this, b.derived()); } /** \returns the determinant of the matrix of which @@ -365,11 +365,11 @@ template class FullPivLU * * \sa MatrixBase::inverse() */ - inline const ei_lu_solve_impl > inverse() const + inline const ei_fullpivlu_solve_impl > inverse() const { ei_assert(m_isInitialized && "LU is not initialized."); ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the inverse of a non-square matrix!"); - return ei_lu_solve_impl > + return ei_fullpivlu_solve_impl > (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); } @@ -493,7 +493,7 @@ typename ei_traits::Scalar FullPivLU::determinant() cons /********* Implementation of kernel() **************************************************/ template -struct ei_traits > +struct ei_traits > { typedef Matrix< typename MatrixType::Scalar, @@ -509,7 +509,7 @@ struct ei_traits > }; template -struct ei_lu_kernel_impl : public ReturnByValue > +struct ei_fullpivlu_kernel_impl : public ReturnByValue > { typedef FullPivLU LUType; typedef typename MatrixType::Scalar Scalar; @@ -517,7 +517,7 @@ struct ei_lu_kernel_impl : public ReturnByValue > const LUType& m_lu; int m_rank, m_cols; - ei_lu_kernel_impl(const LUType& lu) + ei_fullpivlu_kernel_impl(const LUType& lu) : m_lu(lu), m_rank(lu.rank()), m_cols(m_rank==lu.matrixLU().cols() ? 1 : lu.matrixLU().cols() - m_rank){} @@ -599,7 +599,7 @@ struct ei_lu_kernel_impl : public ReturnByValue > /***** Implementation of image() *****************************************************/ template -struct ei_traits > +struct ei_traits > { typedef Matrix< typename MatrixType::Scalar, @@ -613,7 +613,7 @@ struct ei_traits > }; template -struct ei_lu_image_impl : public ReturnByValue > +struct ei_fullpivlu_image_impl : public ReturnByValue > { typedef FullPivLU LUType; typedef typename MatrixType::RealScalar RealScalar; @@ -621,7 +621,7 @@ struct ei_lu_image_impl : public ReturnByValue > int m_rank, m_cols; const MatrixType& m_originalMatrix; - ei_lu_image_impl(const LUType& lu, const MatrixType& originalMatrix) + ei_fullpivlu_image_impl(const LUType& lu, const MatrixType& originalMatrix) : m_lu(lu), m_rank(lu.rank()), m_cols(m_rank == 0 ? 1 : m_rank), m_originalMatrix(originalMatrix) {} @@ -656,7 +656,7 @@ struct ei_lu_image_impl : public ReturnByValue > /***** Implementation of solve() *****************************************************/ template -struct ei_traits > +struct ei_traits > { typedef Matrix > }; template -struct ei_lu_solve_impl : public ReturnByValue > +struct ei_fullpivlu_solve_impl : public ReturnByValue > { typedef typename ei_cleantype::type RhsNested; typedef FullPivLU LUType; const LUType& m_lu; const typename Rhs::Nested m_rhs; - ei_lu_solve_impl(const LUType& lu, const Rhs& rhs) + ei_fullpivlu_solve_impl(const LUType& lu, const Rhs& rhs) : m_lu(lu), m_rhs(rhs) {} diff --git a/Eigen/src/LU/PartialPivLU.h b/Eigen/src/LU/PartialPivLU.h index 647ada38f..2a04ec4a9 100644 --- a/Eigen/src/LU/PartialPivLU.h +++ b/Eigen/src/LU/PartialPivLU.h @@ -26,7 +26,7 @@ #ifndef EIGEN_PARTIALLU_H #define EIGEN_PARTIALLU_H -template struct ei_partiallu_solve_impl; +template struct ei_partialpivlu_solve_impl; /** \ingroup LU_Module * @@ -116,7 +116,7 @@ template class PartialPivLU return m_p; } - /** This method returns a solution x to the equation Ax=b, where A is the matrix of which + /** This method returns the solution x to the equation Ax=b, where A is the matrix of which * *this is the LU decomposition. * * \param b the right-hand-side of the equation to solve. Can be a vector or a matrix, @@ -131,16 +131,14 @@ template class PartialPivLU * Since this PartialPivLU class assumes anyway that the matrix A is invertible, the solution * theoretically exists and is unique regardless of b. * - * \note_about_checking_solutions - * * \sa TriangularView::solve(), inverse(), computeInverse() */ template - inline const ei_partiallu_solve_impl + inline const ei_partialpivlu_solve_impl solve(const MatrixBase& b) const { - ei_assert(m_isInitialized && "LU is not initialized."); - return ei_partiallu_solve_impl(*this, b.derived()); + ei_assert(m_isInitialized && "PartialPivLU is not initialized."); + return ei_partialpivlu_solve_impl(*this, b.derived()); } /** \returns the inverse of the matrix of which *this is the LU decomposition. @@ -150,10 +148,10 @@ template class PartialPivLU * * \sa MatrixBase::inverse(), LU::inverse() */ - inline const ei_partiallu_solve_impl > inverse() const + inline const ei_partialpivlu_solve_impl > inverse() const { - ei_assert(m_isInitialized && "LU is not initialized."); - return ei_partiallu_solve_impl > + ei_assert(m_isInitialized && "PartialPivLU is not initialized."); + return ei_partialpivlu_solve_impl > (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); } @@ -200,7 +198,7 @@ PartialPivLU::PartialPivLU(const MatrixType& matrix) -/** This is the blocked version of ei_lu_unblocked() */ +/** This is the blocked version of ei_fullpivlu_unblocked() */ template struct ei_partial_lu_impl { @@ -410,7 +408,7 @@ typename ei_traits::Scalar PartialPivLU::determinant() c /***** Implementation of solve() *****************************************************/ template -struct ei_traits > +struct ei_traits > { typedef Matrix > }; template -struct ei_partiallu_solve_impl : public ReturnByValue > +struct ei_partialpivlu_solve_impl : public ReturnByValue > { typedef typename ei_cleantype::type RhsNested; typedef PartialPivLU LUType; const LUType& m_lu; const typename Rhs::Nested m_rhs; - ei_partiallu_solve_impl(const LUType& lu, const Rhs& rhs) + ei_partialpivlu_solve_impl(const LUType& lu, const Rhs& rhs) : m_lu(lu), m_rhs(rhs) {} diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index fb00a3cc5..412831e14 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -218,7 +218,7 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\"" \ "note_about_arbitrary_choice_of_solution=If there exists more than one solution, this method will arbitrarily choose one." \ "note_about_using_kernel_to_study_multiple_solutions=If you need a complete analysis of the space of solutions, take the one solution obtained by this method and add to it elements of the kernel, as determined by kernel()." \ - "note_about_checking_solutions=This method just tries to find as good a solution as possible. If you want to check whether a solution "exists" or if it is accurate, just call this function to get a solution and then compute the error margin, or use MatrixBase::isApprox() directly, for instance like this: \code bool a_solution_exists = (A*result).isApprox(b, precision); \endcode The non-existence of a solution doesn't by itself mean that you'll get \c inf or \c nan values." + "note_about_checking_solutions=This method just tries to find as good a solution as possible. If you want to check whether a solution exists or if it is accurate, just call this function to get a result and then compute the error of this result, or use MatrixBase::isApprox() directly, for instance like this: \code bool a_solution_exists = (A*result).isApprox(b, precision); \endcode This method avoids dividing by zero, so that the non-existence of a solution doesn't by itself mean that you'll get \c inf or \c nan values." # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. diff --git a/doc/snippets/LLT_solve.cpp b/doc/snippets/LLT_solve.cpp index 76ab09ec5..7095d2cc3 100644 --- a/doc/snippets/LLT_solve.cpp +++ b/doc/snippets/LLT_solve.cpp @@ -3,6 +3,6 @@ typedef Matrix DataMatrix; DataMatrix samples = DataMatrix::Random(12,2); VectorXf elevations = 2*samples.col(0) + 3*samples.col(1) + VectorXf::Random(12)*0.1; // and let's solve samples * [x y]^T = elevations in least square sense: -Matrix xy; -(samples.adjoint() * samples).llt().solve((samples.adjoint()*elevations), &xy); +Matrix xy + = (samples.adjoint() * samples).llt().solve((samples.adjoint()*elevations)); cout << xy << endl; diff --git a/test/cholesky.cpp b/test/cholesky.cpp index 77d025e59..83f7e0492 100644 --- a/test/cholesky.cpp +++ b/test/cholesky.cpp @@ -93,17 +93,17 @@ template void cholesky(const MatrixType& m) { LLT chollo(symmLo); VERIFY_IS_APPROX(symm, chollo.matrixL().toDense() * chollo.matrixL().adjoint().toDense()); - chollo.solve(vecB, &vecX); + vecX = chollo.solve(vecB); VERIFY_IS_APPROX(symm * vecX, vecB); - chollo.solve(matB, &matX); + matX = chollo.solve(matB); VERIFY_IS_APPROX(symm * matX, matB); // test the upper mode LLT cholup(symmUp); VERIFY_IS_APPROX(symm, cholup.matrixL().toDense() * cholup.matrixL().adjoint().toDense()); - cholup.solve(vecB, &vecX); + vecX = cholup.solve(vecB); VERIFY_IS_APPROX(symm * vecX, vecB); - cholup.solve(matB, &matX); + matX = cholup.solve(matB); VERIFY_IS_APPROX(symm * matX, matB); } @@ -118,9 +118,9 @@ template void cholesky(const MatrixType& m) LDLT ldlt(symm); // TODO(keir): This doesn't make sense now that LDLT pivots. //VERIFY_IS_APPROX(symm, ldlt.matrixL() * ldlt.vectorD().asDiagonal() * ldlt.matrixL().adjoint()); - ldlt.solve(vecB, &vecX); + vecX = ldlt.solve(vecB); VERIFY_IS_APPROX(symm * vecX, vecB); - ldlt.solve(matB, &matX); + matX = ldlt.solve(matB); VERIFY_IS_APPROX(symm * matX, matB); } @@ -132,7 +132,7 @@ template void cholesky_verify_assert() LLT llt; VERIFY_RAISES_ASSERT(llt.matrixL()) - VERIFY_RAISES_ASSERT(llt.solve(tmp,&tmp)) + VERIFY_RAISES_ASSERT(llt.solve(tmp)) VERIFY_RAISES_ASSERT(llt.solveInPlace(&tmp)) LDLT ldlt; @@ -141,7 +141,7 @@ template void cholesky_verify_assert() VERIFY_RAISES_ASSERT(ldlt.vectorD()) VERIFY_RAISES_ASSERT(ldlt.isPositive()) VERIFY_RAISES_ASSERT(ldlt.isNegative()) - VERIFY_RAISES_ASSERT(ldlt.solve(tmp,&tmp)) + VERIFY_RAISES_ASSERT(ldlt.solve(tmp)) VERIFY_RAISES_ASSERT(ldlt.solveInPlace(&tmp)) } diff --git a/test/lu.cpp b/test/lu.cpp index b7214ae12..c46ca9130 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -49,8 +49,8 @@ template void lu_non_invertible() cols2 = cols = MatrixType::ColsAtCompileTime; } - typedef typename ei_lu_kernel_impl::ReturnMatrixType KernelMatrixType; - typedef typename ei_lu_image_impl ::ReturnMatrixType ImageMatrixType; + typedef typename ei_fullpivlu_kernel_impl::ReturnMatrixType KernelMatrixType; + typedef typename ei_fullpivlu_image_impl ::ReturnMatrixType ImageMatrixType; typedef Matrix DynamicMatrixType; typedef Matrix CMatrixType; From f975b9bd3eb0a862efef290a63a3d1d20a03c099 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Fri, 30 Oct 2009 08:51:33 -0400 Subject: [PATCH 25/34] SVD::solve() : port to new API and improvements --- Eigen/src/LU/FullPivLU.h | 2 +- Eigen/src/SVD/SVD.h | 124 +++++++++++++++++++++++++++------------ test/svd.cpp | 4 +- 3 files changed, 89 insertions(+), 41 deletions(-) diff --git a/Eigen/src/LU/FullPivLU.h b/Eigen/src/LU/FullPivLU.h index a28a536b6..067b59549 100644 --- a/Eigen/src/LU/FullPivLU.h +++ b/Eigen/src/LU/FullPivLU.h @@ -200,7 +200,7 @@ template class FullPivLU return ei_fullpivlu_image_impl(*this, originalMatrix.derived()); } - /** This method returns a solution x to the equation Ax=b, where A is the matrix of which + /** \return a solution x to the equation Ax=b, where A is the matrix of which * *this is the LU decomposition. * * \param b the right-hand-side of the equation to solve. Can be a vector or a matrix, diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h index da01cf396..807e7058c 100644 --- a/Eigen/src/SVD/SVD.h +++ b/Eigen/src/SVD/SVD.h @@ -25,6 +25,8 @@ #ifndef EIGEN_SVD_H #define EIGEN_SVD_H +template struct ei_svd_solve_impl; + /** \ingroup SVD_Module * \nonstableyet * @@ -40,24 +42,24 @@ */ template class SVD { - private: + public: typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; enum { + RowsAtCompileTime = MatrixType::RowsAtCompileTime, + ColsAtCompileTime = MatrixType::ColsAtCompileTime, PacketSize = ei_packet_traits::size, AlignmentMask = int(PacketSize)-1, - MinSize = EIGEN_ENUM_MIN(MatrixType::RowsAtCompileTime, MatrixType::ColsAtCompileTime) + MinSize = EIGEN_ENUM_MIN(RowsAtCompileTime, ColsAtCompileTime) }; - typedef Matrix ColVector; - typedef Matrix RowVector; + typedef Matrix ColVector; + typedef Matrix RowVector; - typedef Matrix MatrixUType; - typedef Matrix MatrixVType; - typedef Matrix SingularValuesType; - - public: + typedef Matrix MatrixUType; + typedef Matrix MatrixVType; + typedef Matrix SingularValuesType; /** * \brief Default Constructor. @@ -76,8 +78,24 @@ template class SVD compute(matrix); } - template - bool solve(const MatrixBase &b, ResultType* result) const; + /** \returns a solution of \f$ A x = b \f$ using the current SVD decomposition of A. + * + * \param b the right-hand-side of the equation to solve. + * + * \note_about_checking_solutions + * + * \note_about_arbitrary_choice_of_solution + * \note_about_using_kernel_to_study_multiple_solutions + * + * \sa MatrixBase::svd(), + */ + template + inline const ei_svd_solve_impl + solve(const MatrixBase& b) const + { + ei_assert(m_isInitialized && "SVD is not initialized."); + return ei_svd_solve_impl(*this, b.derived()); + } const MatrixUType& matrixU() const { @@ -108,6 +126,18 @@ template class SVD template void computeScalingRotation(ScalingType *positive, RotationType *unitary) const; + inline int rows() const + { + ei_assert(m_isInitialized && "SVD is not initialized."); + return m_rows; + } + + inline int cols() const + { + ei_assert(m_isInitialized && "SVD is not initialized."); + return m_cols; + } + protected: // Computes (a^2 + b^2)^(1/2) without destructive underflow or overflow. inline static Scalar pythag(Scalar a, Scalar b) @@ -133,6 +163,7 @@ template class SVD /** \internal */ SingularValuesType m_sigma; bool m_isInitialized; + int m_rows, m_cols; }; /** Computes / recomputes the SVD decomposition A = U S V^* of \a matrix @@ -144,8 +175,8 @@ template class SVD template SVD& SVD::compute(const MatrixType& matrix) { - const int m = matrix.rows(); - const int n = matrix.cols(); + const int m = m_rows = matrix.rows(); + const int n = m_cols = matrix.cols(); m_matU.resize(m, m); m_matU.setZero(); @@ -397,40 +428,57 @@ SVD& SVD::compute(const MatrixType& matrix) return *this; } -/** \returns the solution of \f$ A x = b \f$ using the current SVD decomposition of A. - * The parts of the solution corresponding to zero singular values are ignored. - * - * \sa MatrixBase::svd(), LU::solve(), LLT::solve() - */ -template -template -bool SVD::solve(const MatrixBase &b, ResultType* result) const +template +struct ei_traits > { - ei_assert(m_isInitialized && "SVD is not initialized."); + typedef Matrix ReturnMatrixType; +}; - const int rows = m_matU.rows(); - ei_assert(b.rows() == rows); +template +struct ei_svd_solve_impl : public ReturnByValue > +{ + typedef typename ei_cleantype::type RhsNested; + typedef SVD SVDType; + typedef typename MatrixType::RealScalar RealScalar; + typedef typename MatrixType::Scalar Scalar; + const SVDType& m_svd; + const typename Rhs::Nested m_rhs; - result->resize(m_matV.rows(), b.cols()); + ei_svd_solve_impl(const SVDType& svd, const Rhs& rhs) + : m_svd(svd), m_rhs(rhs) + {} - Scalar maxVal = m_sigma.cwise().abs().maxCoeff(); - for (int j=0; j void evalTo(Dest& dst) const { - Matrix aux = m_matU.transpose() * b.col(j); + ei_assert(m_rhs.rows() == m_svd.rows()); - for (int i = 0; i aux = m_svd.matrixU().adjoint() * m_rhs.col(j); - result->col(j) = m_matV * aux; + for (int i = 0; i void svd(const MatrixType& m) a += a * a.adjoint() + a1 * a1.adjoint(); } SVD svd(a); - svd.solve(b, &x); + x = svd.solve(b); VERIFY_IS_APPROX(a * x,b); } @@ -87,7 +87,7 @@ template void svd_verify_assert() MatrixType tmp; SVD svd; - VERIFY_RAISES_ASSERT(svd.solve(tmp, &tmp)) + VERIFY_RAISES_ASSERT(svd.solve(tmp)) VERIFY_RAISES_ASSERT(svd.matrixU()) VERIFY_RAISES_ASSERT(svd.singularValues()) VERIFY_RAISES_ASSERT(svd.matrixV()) From da363d997f1721ceaefcd946fb14e793074f88b9 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Tue, 3 Nov 2009 02:18:10 -0500 Subject: [PATCH 26/34] introduce ei_xxx_return_value and ei_xxx_impl for xxx in solve,kernel,impl put them in a new internal 'misc' directory --- Eigen/CMakeLists.txt | 20 -- Eigen/LU | 3 + Eigen/src/CMakeLists.txt | 1 + Eigen/src/Core/ReturnByValue.h | 6 +- Eigen/src/Core/util/ForwardDeclarations.h | 7 + Eigen/src/LU/FullPivLU.h | 251 ++++++++-------------- Eigen/src/misc/CMakeLists.txt | 6 + Eigen/src/misc/Image.h | 72 +++++++ Eigen/src/misc/Kernel.h | 71 ++++++ Eigen/src/misc/Solve.h | 65 ++++++ test/lu.cpp | 8 +- 11 files changed, 326 insertions(+), 184 deletions(-) create mode 100644 Eigen/src/misc/CMakeLists.txt create mode 100644 Eigen/src/misc/Image.h create mode 100644 Eigen/src/misc/Kernel.h create mode 100644 Eigen/src/misc/Solve.h diff --git a/Eigen/CMakeLists.txt b/Eigen/CMakeLists.txt index 931cc6e20..e0eb837a5 100644 --- a/Eigen/CMakeLists.txt +++ b/Eigen/CMakeLists.txt @@ -1,25 +1,5 @@ set(Eigen_HEADERS Core LU Cholesky QR Geometry Sparse Array SVD LeastSquares QtAlignedMalloc StdVector Householder Jacobi Eigenvalues) -if(EIGEN_BUILD_LIB) - set(Eigen_SRCS - src/Core/CoreInstantiations.cpp - src/Cholesky/CholeskyInstantiations.cpp - src/QR/QrInstantiations.cpp - ) - - add_library(Eigen2 SHARED ${Eigen_SRCS}) - - install(TARGETS Eigen2 - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) -endif(EIGEN_BUILD_LIB) - -if(CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g1 -O2") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g1 -O2") -endif(CMAKE_COMPILER_IS_GNUCXX) - install(FILES ${Eigen_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/Eigen COMPONENT Devel diff --git a/Eigen/LU b/Eigen/LU index 9c21b4407..e4aa3ecde 100644 --- a/Eigen/LU +++ b/Eigen/LU @@ -18,6 +18,9 @@ namespace Eigen { * \endcode */ +#include "src/misc/Solve.h" +#include "src/misc/Kernel.h" +#include "src/misc/Image.h" #include "src/LU/FullPivLU.h" #include "src/LU/PartialPivLU.h" #include "src/LU/Determinant.h" diff --git a/Eigen/src/CMakeLists.txt b/Eigen/src/CMakeLists.txt index 0df8273d1..4c3ee6cb8 100644 --- a/Eigen/src/CMakeLists.txt +++ b/Eigen/src/CMakeLists.txt @@ -10,3 +10,4 @@ ADD_SUBDIRECTORY(Sparse) ADD_SUBDIRECTORY(Jacobi) ADD_SUBDIRECTORY(Householder) ADD_SUBDIRECTORY(Eigenvalues) +ADD_SUBDIRECTORY(misc) \ No newline at end of file diff --git a/Eigen/src/Core/ReturnByValue.h b/Eigen/src/Core/ReturnByValue.h index 87b057f86..1d977ace2 100644 --- a/Eigen/src/Core/ReturnByValue.h +++ b/Eigen/src/Core/ReturnByValue.h @@ -38,8 +38,10 @@ struct ei_traits > // matrix.inverse().block(...) // because the Block ctor with direct access // wants to call coeffRef() to get an address, and that fails (infinite recursion) as ReturnByValue - // doesnt implement coeffRef(). The better fix is probably rather to make Block work directly - // on the nested type, right? + // doesnt implement coeffRef(). + // The fact that I had to do that shows that when doing xpr.block() with a non-direct-access xpr, + // even if xpr has the EvalBeforeNestingBit, the block() doesn't use direct access on the evaluated + // xpr. Flags = (ei_traits::ReturnMatrixType>::Flags | EvalBeforeNestingBit) & ~DirectAccessBit }; diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index c8f2c4cd7..b54f71ed1 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -66,6 +66,13 @@ template class WithFormat; template struct CommaInitializer; template class ReturnByValue; +template struct ei_solve_return_value; +template struct ei_solve_impl; +template struct ei_kernel_return_value; +template struct ei_kernel_impl; +template struct ei_image_return_value; +template struct ei_image_impl; + template class BandMatrix; diff --git a/Eigen/src/LU/FullPivLU.h b/Eigen/src/LU/FullPivLU.h index 067b59549..c5f44dcea 100644 --- a/Eigen/src/LU/FullPivLU.h +++ b/Eigen/src/LU/FullPivLU.h @@ -25,10 +25,6 @@ #ifndef EIGEN_LU_H #define EIGEN_LU_H -template struct ei_fullpivlu_solve_impl; -template struct ei_fullpivlu_kernel_impl; -template struct ei_fullpivlu_image_impl; - /** \ingroup LU_Module * * \class FullPivLU @@ -59,10 +55,10 @@ template struct ei_fullpivlu_image_impl; * * \sa MatrixBase::fullPivLu(), MatrixBase::determinant(), MatrixBase::inverse() */ -template class FullPivLU +template class FullPivLU { public: - + typedef _MatrixType MatrixType; typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; typedef Matrix IntRowVectorType; @@ -70,17 +66,12 @@ template class FullPivLU typedef Matrix RowVectorType; typedef Matrix ColVectorType; - enum { MaxSmallDimAtCompileTime = EIGEN_ENUM_MIN( - MatrixType::MaxColsAtCompileTime, - MatrixType::MaxRowsAtCompileTime) - }; - /** - * \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via LU::compute(const MatrixType&). - */ + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via LU::compute(const MatrixType&). + */ FullPivLU(); /** Constructor. @@ -167,10 +158,10 @@ template class FullPivLU * * \sa image() */ - inline const ei_fullpivlu_kernel_impl kernel() const + inline const ei_kernel_return_value kernel() const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_fullpivlu_kernel_impl(*this); + return ei_kernel_return_value(*this); } /** \returns the image of the matrix, also called its column-space. The columns of the returned matrix @@ -192,12 +183,11 @@ template class FullPivLU * * \sa kernel() */ - template - inline const ei_fullpivlu_image_impl - image(const MatrixBase& originalMatrix) const + inline const ei_image_return_value + image(const MatrixType& originalMatrix) const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_fullpivlu_image_impl(*this, originalMatrix.derived()); + return ei_image_return_value(*this, originalMatrix); } /** \return a solution x to the equation Ax=b, where A is the matrix of which @@ -220,11 +210,11 @@ template class FullPivLU * \sa TriangularView::solve(), kernel(), inverse() */ template - inline const ei_fullpivlu_solve_impl + inline const ei_solve_return_value solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_fullpivlu_solve_impl(*this, b.derived()); + return ei_solve_return_value(*this, b.derived()); } /** \returns the determinant of the matrix of which @@ -365,14 +355,17 @@ template class FullPivLU * * \sa MatrixBase::inverse() */ - inline const ei_fullpivlu_solve_impl > inverse() const + inline const ei_solve_return_value > inverse() const { ei_assert(m_isInitialized && "LU is not initialized."); ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the inverse of a non-square matrix!"); - return ei_fullpivlu_solve_impl > + return ei_solve_return_value > (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); } + inline int rows() const { return m_lu.rows(); } + inline int cols() const { return m_lu.cols(); } + protected: MatrixType m_lu; IntColVectorType m_p; @@ -492,42 +485,21 @@ typename ei_traits::Scalar FullPivLU::determinant() cons /********* Implementation of kernel() **************************************************/ -template -struct ei_traits > +template +struct ei_kernel_impl, Dest> + : ei_kernel_return_value > { - typedef Matrix< - typename MatrixType::Scalar, - MatrixType::ColsAtCompileTime, // the number of rows in the "kernel matrix" - // is the number of cols of the original matrix - // so that the product "matrix * kernel = zero" makes sense - Dynamic, // we don't know at compile-time the dimension of the kernel - MatrixType::Options, - MatrixType::MaxColsAtCompileTime, // see explanation for 2nd template parameter - MatrixType::MaxColsAtCompileTime // the kernel is a subspace of the domain space, - // whose dimension is the number of columns of the original matrix - > ReturnMatrixType; -}; - -template -struct ei_fullpivlu_kernel_impl : public ReturnByValue > -{ - typedef FullPivLU LUType; typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; - const LUType& m_lu; - int m_rank, m_cols; - - ei_fullpivlu_kernel_impl(const LUType& lu) - : m_lu(lu), - m_rank(lu.rank()), - m_cols(m_rank==lu.matrixLU().cols() ? 1 : lu.matrixLU().cols() - m_rank){} + enum { MaxSmallDimAtCompileTime = EIGEN_ENUM_MIN( + MatrixType::MaxColsAtCompileTime, + MatrixType::MaxRowsAtCompileTime) + }; - inline int rows() const { return m_lu.matrixLU().cols(); } - inline int cols() const { return m_cols; } - - template void evalTo(Dest& dst) const + void evalTo(Dest& dst) const { - const int cols = m_lu.matrixLU().cols(), dimker = cols - m_rank; + const FullPivLU& dec = this->m_dec; + const int cols = dec.matrixLU().cols(), rank = this->m_rank, dimker = cols - rank; if(dimker == 0) { // The Kernel is just {0}, so it doesn't have a basis properly speaking, but let's @@ -549,89 +521,73 @@ struct ei_fullpivlu_kernel_impl : public ReturnByValue pivots(m_rank); - RealScalar premultiplied_threshold = m_lu.maxPivot() * m_lu.threshold(); + Matrix pivots(rank); + RealScalar premultiplied_threshold = dec.maxPivot() * dec.threshold(); int p = 0; - for(int i = 0; i < m_lu.nonzeroPivots(); ++i) - if(ei_abs(m_lu.matrixLU().coeff(i,i)) > premultiplied_threshold) + for(int i = 0; i < dec.nonzeroPivots(); ++i) + if(ei_abs(dec.matrixLU().coeff(i,i)) > premultiplied_threshold) pivots.coeffRef(p++) = i; - ei_assert(p == m_rank && "You hit a bug in Eigen! Please report (backtrace and matrix)!"); + ei_internal_assert(p == rank); // we construct a temporaty trapezoid matrix m, by taking the U matrix and // permuting the rows and cols to bring the nonnegligible pivots to the top of // the main diagonal. We need that to be able to apply our triangular solvers. // FIXME when we get triangularView-for-rectangular-matrices, this can be simplified Matrix - m(m_lu.matrixLU().block(0, 0, m_rank, cols)); - for(int i = 0; i < m_rank; ++i) + MaxSmallDimAtCompileTime, MatrixType::MaxColsAtCompileTime> + m(dec.matrixLU().block(0, 0, rank, cols)); + for(int i = 0; i < rank; ++i) { if(i) m.row(i).start(i).setZero(); - m.row(i).end(cols-i) = m_lu.matrixLU().row(pivots.coeff(i)).end(cols-i); + m.row(i).end(cols-i) = dec.matrixLU().row(pivots.coeff(i)).end(cols-i); } - m.block(0, 0, m_rank, m_rank).template triangularView().setZero(); - for(int i = 0; i < m_rank; ++i) + m.block(0, 0, rank, rank); + m.block(0, 0, rank, rank).template triangularView().setZero(); + for(int i = 0; i < rank; ++i) m.col(i).swap(m.col(pivots.coeff(i))); // ok, we have our trapezoid matrix, we can apply the triangular solver. // notice that the math behind this suggests that we should apply this to the // negative of the RHS, but for performance we just put the negative sign elsewhere, see below. - m.corner(TopLeft, m_rank, m_rank) + m.corner(TopLeft, rank, rank) .template triangularView().solveInPlace( - m.corner(TopRight, m_rank, dimker) + m.corner(TopRight, rank, dimker) ); // now we must undo the column permutation that we had applied! - for(int i = m_rank-1; i >= 0; --i) + for(int i = rank-1; i >= 0; --i) m.col(i).swap(m.col(pivots.coeff(i))); // see the negative sign in the next line, that's what we were talking about above. - for(int i = 0; i < m_rank; ++i) dst.row(m_lu.permutationQ().coeff(i)) = -m.row(i).end(dimker); - for(int i = m_rank; i < cols; ++i) dst.row(m_lu.permutationQ().coeff(i)).setZero(); - for(int k = 0; k < dimker; ++k) dst.coeffRef(m_lu.permutationQ().coeff(m_rank+k), k) = Scalar(1); + for(int i = 0; i < rank; ++i) dst.row(dec.permutationQ().coeff(i)) = -m.row(i).end(dimker); + for(int i = rank; i < cols; ++i) dst.row(dec.permutationQ().coeff(i)).setZero(); + for(int k = 0; k < dimker; ++k) dst.coeffRef(dec.permutationQ().coeff(rank+k), k) = Scalar(1); } }; /***** Implementation of image() *****************************************************/ -template -struct ei_traits > +template +struct ei_image_impl, Dest> + : ei_image_return_value > { - typedef Matrix< - typename MatrixType::Scalar, - MatrixType::RowsAtCompileTime, // the image is a subspace of the destination space, whose - // dimension is the number of rows of the original matrix - Dynamic, // we don't know at compile time the dimension of the image (the rank) - MatrixType::Options, - MatrixType::MaxRowsAtCompileTime, // the image matrix will consist of columns from the original matrix, - MatrixType::MaxColsAtCompileTime // so it has the same number of rows and at most as many columns. - > ReturnMatrixType; -}; - -template -struct ei_fullpivlu_image_impl : public ReturnByValue > -{ - typedef FullPivLU LUType; + typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; - const LUType& m_lu; - int m_rank, m_cols; - const MatrixType& m_originalMatrix; - - ei_fullpivlu_image_impl(const LUType& lu, const MatrixType& originalMatrix) - : m_lu(lu), m_rank(lu.rank()), - m_cols(m_rank == 0 ? 1 : m_rank), - m_originalMatrix(originalMatrix) {} + enum { MaxSmallDimAtCompileTime = EIGEN_ENUM_MIN( + MatrixType::MaxColsAtCompileTime, + MatrixType::MaxRowsAtCompileTime) + }; - inline int rows() const { return m_lu.matrixLU().rows(); } - inline int cols() const { return m_cols; } - - template void evalTo(Dest& dst) const + void evalTo(Dest& dst) const { - if(m_rank == 0) + const int rank = this->m_rank; + const FullPivLU& dec = this->m_dec; + const MatrixType& originalMatrix = this->m_originalMatrix; + if(rank == 0) { // The Image is just {0}, so it doesn't have a basis properly speaking, but let's // avoid crashing/asserting as that depends on floating point calculations. Let's @@ -640,61 +596,40 @@ struct ei_fullpivlu_image_impl : public ReturnByValue pivots(m_rank); - RealScalar premultiplied_threshold = m_lu.maxPivot() * m_lu.threshold(); + Matrix pivots(rank); + RealScalar premultiplied_threshold = dec.maxPivot() * dec.threshold(); int p = 0; - for(int i = 0; i < m_lu.nonzeroPivots(); ++i) - if(ei_abs(m_lu.matrixLU().coeff(i,i)) > premultiplied_threshold) + for(int i = 0; i < dec.nonzeroPivots(); ++i) + if(ei_abs(dec.matrixLU().coeff(i,i)) > premultiplied_threshold) pivots.coeffRef(p++) = i; - ei_assert(p == m_rank && "You hit a bug in Eigen! Please report (backtrace and matrix)!"); + ei_internal_assert(p == rank); - for(int i = 0; i < m_rank; ++i) - dst.col(i) = m_originalMatrix.col(m_lu.permutationQ().coeff(pivots.coeff(i))); + for(int i = 0; i < rank; ++i) + dst.col(i) = originalMatrix.col(dec.permutationQ().coeff(pivots.coeff(i))); } }; /***** Implementation of solve() *****************************************************/ -template -struct ei_traits > +template +struct ei_solve_impl, Rhs, Dest> + : ei_solve_return_value, Rhs> { - typedef Matrix ReturnMatrixType; -}; - -template -struct ei_fullpivlu_solve_impl : public ReturnByValue > -{ - typedef typename ei_cleantype::type RhsNested; - typedef FullPivLU LUType; - const LUType& m_lu; - const typename Rhs::Nested m_rhs; - - ei_fullpivlu_solve_impl(const LUType& lu, const Rhs& rhs) - : m_lu(lu), m_rhs(rhs) - {} - - inline int rows() const { return m_lu.matrixLU().cols(); } - inline int cols() const { return m_rhs.cols(); } - - template void evalTo(Dest& dst) const + void evalTo(Dest& dst) const { /* The decomposition PAQ = LU can be rewritten as A = P^{-1} L U Q^{-1}. - * So we proceed as follows: - * Step 1: compute c = P * rhs. - * Step 2: replace c by the solution x to Lx = c. Exists because L is invertible. - * Step 3: replace c by the solution x to Ux = c. May or may not exist. - * Step 4: result = Q * c; - */ + * So we proceed as follows: + * Step 1: compute c = P * rhs. + * Step 2: replace c by the solution x to Lx = c. Exists because L is invertible. + * Step 3: replace c by the solution x to Ux = c. May or may not exist. + * Step 4: result = Q * c; + */ - const int rows = m_lu.matrixLU().rows(), - cols = m_lu.matrixLU().cols(), - nonzero_pivots = m_lu.nonzeroPivots(); - ei_assert(m_rhs.rows() == rows); + const FullPivLU& dec = this->m_dec; + const Rhs& rhs = this->m_rhs; + const int rows = dec.matrixLU().rows(), cols = dec.matrixLU().cols(), + nonzero_pivots = dec.nonzeroPivots(); + ei_assert(rhs.rows() == rows); const int smalldim = std::min(rows, cols); if(nonzero_pivots == 0) @@ -702,36 +637,36 @@ struct ei_fullpivlu_solve_impl : public ReturnByValue() .solveInPlace(c.corner(Eigen::TopLeft, smalldim, c.cols())); if(rows>cols) { c.corner(Eigen::BottomLeft, rows-cols, c.cols()) - -= m_lu.matrixLU().corner(Eigen::BottomLeft, rows-cols, cols) - * c.corner(Eigen::TopLeft, cols, c.cols()); + -= dec.matrixLU().corner(Eigen::BottomLeft, rows-cols, cols) + * c.corner(Eigen::TopLeft, cols, c.cols()); } // Step 3 - m_lu.matrixLU() + dec.matrixLU() .corner(TopLeft, nonzero_pivots, nonzero_pivots) .template triangularView() .solveInPlace(c.corner(TopLeft, nonzero_pivots, c.cols())); // Step 4 for(int i = 0; i < nonzero_pivots; ++i) - dst.row(m_lu.permutationQ().coeff(i)) = c.row(i); - for(int i = nonzero_pivots; i < m_lu.matrixLU().cols(); ++i) - dst.row(m_lu.permutationQ().coeff(i)).setZero(); + dst.row(dec.permutationQ().coeff(i)) = c.row(i); + for(int i = nonzero_pivots; i < dec.matrixLU().cols(); ++i) + dst.row(dec.permutationQ().coeff(i)).setZero(); } }; diff --git a/Eigen/src/misc/CMakeLists.txt b/Eigen/src/misc/CMakeLists.txt new file mode 100644 index 000000000..a58ffb745 --- /dev/null +++ b/Eigen/src/misc/CMakeLists.txt @@ -0,0 +1,6 @@ +FILE(GLOB Eigen_misc_SRCS "*.h") + +INSTALL(FILES + ${Eigen_misc_SRCS} + DESTINATION ${INCLUDE_INSTALL_DIR}/Eigen/src/misc COMPONENT Devel + ) diff --git a/Eigen/src/misc/Image.h b/Eigen/src/misc/Image.h new file mode 100644 index 000000000..a7e2bceec --- /dev/null +++ b/Eigen/src/misc/Image.h @@ -0,0 +1,72 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 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_MISC_IMAGE_H +#define EIGEN_MISC_IMAGE_H + +/** \class ei_image_return_value + * + */ +template +struct ei_traits > +{ + typedef typename DecompositionType::MatrixType MatrixType; + typedef Matrix< + typename MatrixType::Scalar, + MatrixType::RowsAtCompileTime, // the image is a subspace of the destination space, whose + // dimension is the number of rows of the original matrix + Dynamic, // we don't know at compile time the dimension of the image (the rank) + MatrixType::Options, + MatrixType::MaxRowsAtCompileTime, // the image matrix will consist of columns from the original matrix, + MatrixType::MaxColsAtCompileTime // so it has the same number of rows and at most as many columns. + > ReturnMatrixType; +}; + +template struct ei_image_return_value + : public ReturnByValue > +{ + typedef _DecompositionType DecompositionType; + typedef typename DecompositionType::MatrixType MatrixType; + + const DecompositionType& m_dec; + int m_rank, m_cols; + const MatrixType& m_originalMatrix; + + ei_image_return_value(const DecompositionType& dec, const MatrixType& originalMatrix) + : m_dec(dec), m_rank(dec.rank()), + m_cols(m_rank == 0 ? 1 : m_rank), + m_originalMatrix(originalMatrix) + {} + + inline int rows() const { return m_dec.rows(); } + inline int cols() const { return m_cols; } + + template inline void evalTo(Dest& dst) const + { + static_cast *> + (this)->evalTo(dst); + } +}; + +#endif // EIGEN_MISC_IMAGE_H diff --git a/Eigen/src/misc/Kernel.h b/Eigen/src/misc/Kernel.h new file mode 100644 index 000000000..bfd75f54b --- /dev/null +++ b/Eigen/src/misc/Kernel.h @@ -0,0 +1,71 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 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_MISC_KERNEL_H +#define EIGEN_MISC_KERNEL_H + +/** \class ei_kernel_return_value + * + */ +template +struct ei_traits > +{ + typedef typename DecompositionType::MatrixType MatrixType; + typedef Matrix< + typename MatrixType::Scalar, + MatrixType::ColsAtCompileTime, // the number of rows in the "kernel matrix" + // is the number of cols of the original matrix + // so that the product "matrix * kernel = zero" makes sense + Dynamic, // we don't know at compile-time the dimension of the kernel + MatrixType::Options, + MatrixType::MaxColsAtCompileTime, // see explanation for 2nd template parameter + MatrixType::MaxColsAtCompileTime // the kernel is a subspace of the domain space, + // whose dimension is the number of columns of the original matrix + > ReturnMatrixType; +}; + +template struct ei_kernel_return_value + : public ReturnByValue > +{ + typedef _DecompositionType DecompositionType; + const DecompositionType& m_dec; + int m_rank, m_cols; + + ei_kernel_return_value(const DecompositionType& dec) + : m_dec(dec), + m_rank(dec.rank()), + m_cols(m_rank==dec.cols() ? 1 : dec.cols() - m_rank) + {} + + inline int rows() const { return m_dec.cols(); } + inline int cols() const { return m_cols; } + + template inline void evalTo(Dest& dst) const + { + static_cast *> + (this)->evalTo(dst); + } +}; + +#endif // EIGEN_MISC_KERNEL_H diff --git a/Eigen/src/misc/Solve.h b/Eigen/src/misc/Solve.h new file mode 100644 index 000000000..c62e34b13 --- /dev/null +++ b/Eigen/src/misc/Solve.h @@ -0,0 +1,65 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 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_MISC_SOLVE_H +#define EIGEN_MISC_SOLVE_H + +/** \class ei_solve_return_value + * + */ +template +struct ei_traits > +{ + typedef typename DecompositionType::MatrixType MatrixType; + typedef Matrix ReturnMatrixType; +}; + +template struct ei_solve_return_value + : public ReturnByValue > +{ + typedef typename ei_cleantype::type RhsNestedCleaned; + typedef _DecompositionType DecompositionType; + const DecompositionType& m_dec; + const typename Rhs::Nested m_rhs; + + ei_solve_return_value(const DecompositionType& dec, const Rhs& rhs) + : m_dec(dec), m_rhs(rhs) + {} + + inline int rows() const { return m_dec.cols(); } + inline int cols() const { return m_rhs.cols(); } + + template inline void evalTo(Dest& dst) const + { + static_cast *> + (this)->evalTo(dst); + } +}; + +#endif // EIGEN_MISC_SOLVE_H diff --git a/test/lu.cpp b/test/lu.cpp index c46ca9130..954893651 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -49,8 +49,8 @@ template void lu_non_invertible() cols2 = cols = MatrixType::ColsAtCompileTime; } - typedef typename ei_fullpivlu_kernel_impl::ReturnMatrixType KernelMatrixType; - typedef typename ei_fullpivlu_image_impl ::ReturnMatrixType ImageMatrixType; + typedef typename ei_kernel_return_value >::ReturnMatrixType KernelMatrixType; + typedef typename ei_image_return_value >::ReturnMatrixType ImageMatrixType; typedef Matrix DynamicMatrixType; typedef Matrix CMatrixType; @@ -65,10 +65,10 @@ template void lu_non_invertible() createRandomMatrixOfRank(rank, rows, cols, m1); FullPivLU lu(m1); + std::cout << lu.kernel().rows() << " " << lu.kernel().cols() << std::endl; KernelMatrixType m1kernel = lu.kernel(); - ImageMatrixType m1image = lu.image(m1); + ImageMatrixType m1image = lu.image(m1); - // std::cerr << rank << " " << lu.rank() << std::endl; VERIFY(rank == lu.rank()); VERIFY(cols - lu.rank() == lu.dimensionOfKernel()); VERIFY(!lu.isInjective()); From a77872dd6c50282ec84e81c2987a5442218fcf8a Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Tue, 3 Nov 2009 03:06:34 -0500 Subject: [PATCH 27/34] move partial-pivoting lu to ei_solve_impl --- Eigen/src/LU/PartialPivLU.h | 61 +++++++++++++------------------------ 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/Eigen/src/LU/PartialPivLU.h b/Eigen/src/LU/PartialPivLU.h index 2a04ec4a9..8f3b7dfc1 100644 --- a/Eigen/src/LU/PartialPivLU.h +++ b/Eigen/src/LU/PartialPivLU.h @@ -26,8 +26,6 @@ #ifndef EIGEN_PARTIALLU_H #define EIGEN_PARTIALLU_H -template struct ei_partialpivlu_solve_impl; - /** \ingroup LU_Module * * \class PartialPivLU @@ -59,10 +57,11 @@ template struct ei_partialpivlu_solve_impl; * * \sa MatrixBase::partialPivLu(), MatrixBase::determinant(), MatrixBase::inverse(), MatrixBase::computeInverse(), class FullPivLU */ -template class PartialPivLU +template class PartialPivLU { public: + typedef _MatrixType MatrixType; typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; typedef Matrix IntRowVectorType; @@ -134,11 +133,11 @@ template class PartialPivLU * \sa TriangularView::solve(), inverse(), computeInverse() */ template - inline const ei_partialpivlu_solve_impl + inline const ei_solve_return_value solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "PartialPivLU is not initialized."); - return ei_partialpivlu_solve_impl(*this, b.derived()); + return ei_solve_return_value(*this, b.derived()); } /** \returns the inverse of the matrix of which *this is the LU decomposition. @@ -148,10 +147,10 @@ template class PartialPivLU * * \sa MatrixBase::inverse(), LU::inverse() */ - inline const ei_partialpivlu_solve_impl > inverse() const + inline const ei_solve_return_value > inverse() const { ei_assert(m_isInitialized && "PartialPivLU is not initialized."); - return ei_partialpivlu_solve_impl > + return ei_solve_return_value > (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); } @@ -170,6 +169,9 @@ template class PartialPivLU */ typename ei_traits::Scalar determinant() const; + inline int rows() const { return m_lu.rows(); } + inline int cols() const { return m_lu.cols(); } + protected: MatrixType m_lu; IntColVectorType m_p; @@ -407,33 +409,11 @@ typename ei_traits::Scalar PartialPivLU::determinant() c /***** Implementation of solve() *****************************************************/ -template -struct ei_traits > +template +struct ei_solve_impl, Rhs, Dest> + : ei_solve_return_value, Rhs> { - typedef Matrix ReturnMatrixType; -}; - -template -struct ei_partialpivlu_solve_impl : public ReturnByValue > -{ - typedef typename ei_cleantype::type RhsNested; - typedef PartialPivLU LUType; - const LUType& m_lu; - const typename Rhs::Nested m_rhs; - - ei_partialpivlu_solve_impl(const LUType& lu, const Rhs& rhs) - : m_lu(lu), m_rhs(rhs) - {} - - inline int rows() const { return m_lu.matrixLU().cols(); } - inline int cols() const { return m_rhs.cols(); } - - template void evalTo(Dest& dst) const + void evalTo(Dest& dst) const { /* The decomposition PA = LU can be rewritten as A = P^{-1} L U. * So we proceed as follows: @@ -442,19 +422,22 @@ struct ei_partialpivlu_solve_impl : public ReturnByValue& dec = this->m_dec; + const Rhs& rhs = this->m_rhs; + + const int size = dec.matrixLU().rows(); + ei_assert(rhs.rows() == size); - dst.resize(size, m_rhs.cols()); + dst.resize(size, rhs.cols()); // Step 1 - for(int i = 0; i < size; ++i) dst.row(m_lu.permutationP().coeff(i)) = m_rhs.row(i); + for(int i = 0; i < size; ++i) dst.row(dec.permutationP().coeff(i)) = rhs.row(i); // Step 2 - m_lu.matrixLU().template triangularView().solveInPlace(dst); + dec.matrixLU().template triangularView().solveInPlace(dst); // Step 3 - m_lu.matrixLU().template triangularView().solveInPlace(dst); + dec.matrixLU().template triangularView().solveInPlace(dst); } }; From 0182695204292540931d4b180a8cb58038f1c735 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Tue, 3 Nov 2009 11:34:45 -0500 Subject: [PATCH 28/34] move cholesky to ei_xxx_return_value --- Eigen/Cholesky | 1 + Eigen/src/Cholesky/LDLT.h | 47 ++++++++++-------------------------- Eigen/src/Cholesky/LLT.h | 51 ++++++++++++--------------------------- test/cholesky.cpp | 3 +++ 4 files changed, 32 insertions(+), 70 deletions(-) diff --git a/Eigen/Cholesky b/Eigen/Cholesky index f1806f726..634dc156f 100644 --- a/Eigen/Cholesky +++ b/Eigen/Cholesky @@ -30,6 +30,7 @@ namespace Eigen { * \endcode */ +#include "src/misc/Solve.h" #include "src/Array/CwiseOperators.h" #include "src/Array/Functors.h" #include "src/Cholesky/LLT.h" diff --git a/Eigen/src/Cholesky/LDLT.h b/Eigen/src/Cholesky/LDLT.h index 04c02d4fc..761a4a8e8 100644 --- a/Eigen/src/Cholesky/LDLT.h +++ b/Eigen/src/Cholesky/LDLT.h @@ -27,8 +27,6 @@ #ifndef EIGEN_LDLT_H #define EIGEN_LDLT_H -template struct ei_ldlt_solve_impl; - /** \ingroup cholesky_Module * * \class LDLT @@ -54,10 +52,10 @@ template struct ei_ldlt_solve_impl; * Note that during the decomposition, only the upper triangular part of A is considered. Therefore, * the strict lower part does not have to store correct values. */ -template class LDLT +template class LDLT { public: - + typedef _MatrixType MatrixType; typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; typedef Matrix VectorType; @@ -126,13 +124,13 @@ template class LDLT * \sa solveInPlace(), MatrixBase::ldlt() */ template - inline const ei_ldlt_solve_impl + inline const ei_solve_return_value solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "LDLT is not initialized."); ei_assert(m_matrix.rows()==b.rows() && "LDLT::solve(): invalid number of rows of the right hand side matrix b"); - return ei_ldlt_solve_impl(*this, b.derived()); + return ei_solve_return_value(*this, b.derived()); } template @@ -149,6 +147,9 @@ template class LDLT ei_assert(m_isInitialized && "LDLT is not initialized."); return m_matrix; } + + inline int rows() const { return m_matrix.rows(); } + inline int cols() const { return m_matrix.cols(); } protected: /** \internal @@ -263,36 +264,14 @@ LDLT& LDLT::compute(const MatrixType& a) return *this; } -template -struct ei_traits > +template +struct ei_solve_impl, Rhs, Dest> + : ei_solve_return_value, Rhs> { - typedef Matrix ReturnMatrixType; -}; - -template -struct ei_ldlt_solve_impl : public ReturnByValue > -{ - typedef typename ei_cleantype::type RhsNested; - typedef LDLT LDLTType; - const LDLTType& m_ldlt; - const typename Rhs::Nested m_rhs; - - ei_ldlt_solve_impl(const LDLTType& ldlt, const Rhs& rhs) - : m_ldlt(ldlt), m_rhs(rhs) - {} - - inline int rows() const { return m_ldlt.matrixLDLT().cols(); } - inline int cols() const { return m_rhs.cols(); } - - template void evalTo(Dest& dst) const + void evalTo(Dest& dst) const { - dst = m_rhs; - m_ldlt.solveInPlace(dst); + dst = this->m_rhs; + this->m_dec.solveInPlace(dst); } }; diff --git a/Eigen/src/Cholesky/LLT.h b/Eigen/src/Cholesky/LLT.h index d6fd514cc..0ad67aa5f 100644 --- a/Eigen/src/Cholesky/LLT.h +++ b/Eigen/src/Cholesky/LLT.h @@ -26,7 +26,6 @@ #define EIGEN_LLT_H template struct LLT_Traits; -template struct ei_llt_solve_impl; /** \ingroup cholesky_Module * @@ -54,9 +53,10 @@ template struct ei_llt_solve_impl; * Note that during the decomposition, only the upper triangular part of A is considered. Therefore, * the strict lower part does not have to store correct values. */ -template class LLT +template class LLT { - private: + public: + typedef _MatrixType MatrixType; typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; typedef Matrix VectorType; @@ -69,8 +69,6 @@ template class LLT typedef LLT_Traits Traits; - public: - /** * \brief Default Constructor. * @@ -111,13 +109,13 @@ template class LLT * \sa solveInPlace(), MatrixBase::llt() */ template - inline const ei_llt_solve_impl + inline const ei_solve_return_value solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "LLT is not initialized."); ei_assert(m_matrix.rows()==b.rows() && "LLT::solve(): invalid number of rows of the right hand side matrix b"); - return ei_llt_solve_impl(*this, b.derived()); + return ei_solve_return_value(*this, b.derived()); } template @@ -134,7 +132,10 @@ template class LLT ei_assert(m_isInitialized && "LLT is not initialized."); return m_matrix; } - + + inline int rows() const { return m_matrix.rows(); } + inline int cols() const { return m_matrix.cols(); } + protected: /** \internal * Used to compute and store L @@ -257,36 +258,14 @@ LLT& LLT::compute(const MatrixType& a) return *this; } -template -struct ei_traits > +template +struct ei_solve_impl, Rhs, Dest> + : ei_solve_return_value, Rhs> { - typedef Matrix ReturnMatrixType; -}; - -template -struct ei_llt_solve_impl : public ReturnByValue > -{ - typedef typename ei_cleantype::type RhsNested; - typedef LLT LLTType; - const LLTType& m_llt; - const typename Rhs::Nested m_rhs; - - ei_llt_solve_impl(const LLTType& llt, const Rhs& rhs) - : m_llt(llt), m_rhs(rhs) - {} - - inline int rows() const { return m_llt.matrixLLT().cols(); } - inline int cols() const { return m_rhs.cols(); } - - template void evalTo(Dest& dst) const + void evalTo(Dest& dst) const { - dst = m_rhs; - m_llt.solveInPlace(dst); + dst = this->m_rhs; + this->m_dec.solveInPlace(dst); } }; diff --git a/test/cholesky.cpp b/test/cholesky.cpp index 83f7e0492..c3ef96752 100644 --- a/test/cholesky.cpp +++ b/test/cholesky.cpp @@ -22,7 +22,10 @@ // License and a copy of the GNU General Public License along with // Eigen. If not, see . +#ifndef EIGEN_NO_ASSERTION_CHECKING #define EIGEN_NO_ASSERTION_CHECKING +#endif + #include "main.h" #include #include From 68210b03c17915b49655dfa4a13c28cc31a59092 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 4 Nov 2009 21:00:12 -0500 Subject: [PATCH 29/34] port svd to the ei_xxx_return_value thing this commit made in caltrain from Palo Alto to SF --- Eigen/src/SVD/SVD.h | 58 +++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h index 807e7058c..b43123384 100644 --- a/Eigen/src/SVD/SVD.h +++ b/Eigen/src/SVD/SVD.h @@ -40,9 +40,10 @@ template struct ei_svd_solve_impl; * * \sa MatrixBase::SVD() */ -template class SVD +template class SVD { public: + typedef _MatrixType MatrixType; typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; @@ -90,11 +91,11 @@ template class SVD * \sa MatrixBase::svd(), */ template - inline const ei_svd_solve_impl + inline const ei_solve_return_value solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "SVD is not initialized."); - return ei_svd_solve_impl(*this, b.derived()); + return ei_solve_return_value(*this, b.derived()); } const MatrixUType& matrixU() const @@ -428,54 +429,33 @@ SVD& SVD::compute(const MatrixType& matrix) return *this; } -template -struct ei_traits > +template +struct ei_solve_impl, Rhs, Dest> + : ei_solve_return_value, Rhs> { - typedef Matrix ReturnMatrixType; -}; - -template -struct ei_svd_solve_impl : public ReturnByValue > -{ - typedef typename ei_cleantype::type RhsNested; - typedef SVD SVDType; - typedef typename MatrixType::RealScalar RealScalar; - typedef typename MatrixType::Scalar Scalar; - const SVDType& m_svd; - const typename Rhs::Nested m_rhs; - - ei_svd_solve_impl(const SVDType& svd, const Rhs& rhs) - : m_svd(svd), m_rhs(rhs) - {} - - inline int rows() const { return m_svd.cols(); } - inline int cols() const { return m_rhs.cols(); } - - template void evalTo(Dest& dst) const + void evalTo(Dest& dst) const { - ei_assert(m_rhs.rows() == m_svd.rows()); + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + const int cols = this->cols(); + const SVD& svd = this->m_dec; + const Rhs& rhs = this->m_rhs; + ei_assert(rhs.rows() == svd.rows()); - dst.resize(rows(), cols()); - - for (int j=0; j aux = m_svd.matrixU().adjoint() * m_rhs.col(j); + Matrix aux = svd.matrixU().adjoint() * rhs.col(j); - for (int i = 0; i Date: Sun, 8 Nov 2009 10:21:26 -0500 Subject: [PATCH 30/34] port the qr module to ei_solve_xxx. --- Eigen/src/LU/PartialPivLU.h | 2 - Eigen/src/QR/ColPivHouseholderQR.h | 139 +++++++++++++------------- Eigen/src/QR/FullPivHouseholderQR.h | 149 ++++++++++++++-------------- Eigen/src/QR/HouseholderQR.h | 72 ++++++++------ Eigen/src/SVD/SVD.h | 1 - test/qr.cpp | 6 +- test/qr_colpivoting.cpp | 13 +-- test/qr_fullpivoting.cpp | 9 +- 8 files changed, 195 insertions(+), 196 deletions(-) diff --git a/Eigen/src/LU/PartialPivLU.h b/Eigen/src/LU/PartialPivLU.h index 8f3b7dfc1..eeec3533f 100644 --- a/Eigen/src/LU/PartialPivLU.h +++ b/Eigen/src/LU/PartialPivLU.h @@ -198,8 +198,6 @@ PartialPivLU::PartialPivLU(const MatrixType& matrix) compute(matrix); } - - /** This is the blocked version of ei_fullpivlu_unblocked() */ template struct ei_partial_lu_impl diff --git a/Eigen/src/QR/ColPivHouseholderQR.h b/Eigen/src/QR/ColPivHouseholderQR.h index 05287ff3c..a774fdd73 100644 --- a/Eigen/src/QR/ColPivHouseholderQR.h +++ b/Eigen/src/QR/ColPivHouseholderQR.h @@ -42,17 +42,17 @@ * * \sa MatrixBase::colPivHouseholderQr() */ -template class ColPivHouseholderQR +template class ColPivHouseholderQR { public: + typedef _MatrixType MatrixType; enum { RowsAtCompileTime = MatrixType::RowsAtCompileTime, ColsAtCompileTime = MatrixType::ColsAtCompileTime, Options = MatrixType::Options, DiagSizeAtCompileTime = EIGEN_ENUM_MIN(ColsAtCompileTime,RowsAtCompileTime) }; - typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; typedef Matrix MatrixQType; @@ -83,22 +83,27 @@ template class ColPivHouseholderQR /** This method finds a solution x to the equation Ax=b, where A is the matrix of which * *this is the QR decomposition, if any exists. * - * \returns \c true if a solution exists, \c false if no solution exists. - * * \param b the right-hand-side of the equation to solve. * - * \param result a pointer to the vector/matrix in which to store the solution, if any exists. - * Resized if necessary, so that result->rows()==A.cols() and result->cols()==b.cols(). - * If no solution exists, *result is left with undefined coefficients. + * \returns a solution. * * \note The case where b is a matrix is not yet implemented. Also, this * code is space inefficient. * + * \note_about_checking_solutions + * + * \note_about_arbitrary_choice_of_solution + * * Example: \include ColPivHouseholderQR_solve.cpp * Output: \verbinclude ColPivHouseholderQR_solve.out */ - template - bool solve(const MatrixBase& b, ResultType *result) const; + template + inline const ei_solve_return_value + solve(const MatrixBase& b) const + { + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); + return ei_solve_return_value(*this, b.derived()); + } HouseholderSequenceType matrixQ(void) const; @@ -204,36 +209,24 @@ template class ColPivHouseholderQR return isInjective() && isSurjective(); } - /** Computes the inverse of the matrix of which *this is the QR decomposition. - * - * \param result a pointer to the matrix into which to store the inverse. Resized if needed. - * - * \note If this matrix is not invertible, *result is left with undefined coefficients. - * Use isInvertible() to first determine whether this matrix is invertible. - * - * \sa inverse() - */ - inline void computeInverse(MatrixType *result) const - { - ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); - ei_assert(m_qr.rows() == m_qr.cols() && "You can't take the inverse of a non-square matrix!"); - solve(MatrixType::Identity(m_qr.rows(), m_qr.cols()), result); - } - /** \returns the inverse of the matrix of which *this is the QR decomposition. * * \note If this matrix is not invertible, the returned matrix has undefined coefficients. * Use isInvertible() to first determine whether this matrix is invertible. - * - * \sa computeInverse() */ - inline MatrixType inverse() const + inline const + ei_solve_return_value > + inverse() const { - MatrixType result; - computeInverse(&result); - return result; + ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); + return ei_solve_return_value > + (*this, MatrixType::Identity(m_qr.rows(), m_qr.cols()).nestByValue()); } + inline int rows() const { return m_qr.rows(); } + inline int cols() const { return m_qr.cols(); } + const HCoeffsType& hCoeffs() const { return m_hCoeffs; } + protected: MatrixType m_qr; HCoeffsType m_hCoeffs; @@ -331,50 +324,56 @@ ColPivHouseholderQR& ColPivHouseholderQR::compute(const return *this; } -template -template -bool ColPivHouseholderQR::solve( - const MatrixBase& b, - ResultType *result -) const +template +struct ei_solve_impl, Rhs, Dest> + : ei_solve_return_value, Rhs> { - ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); - result->resize(m_qr.cols(), b.cols()); - if(m_rank==0) + void evalTo(Dest& dst) const { - if(b.squaredNorm() == RealScalar(0)) + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + const ColPivHouseholderQR& dec = this->m_dec; + const Rhs& rhs = this->m_rhs; + const int rows = dec.rows(), cols = dec.cols(); + dst.resize(cols, rhs.cols()); + ei_assert(rhs.rows() == rows); + + // FIXME introduce nonzeroPivots() and use it here. and more generally, + // make the same improvements in this dec as in FullPivLU. + if(dec.rank()==0) { - result->setZero(); - return true; + dst.setZero(); + return; } - else return false; + + typename Rhs::PlainMatrixType c(rhs); + + // Note that the matrix Q = H_0^* H_1^*... so its inverse is Q^* = (H_0 H_1 ...)^T + c.applyOnTheLeft(makeHouseholderSequence( + dec.matrixQR().corner(TopLeft,rows,dec.rank()), + dec.hCoeffs().start(dec.rank())).transpose() + ); + + if(!dec.isSurjective()) + { + // is c is in the image of R ? + RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, dec.rank(), c.cols()).cwise().abs().maxCoeff(); + RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-dec.rank(), c.cols()).cwise().abs().maxCoeff(); + // FIXME brain dead + const RealScalar m_precision = epsilon() * std::min(rows,cols); + if(!ei_isMuchSmallerThan(biggest_in_lower_part_of_c, biggest_in_upper_part_of_c, m_precision*4)) + return; + } + + dec.matrixQR() + .corner(TopLeft, dec.rank(), dec.rank()) + .template triangularView() + .solveInPlace(c.corner(TopLeft, dec.rank(), c.cols())); + + for(int i = 0; i < dec.rank(); ++i) dst.row(dec.colsPermutation().coeff(i)) = c.row(i); + for(int i = dec.rank(); i < cols; ++i) dst.row(dec.colsPermutation().coeff(i)).setZero(); } - - const int rows = m_qr.rows(); - ei_assert(b.rows() == rows); - - typename OtherDerived::PlainMatrixType c(b); - - // Note that the matrix Q = H_0^* H_1^*... so its inverse is Q^* = (H_0 H_1 ...)^T - c.applyOnTheLeft(makeHouseholderSequence(m_qr.corner(TopLeft,rows,m_rank), m_hCoeffs.start(m_rank)).transpose()); - - if(!isSurjective()) - { - // is c is in the image of R ? - RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, m_rank, c.cols()).cwise().abs().maxCoeff(); - RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-m_rank, c.cols()).cwise().abs().maxCoeff(); - if(!ei_isMuchSmallerThan(biggest_in_lower_part_of_c, biggest_in_upper_part_of_c, m_precision*4)) - return false; - } - - m_qr.corner(TopLeft, m_rank, m_rank) - .template triangularView() - .solveInPlace(c.corner(TopLeft, m_rank, c.cols())); - - for(int i = 0; i < m_rank; ++i) result->row(m_cols_permutation.coeff(i)) = c.row(i); - for(int i = m_rank; i < m_qr.cols(); ++i) result->row(m_cols_permutation.coeff(i)).setZero(); - return true; -} +}; /** \returns the matrix Q as a sequence of householder transformations */ template diff --git a/Eigen/src/QR/FullPivHouseholderQR.h b/Eigen/src/QR/FullPivHouseholderQR.h index 07ec343a5..36ec71b95 100644 --- a/Eigen/src/QR/FullPivHouseholderQR.h +++ b/Eigen/src/QR/FullPivHouseholderQR.h @@ -42,17 +42,17 @@ * * \sa MatrixBase::fullPivHouseholderQr() */ -template class FullPivHouseholderQR +template class FullPivHouseholderQR { public: + typedef _MatrixType MatrixType; enum { RowsAtCompileTime = MatrixType::RowsAtCompileTime, ColsAtCompileTime = MatrixType::ColsAtCompileTime, Options = MatrixType::Options, DiagSizeAtCompileTime = EIGEN_ENUM_MIN(ColsAtCompileTime,RowsAtCompileTime) }; - typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; typedef Matrix MatrixQType; @@ -78,22 +78,27 @@ template class FullPivHouseholderQR /** This method finds a solution x to the equation Ax=b, where A is the matrix of which * *this is the QR decomposition, if any exists. * - * \returns \c true if a solution exists, \c false if no solution exists. - * * \param b the right-hand-side of the equation to solve. * - * \param result a pointer to the vector/matrix in which to store the solution, if any exists. - * Resized if necessary, so that result->rows()==A.cols() and result->cols()==b.cols(). - * If no solution exists, *result is left with undefined coefficients. + * \returns a solution. * * \note The case where b is a matrix is not yet implemented. Also, this * code is space inefficient. * + * \note_about_checking_solutions + * + * \note_about_arbitrary_choice_of_solution + * * Example: \include FullPivHouseholderQR_solve.cpp * Output: \verbinclude FullPivHouseholderQR_solve.out */ - template - bool solve(const MatrixBase& b, ResultType *result) const; + template + inline const ei_solve_return_value + solve(const MatrixBase& b) const + { + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); + return ei_solve_return_value(*this, b.derived()); + } MatrixQType matrixQ(void) const; @@ -205,36 +210,23 @@ template class FullPivHouseholderQR return isInjective() && isSurjective(); } - /** Computes the inverse of the matrix of which *this is the QR decomposition. - * - * \param result a pointer to the matrix into which to store the inverse. Resized if needed. - * - * \note If this matrix is not invertible, *result is left with undefined coefficients. - * Use isInvertible() to first determine whether this matrix is invertible. - * - * \sa inverse() - */ - inline void computeInverse(MatrixType *result) const - { - ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); - ei_assert(m_qr.rows() == m_qr.cols() && "You can't take the inverse of a non-square matrix!"); - solve(MatrixType::Identity(m_qr.rows(), m_qr.cols()), result); - } - /** \returns the inverse of the matrix of which *this is the QR decomposition. * * \note If this matrix is not invertible, the returned matrix has undefined coefficients. * Use isInvertible() to first determine whether this matrix is invertible. - * - * \sa computeInverse() - */ - inline MatrixType inverse() const + */ inline const + ei_solve_return_value > + inverse() const { - MatrixType result; - computeInverse(&result); - return result; + ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); + return ei_solve_return_value > + (*this, MatrixType::Identity(m_qr.rows(), m_qr.cols()).nestByValue()); } + inline int rows() const { return m_qr.rows(); } + inline int cols() const { return m_qr.cols(); } + const HCoeffsType& hCoeffs() const { return m_hCoeffs; } + protected: MatrixType m_qr; HCoeffsType m_hCoeffs; @@ -340,56 +332,59 @@ FullPivHouseholderQR& FullPivHouseholderQR::compute(cons return *this; } -template -template -bool FullPivHouseholderQR::solve( - const MatrixBase& b, - ResultType *result -) const +template +struct ei_solve_impl, Rhs, Dest> + : ei_solve_return_value, Rhs> { - ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); - result->resize(m_qr.cols(), b.cols()); - if(m_rank==0) + void evalTo(Dest& dst) const { - if(b.squaredNorm() == RealScalar(0)) + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + const FullPivHouseholderQR& dec = this->m_dec; + const Rhs& rhs = this->m_rhs; + const int rows = dec.rows(), cols = dec.cols(); + dst.resize(cols, rhs.cols()); + ei_assert(rhs.rows() == rows); + + // FIXME introduce nonzeroPivots() and use it here. and more generally, + // make the same improvements in this dec as in FullPivLU. + if(dec.rank()==0) { - result->setZero(); - return true; + dst.setZero(); + return; } - else return false; + + typename Rhs::PlainMatrixType c(rhs); + + Matrix temp(rhs.cols()); + for (int k = 0; k < dec.rank(); ++k) + { + int remainingSize = rows-k; + c.row(k).swap(c.row(dec.rowsTranspositions().coeff(k))); + c.corner(BottomRight, remainingSize, rhs.cols()) + .applyHouseholderOnTheLeft(dec.matrixQR().col(k).end(remainingSize-1), + dec.hCoeffs().coeff(k), &temp.coeffRef(0)); + } + + if(!dec.isSurjective()) + { + // is c is in the image of R ? + RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, dec.rank(), c.cols()).cwise().abs().maxCoeff(); + RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-dec.rank(), c.cols()).cwise().abs().maxCoeff(); + // FIXME brain dead + const RealScalar m_precision = epsilon() * std::min(rows,cols); + if(!ei_isMuchSmallerThan(biggest_in_lower_part_of_c, biggest_in_upper_part_of_c, m_precision)) + return; + } + dec.matrixQR() + .corner(TopLeft, dec.rank(), dec.rank()) + .template triangularView() + .solveInPlace(c.corner(TopLeft, dec.rank(), c.cols())); + + for(int i = 0; i < dec.rank(); ++i) dst.row(dec.colsPermutation().coeff(i)) = c.row(i); + for(int i = dec.rank(); i < cols; ++i) dst.row(dec.colsPermutation().coeff(i)).setZero(); } - - const int rows = m_qr.rows(); - const int cols = b.cols(); - ei_assert(b.rows() == rows); - - typename OtherDerived::PlainMatrixType c(b); - - Matrix temp(cols); - for (int k = 0; k < m_rank; ++k) - { - int remainingSize = rows-k; - c.row(k).swap(c.row(m_rows_transpositions.coeff(k))); - c.corner(BottomRight, remainingSize, cols) - .applyHouseholderOnTheLeft(m_qr.col(k).end(remainingSize-1), m_hCoeffs.coeff(k), &temp.coeffRef(0)); - } - - if(!isSurjective()) - { - // is c is in the image of R ? - RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, m_rank, c.cols()).cwise().abs().maxCoeff(); - RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-m_rank, c.cols()).cwise().abs().maxCoeff(); - if(!ei_isMuchSmallerThan(biggest_in_lower_part_of_c, biggest_in_upper_part_of_c, m_precision)) - return false; - } - m_qr.corner(TopLeft, m_rank, m_rank) - .template triangularView() - .solveInPlace(c.corner(TopLeft, m_rank, c.cols())); - - for(int i = 0; i < m_rank; ++i) result->row(m_cols_permutation.coeff(i)) = c.row(i); - for(int i = m_rank; i < m_qr.cols(); ++i) result->row(m_cols_permutation.coeff(i)).setZero(); - return true; -} +}; /** \returns the matrix Q */ template diff --git a/Eigen/src/QR/HouseholderQR.h b/Eigen/src/QR/HouseholderQR.h index a32aa4eaf..6db0411d9 100644 --- a/Eigen/src/QR/HouseholderQR.h +++ b/Eigen/src/QR/HouseholderQR.h @@ -46,17 +46,17 @@ * * \sa MatrixBase::householderQr() */ -template class HouseholderQR +template class HouseholderQR { public: + typedef _MatrixType MatrixType; enum { RowsAtCompileTime = MatrixType::RowsAtCompileTime, ColsAtCompileTime = MatrixType::ColsAtCompileTime, Options = MatrixType::Options, DiagSizeAtCompileTime = EIGEN_ENUM_MIN(ColsAtCompileTime,RowsAtCompileTime) }; - typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; typedef Matrix::Flags&RowMajorBit ? RowMajor : ColMajor)> MatrixQType; @@ -85,19 +85,26 @@ template class HouseholderQR * * \param b the right-hand-side of the equation to solve. * - * \param result a pointer to the vector/matrix in which to store the solution, if any exists. - * Resized if necessary, so that result->rows()==A.cols() and result->cols()==b.cols(). - * If no solution exists, *result is left with undefined coefficients. + * \returns a solution. * * \note The case where b is a matrix is not yet implemented. Also, this * code is space inefficient. * + * \note_about_checking_solutions + * + * \note_about_arbitrary_choice_of_solution + * * Example: \include HouseholderQR_solve.cpp * Output: \verbinclude HouseholderQR_solve.out */ - template - void solve(const MatrixBase& b, ResultType *result) const; - + template + inline const ei_solve_return_value + solve(const MatrixBase& b) const + { + ei_assert(m_isInitialized && "HouseholderQR is not initialized."); + return ei_solve_return_value(*this, b.derived()); + } + MatrixQType matrixQ() const; HouseholderSequenceType matrixQAsHouseholderSequence() const @@ -145,6 +152,10 @@ template class HouseholderQR */ typename MatrixType::RealScalar logAbsDeterminant() const; + inline int rows() const { return m_qr.rows(); } + inline int cols() const { return m_qr.cols(); } + const HCoeffsType& hCoeffs() const { return m_hCoeffs; } + protected: MatrixType m_qr; HCoeffsType m_hCoeffs; @@ -198,31 +209,36 @@ HouseholderQR& HouseholderQR::compute(const MatrixType& return *this; } -template -template -void HouseholderQR::solve( - const MatrixBase& b, - ResultType *result -) const +template +struct ei_solve_impl, Rhs, Dest> + : ei_solve_return_value, Rhs> { - ei_assert(m_isInitialized && "HouseholderQR is not initialized."); - result->derived().resize(m_qr.cols(), b.cols()); - const int rows = m_qr.rows(); - const int rank = std::min(m_qr.rows(), m_qr.cols()); - ei_assert(b.rows() == rows); + void evalTo(Dest& dst) const + { + const HouseholderQR& dec = this->m_dec; + const Rhs& rhs = this->m_rhs; + const int rows = dec.rows(), cols = dec.cols(); + dst.resize(cols, rhs.cols()); + const int rank = std::min(rows, cols); + ei_assert(rhs.rows() == rows); - typename OtherDerived::PlainMatrixType c(b); + typename Rhs::PlainMatrixType c(rhs); - // Note that the matrix Q = H_0^* H_1^*... so its inverse is Q^* = (H_0 H_1 ...)^T - c.applyOnTheLeft(makeHouseholderSequence(m_qr.corner(TopLeft,rows,rank), m_hCoeffs.start(rank)).transpose()); + // Note that the matrix Q = H_0^* H_1^*... so its inverse is Q^* = (H_0 H_1 ...)^T + c.applyOnTheLeft(makeHouseholderSequence( + dec.matrixQR().corner(TopLeft,rows,rank), + dec.hCoeffs().start(rank)).transpose() + ); - m_qr.corner(TopLeft, rank, rank) - .template triangularView() - .solveInPlace(c.corner(TopLeft, rank, c.cols())); + dec.matrixQR() + .corner(TopLeft, rank, rank) + .template triangularView() + .solveInPlace(c.corner(TopLeft, rank, c.cols())); - result->corner(TopLeft, rank, c.cols()) = c.corner(TopLeft,rank, c.cols()); - result->corner(BottomLeft, result->rows()-rank, c.cols()).setZero(); -} + dst.corner(TopLeft, rank, c.cols()) = c.corner(TopLeft, rank, c.cols()); + dst.corner(BottomLeft, cols-rank, c.cols()).setZero(); + } +}; /** \returns the matrix Q */ template diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h index b43123384..8ca425525 100644 --- a/Eigen/src/SVD/SVD.h +++ b/Eigen/src/SVD/SVD.h @@ -86,7 +86,6 @@ template class SVD * \note_about_checking_solutions * * \note_about_arbitrary_choice_of_solution - * \note_about_using_kernel_to_study_multiple_solutions * * \sa MatrixBase::svd(), */ diff --git a/test/qr.cpp b/test/qr.cpp index cbb23c4ca..90b5c4446 100644 --- a/test/qr.cpp +++ b/test/qr.cpp @@ -63,7 +63,7 @@ template void qr_fixedsize() Matrix m2 = Matrix::Random(Cols,Cols2); Matrix m3 = m1*m2; m2 = Matrix::Random(Cols,Cols2); - qr.solve(m3, &m2); + m2 = qr.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); } @@ -86,7 +86,7 @@ template void qr_invertible() HouseholderQR qr(m1); m3 = MatrixType::Random(size,size); - qr.solve(m3, &m2); + m2 = qr.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); // now construct a matrix with prescribed determinant @@ -106,7 +106,7 @@ template void qr_verify_assert() HouseholderQR qr; VERIFY_RAISES_ASSERT(qr.matrixQR()) - VERIFY_RAISES_ASSERT(qr.solve(tmp,&tmp)) + VERIFY_RAISES_ASSERT(qr.solve(tmp)) VERIFY_RAISES_ASSERT(qr.matrixQ()) VERIFY_RAISES_ASSERT(qr.absDeterminant()) VERIFY_RAISES_ASSERT(qr.logAbsDeterminant()) diff --git a/test/qr_colpivoting.cpp b/test/qr_colpivoting.cpp index 406be597d..763c12067 100644 --- a/test/qr_colpivoting.cpp +++ b/test/qr_colpivoting.cpp @@ -61,10 +61,8 @@ template void qr() MatrixType m2 = MatrixType::Random(cols,cols2); MatrixType m3 = m1*m2; m2 = MatrixType::Random(cols,cols2); - VERIFY(qr.solve(m3, &m2)); + m2 = qr.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); - m3 = MatrixType::Random(rows,cols2); - VERIFY(!qr.solve(m3, &m2)); } template void qr_fixedsize() @@ -95,10 +93,8 @@ template void qr_fixedsize() Matrix m2 = Matrix::Random(Cols,Cols2); Matrix m3 = m1*m2; m2 = Matrix::Random(Cols,Cols2); - VERIFY(qr.solve(m3, &m2)); + m2 = qr.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); - m3 = Matrix::Random(Rows,Cols2); - VERIFY(!qr.solve(m3, &m2)); } template void qr_invertible() @@ -120,7 +116,7 @@ template void qr_invertible() ColPivHouseholderQR qr(m1); m3 = MatrixType::Random(size,size); - qr.solve(m3, &m2); + m2 = qr.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); // now construct a matrix with prescribed determinant @@ -140,13 +136,12 @@ template void qr_verify_assert() ColPivHouseholderQR qr; VERIFY_RAISES_ASSERT(qr.matrixQR()) - VERIFY_RAISES_ASSERT(qr.solve(tmp,&tmp)) + VERIFY_RAISES_ASSERT(qr.solve(tmp)) VERIFY_RAISES_ASSERT(qr.matrixQ()) VERIFY_RAISES_ASSERT(qr.dimensionOfKernel()) VERIFY_RAISES_ASSERT(qr.isInjective()) VERIFY_RAISES_ASSERT(qr.isSurjective()) VERIFY_RAISES_ASSERT(qr.isInvertible()) - VERIFY_RAISES_ASSERT(qr.computeInverse(&tmp)) VERIFY_RAISES_ASSERT(qr.inverse()) VERIFY_RAISES_ASSERT(qr.absDeterminant()) VERIFY_RAISES_ASSERT(qr.logAbsDeterminant()) diff --git a/test/qr_fullpivoting.cpp b/test/qr_fullpivoting.cpp index 38ee4eac1..65d9a071f 100644 --- a/test/qr_fullpivoting.cpp +++ b/test/qr_fullpivoting.cpp @@ -61,10 +61,8 @@ template void qr() MatrixType m2 = MatrixType::Random(cols,cols2); MatrixType m3 = m1*m2; m2 = MatrixType::Random(cols,cols2); - VERIFY(qr.solve(m3, &m2)); + m2 = qr.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); - m3 = MatrixType::Random(rows,cols2); - VERIFY(!qr.solve(m3, &m2)); } template void qr_invertible() @@ -90,7 +88,7 @@ template void qr_invertible() VERIFY(qr.isSurjective()); m3 = MatrixType::Random(size,size); - VERIFY(qr.solve(m3, &m2)); + m2 = qr.solve(m3); VERIFY_IS_APPROX(m3, m1*m2); // now construct a matrix with prescribed determinant @@ -110,13 +108,12 @@ template void qr_verify_assert() FullPivHouseholderQR qr; VERIFY_RAISES_ASSERT(qr.matrixQR()) - VERIFY_RAISES_ASSERT(qr.solve(tmp,&tmp)) + VERIFY_RAISES_ASSERT(qr.solve(tmp)) VERIFY_RAISES_ASSERT(qr.matrixQ()) VERIFY_RAISES_ASSERT(qr.dimensionOfKernel()) VERIFY_RAISES_ASSERT(qr.isInjective()) VERIFY_RAISES_ASSERT(qr.isSurjective()) VERIFY_RAISES_ASSERT(qr.isInvertible()) - VERIFY_RAISES_ASSERT(qr.computeInverse(&tmp)) VERIFY_RAISES_ASSERT(qr.inverse()) VERIFY_RAISES_ASSERT(qr.absDeterminant()) VERIFY_RAISES_ASSERT(qr.logAbsDeterminant()) From e4e58e8337e82ba76f6bf4fe7000acac9337056c Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Sun, 8 Nov 2009 16:51:41 -0500 Subject: [PATCH 31/34] simplifications in the ei_solve_impl system, factor out some boilerplate code --- Eigen/src/Cholesky/LDLT.h | 14 +-- Eigen/src/Cholesky/LLT.h | 15 +-- Eigen/src/Core/util/ForwardDeclarations.h | 6 +- Eigen/src/LU/FullPivLU.h | 114 +++++++++++----------- Eigen/src/LU/PartialPivLU.h | 25 +++-- Eigen/src/QR/ColPivHouseholderQR.h | 44 ++++----- Eigen/src/QR/FullPivHouseholderQR.h | 52 +++++----- Eigen/src/QR/HouseholderQR.h | 30 +++--- Eigen/src/SVD/SVD.h | 24 +++-- Eigen/src/misc/Image.h | 11 ++- Eigen/src/misc/Kernel.h | 10 +- Eigen/src/misc/Solve.h | 11 ++- doc/snippets/FullPivLU_image.cpp | 2 +- doc/snippets/FullPivLU_kernel.cpp | 2 +- doc/snippets/FullPivLU_solve.cpp | 2 +- doc/snippets/HouseholderQR_solve.cpp | 2 +- 16 files changed, 189 insertions(+), 175 deletions(-) diff --git a/Eigen/src/Cholesky/LDLT.h b/Eigen/src/Cholesky/LDLT.h index 761a4a8e8..a1faae49c 100644 --- a/Eigen/src/Cholesky/LDLT.h +++ b/Eigen/src/Cholesky/LDLT.h @@ -264,14 +264,16 @@ LDLT& LDLT::compute(const MatrixType& a) return *this; } -template -struct ei_solve_impl, Rhs, Dest> - : ei_solve_return_value, Rhs> +template +struct ei_solve_impl, Rhs> + : ei_solve_return_value, Rhs> { - void evalTo(Dest& dst) const + EIGEN_MAKE_SOLVE_HELPERS(LDLT<_MatrixType>,Rhs) + + template void evalTo(Dest& dst) const { - dst = this->m_rhs; - this->m_dec.solveInPlace(dst); + dst = rhs(); + dec().solveInPlace(dst); } }; diff --git a/Eigen/src/Cholesky/LLT.h b/Eigen/src/Cholesky/LLT.h index 0ad67aa5f..30c48578a 100644 --- a/Eigen/src/Cholesky/LLT.h +++ b/Eigen/src/Cholesky/LLT.h @@ -258,14 +258,17 @@ LLT& LLT::compute(const MatrixType& a) return *this; } -template -struct ei_solve_impl, Rhs, Dest> - : ei_solve_return_value, Rhs> +template +struct ei_solve_impl, Rhs> + : ei_solve_return_value, Rhs> { - void evalTo(Dest& dst) const + typedef LLT<_MatrixType,UpLo> LLTType; + EIGEN_MAKE_SOLVE_HELPERS(LLTType,Rhs) + + template void evalTo(Dest& dst) const { - dst = this->m_rhs; - this->m_dec.solveInPlace(dst); + dst = rhs(); + dec().solveInPlace(dst); } }; diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index b54f71ed1..323848919 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -67,11 +67,11 @@ template struct CommaInitializer; template class ReturnByValue; template struct ei_solve_return_value; -template struct ei_solve_impl; +template struct ei_solve_impl; template struct ei_kernel_return_value; -template struct ei_kernel_impl; +template struct ei_kernel_impl; template struct ei_image_return_value; -template struct ei_image_impl; +template struct ei_image_impl; template class BandMatrix; diff --git a/Eigen/src/LU/FullPivLU.h b/Eigen/src/LU/FullPivLU.h index c5f44dcea..c38789bd9 100644 --- a/Eigen/src/LU/FullPivLU.h +++ b/Eigen/src/LU/FullPivLU.h @@ -485,21 +485,20 @@ typename ei_traits::Scalar FullPivLU::determinant() cons /********* Implementation of kernel() **************************************************/ -template -struct ei_kernel_impl, Dest> - : ei_kernel_return_value > +template +struct ei_kernel_impl > + : ei_kernel_return_value > { - typedef typename MatrixType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; + EIGEN_MAKE_KERNEL_HELPERS(FullPivLU<_MatrixType>) + enum { MaxSmallDimAtCompileTime = EIGEN_ENUM_MIN( MatrixType::MaxColsAtCompileTime, MatrixType::MaxRowsAtCompileTime) }; - void evalTo(Dest& dst) const + template void evalTo(Dest& dst) const { - const FullPivLU& dec = this->m_dec; - const int cols = dec.matrixLU().cols(), rank = this->m_rank, dimker = cols - rank; + const int cols = dec().matrixLU().cols(), dimker = cols - rank(); if(dimker == 0) { // The Kernel is just {0}, so it doesn't have a basis properly speaking, but let's @@ -525,13 +524,13 @@ struct ei_kernel_impl, Dest> * independent vectors in Ker U. */ - Matrix pivots(rank); - RealScalar premultiplied_threshold = dec.maxPivot() * dec.threshold(); + Matrix pivots(rank()); + RealScalar premultiplied_threshold = dec().maxPivot() * dec().threshold(); int p = 0; - for(int i = 0; i < dec.nonzeroPivots(); ++i) - if(ei_abs(dec.matrixLU().coeff(i,i)) > premultiplied_threshold) + for(int i = 0; i < dec().nonzeroPivots(); ++i) + if(ei_abs(dec().matrixLU().coeff(i,i)) > premultiplied_threshold) pivots.coeffRef(p++) = i; - ei_internal_assert(p == rank); + ei_internal_assert(p == rank()); // we construct a temporaty trapezoid matrix m, by taking the U matrix and // permuting the rows and cols to bring the nonnegligible pivots to the top of @@ -539,55 +538,52 @@ struct ei_kernel_impl, Dest> // FIXME when we get triangularView-for-rectangular-matrices, this can be simplified Matrix - m(dec.matrixLU().block(0, 0, rank, cols)); - for(int i = 0; i < rank; ++i) + m(dec().matrixLU().block(0, 0, rank(), cols)); + for(int i = 0; i < rank(); ++i) { if(i) m.row(i).start(i).setZero(); - m.row(i).end(cols-i) = dec.matrixLU().row(pivots.coeff(i)).end(cols-i); + m.row(i).end(cols-i) = dec().matrixLU().row(pivots.coeff(i)).end(cols-i); } - m.block(0, 0, rank, rank); - m.block(0, 0, rank, rank).template triangularView().setZero(); - for(int i = 0; i < rank; ++i) + m.block(0, 0, rank(), rank()); + m.block(0, 0, rank(), rank()).template triangularView().setZero(); + for(int i = 0; i < rank(); ++i) m.col(i).swap(m.col(pivots.coeff(i))); // ok, we have our trapezoid matrix, we can apply the triangular solver. // notice that the math behind this suggests that we should apply this to the // negative of the RHS, but for performance we just put the negative sign elsewhere, see below. - m.corner(TopLeft, rank, rank) + m.corner(TopLeft, rank(), rank()) .template triangularView().solveInPlace( - m.corner(TopRight, rank, dimker) + m.corner(TopRight, rank(), dimker) ); // now we must undo the column permutation that we had applied! - for(int i = rank-1; i >= 0; --i) + for(int i = rank()-1; i >= 0; --i) m.col(i).swap(m.col(pivots.coeff(i))); // see the negative sign in the next line, that's what we were talking about above. - for(int i = 0; i < rank; ++i) dst.row(dec.permutationQ().coeff(i)) = -m.row(i).end(dimker); - for(int i = rank; i < cols; ++i) dst.row(dec.permutationQ().coeff(i)).setZero(); - for(int k = 0; k < dimker; ++k) dst.coeffRef(dec.permutationQ().coeff(rank+k), k) = Scalar(1); + for(int i = 0; i < rank(); ++i) dst.row(dec().permutationQ().coeff(i)) = -m.row(i).end(dimker); + for(int i = rank(); i < cols; ++i) dst.row(dec().permutationQ().coeff(i)).setZero(); + for(int k = 0; k < dimker; ++k) dst.coeffRef(dec().permutationQ().coeff(rank()+k), k) = Scalar(1); } }; /***** Implementation of image() *****************************************************/ -template -struct ei_image_impl, Dest> - : ei_image_return_value > +template +struct ei_image_impl > + : ei_image_return_value > { - typedef typename MatrixType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; + EIGEN_MAKE_IMAGE_HELPERS(FullPivLU<_MatrixType>) + enum { MaxSmallDimAtCompileTime = EIGEN_ENUM_MIN( MatrixType::MaxColsAtCompileTime, MatrixType::MaxRowsAtCompileTime) }; - void evalTo(Dest& dst) const + template void evalTo(Dest& dst) const { - const int rank = this->m_rank; - const FullPivLU& dec = this->m_dec; - const MatrixType& originalMatrix = this->m_originalMatrix; - if(rank == 0) + if(rank() == 0) { // The Image is just {0}, so it doesn't have a basis properly speaking, but let's // avoid crashing/asserting as that depends on floating point calculations. Let's @@ -596,26 +592,28 @@ struct ei_image_impl, Dest> return; } - Matrix pivots(rank); - RealScalar premultiplied_threshold = dec.maxPivot() * dec.threshold(); + Matrix pivots(rank()); + RealScalar premultiplied_threshold = dec().maxPivot() * dec().threshold(); int p = 0; - for(int i = 0; i < dec.nonzeroPivots(); ++i) - if(ei_abs(dec.matrixLU().coeff(i,i)) > premultiplied_threshold) + for(int i = 0; i < dec().nonzeroPivots(); ++i) + if(ei_abs(dec().matrixLU().coeff(i,i)) > premultiplied_threshold) pivots.coeffRef(p++) = i; - ei_internal_assert(p == rank); + ei_internal_assert(p == rank()); - for(int i = 0; i < rank; ++i) - dst.col(i) = originalMatrix.col(dec.permutationQ().coeff(pivots.coeff(i))); + for(int i = 0; i < rank(); ++i) + dst.col(i) = originalMatrix().col(dec().permutationQ().coeff(pivots.coeff(i))); } }; /***** Implementation of solve() *****************************************************/ -template -struct ei_solve_impl, Rhs, Dest> - : ei_solve_return_value, Rhs> +template +struct ei_solve_impl, Rhs> + : ei_solve_return_value, Rhs> { - void evalTo(Dest& dst) const + EIGEN_MAKE_SOLVE_HELPERS(FullPivLU<_MatrixType>,Rhs) + + template void evalTo(Dest& dst) const { /* The decomposition PAQ = LU can be rewritten as A = P^{-1} L U Q^{-1}. * So we proceed as follows: @@ -625,11 +623,9 @@ struct ei_solve_impl, Rhs, Dest> * Step 4: result = Q * c; */ - const FullPivLU& dec = this->m_dec; - const Rhs& rhs = this->m_rhs; - const int rows = dec.matrixLU().rows(), cols = dec.matrixLU().cols(), - nonzero_pivots = dec.nonzeroPivots(); - ei_assert(rhs.rows() == rows); + const int rows = dec().matrixLU().rows(), cols = dec().matrixLU().cols(), + nonzero_pivots = dec().nonzeroPivots(); + ei_assert(rhs().rows() == rows); const int smalldim = std::min(rows, cols); if(nonzero_pivots == 0) @@ -638,35 +634,35 @@ struct ei_solve_impl, Rhs, Dest> return; } - typename Rhs::PlainMatrixType c(rhs.rows(), rhs.cols()); + typename Rhs::PlainMatrixType c(rhs().rows(), rhs().cols()); // Step 1 for(int i = 0; i < rows; ++i) - c.row(dec.permutationP().coeff(i)) = rhs.row(i); + c.row(dec().permutationP().coeff(i)) = rhs().row(i); // Step 2 - dec.matrixLU() + dec().matrixLU() .corner(Eigen::TopLeft,smalldim,smalldim) .template triangularView() .solveInPlace(c.corner(Eigen::TopLeft, smalldim, c.cols())); if(rows>cols) { c.corner(Eigen::BottomLeft, rows-cols, c.cols()) - -= dec.matrixLU().corner(Eigen::BottomLeft, rows-cols, cols) + -= dec().matrixLU().corner(Eigen::BottomLeft, rows-cols, cols) * c.corner(Eigen::TopLeft, cols, c.cols()); } // Step 3 - dec.matrixLU() + dec().matrixLU() .corner(TopLeft, nonzero_pivots, nonzero_pivots) .template triangularView() .solveInPlace(c.corner(TopLeft, nonzero_pivots, c.cols())); // Step 4 for(int i = 0; i < nonzero_pivots; ++i) - dst.row(dec.permutationQ().coeff(i)) = c.row(i); - for(int i = nonzero_pivots; i < dec.matrixLU().cols(); ++i) - dst.row(dec.permutationQ().coeff(i)).setZero(); + dst.row(dec().permutationQ().coeff(i)) = c.row(i); + for(int i = nonzero_pivots; i < dec().matrixLU().cols(); ++i) + dst.row(dec().permutationQ().coeff(i)).setZero(); } }; diff --git a/Eigen/src/LU/PartialPivLU.h b/Eigen/src/LU/PartialPivLU.h index eeec3533f..4f6cda68f 100644 --- a/Eigen/src/LU/PartialPivLU.h +++ b/Eigen/src/LU/PartialPivLU.h @@ -407,11 +407,13 @@ typename ei_traits::Scalar PartialPivLU::determinant() c /***** Implementation of solve() *****************************************************/ -template -struct ei_solve_impl, Rhs, Dest> - : ei_solve_return_value, Rhs> +template +struct ei_solve_impl, Rhs> + : ei_solve_return_value, Rhs> { - void evalTo(Dest& dst) const + EIGEN_MAKE_SOLVE_HELPERS(PartialPivLU<_MatrixType>,Rhs) + + template void evalTo(Dest& dst) const { /* The decomposition PA = LU can be rewritten as A = P^{-1} L U. * So we proceed as follows: @@ -419,23 +421,20 @@ struct ei_solve_impl, Rhs, Dest> * Step 2: replace c by the solution x to Lx = c. * Step 3: replace c by the solution x to Ux = c. */ - - const PartialPivLU& dec = this->m_dec; - const Rhs& rhs = this->m_rhs; - const int size = dec.matrixLU().rows(); - ei_assert(rhs.rows() == size); + const int size = dec().matrixLU().rows(); + ei_assert(rhs().rows() == size); - dst.resize(size, rhs.cols()); + dst.resize(size, rhs().cols()); // Step 1 - for(int i = 0; i < size; ++i) dst.row(dec.permutationP().coeff(i)) = rhs.row(i); + for(int i = 0; i < size; ++i) dst.row(dec().permutationP().coeff(i)) = rhs().row(i); // Step 2 - dec.matrixLU().template triangularView().solveInPlace(dst); + dec().matrixLU().template triangularView().solveInPlace(dst); // Step 3 - dec.matrixLU().template triangularView().solveInPlace(dst); + dec().matrixLU().template triangularView().solveInPlace(dst); } }; diff --git a/Eigen/src/QR/ColPivHouseholderQR.h b/Eigen/src/QR/ColPivHouseholderQR.h index a774fdd73..4e2359e0b 100644 --- a/Eigen/src/QR/ColPivHouseholderQR.h +++ b/Eigen/src/QR/ColPivHouseholderQR.h @@ -324,54 +324,52 @@ ColPivHouseholderQR& ColPivHouseholderQR::compute(const return *this; } -template -struct ei_solve_impl, Rhs, Dest> - : ei_solve_return_value, Rhs> +template +struct ei_solve_impl, Rhs> + : ei_solve_return_value, Rhs> { - void evalTo(Dest& dst) const + EIGEN_MAKE_SOLVE_HELPERS(ColPivHouseholderQR<_MatrixType>,Rhs) + + template void evalTo(Dest& dst) const { - typedef typename MatrixType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; - const ColPivHouseholderQR& dec = this->m_dec; - const Rhs& rhs = this->m_rhs; - const int rows = dec.rows(), cols = dec.cols(); - dst.resize(cols, rhs.cols()); - ei_assert(rhs.rows() == rows); + const int rows = dec().rows(), cols = dec().cols(); + dst.resize(cols, rhs().cols()); + ei_assert(rhs().rows() == rows); // FIXME introduce nonzeroPivots() and use it here. and more generally, // make the same improvements in this dec as in FullPivLU. - if(dec.rank()==0) + if(dec().rank()==0) { dst.setZero(); return; } - typename Rhs::PlainMatrixType c(rhs); + typename Rhs::PlainMatrixType c(rhs()); // Note that the matrix Q = H_0^* H_1^*... so its inverse is Q^* = (H_0 H_1 ...)^T c.applyOnTheLeft(makeHouseholderSequence( - dec.matrixQR().corner(TopLeft,rows,dec.rank()), - dec.hCoeffs().start(dec.rank())).transpose() + dec().matrixQR().corner(TopLeft,rows,dec().rank()), + dec().hCoeffs().start(dec().rank())).transpose() ); - if(!dec.isSurjective()) + if(!dec().isSurjective()) { // is c is in the image of R ? - RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, dec.rank(), c.cols()).cwise().abs().maxCoeff(); - RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-dec.rank(), c.cols()).cwise().abs().maxCoeff(); + RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, dec().rank(), c.cols()).cwise().abs().maxCoeff(); + RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-dec().rank(), c.cols()).cwise().abs().maxCoeff(); // FIXME brain dead const RealScalar m_precision = epsilon() * std::min(rows,cols); if(!ei_isMuchSmallerThan(biggest_in_lower_part_of_c, biggest_in_upper_part_of_c, m_precision*4)) return; } - dec.matrixQR() - .corner(TopLeft, dec.rank(), dec.rank()) + dec().matrixQR() + .corner(TopLeft, dec().rank(), dec().rank()) .template triangularView() - .solveInPlace(c.corner(TopLeft, dec.rank(), c.cols())); + .solveInPlace(c.corner(TopLeft, dec().rank(), c.cols())); - for(int i = 0; i < dec.rank(); ++i) dst.row(dec.colsPermutation().coeff(i)) = c.row(i); - for(int i = dec.rank(); i < cols; ++i) dst.row(dec.colsPermutation().coeff(i)).setZero(); + for(int i = 0; i < dec().rank(); ++i) dst.row(dec().colsPermutation().coeff(i)) = c.row(i); + for(int i = dec().rank(); i < cols; ++i) dst.row(dec().colsPermutation().coeff(i)).setZero(); } }; diff --git a/Eigen/src/QR/FullPivHouseholderQR.h b/Eigen/src/QR/FullPivHouseholderQR.h index 36ec71b95..ba6866fc0 100644 --- a/Eigen/src/QR/FullPivHouseholderQR.h +++ b/Eigen/src/QR/FullPivHouseholderQR.h @@ -332,57 +332,55 @@ FullPivHouseholderQR& FullPivHouseholderQR::compute(cons return *this; } -template -struct ei_solve_impl, Rhs, Dest> - : ei_solve_return_value, Rhs> +template +struct ei_solve_impl, Rhs> + : ei_solve_return_value, Rhs> { - void evalTo(Dest& dst) const + EIGEN_MAKE_SOLVE_HELPERS(FullPivHouseholderQR<_MatrixType>,Rhs) + + template void evalTo(Dest& dst) const { - typedef typename MatrixType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; - const FullPivHouseholderQR& dec = this->m_dec; - const Rhs& rhs = this->m_rhs; - const int rows = dec.rows(), cols = dec.cols(); - dst.resize(cols, rhs.cols()); - ei_assert(rhs.rows() == rows); + const int rows = dec().rows(), cols = dec().cols(); + dst.resize(cols, rhs().cols()); + ei_assert(rhs().rows() == rows); // FIXME introduce nonzeroPivots() and use it here. and more generally, // make the same improvements in this dec as in FullPivLU. - if(dec.rank()==0) + if(dec().rank()==0) { dst.setZero(); return; } - typename Rhs::PlainMatrixType c(rhs); + typename Rhs::PlainMatrixType c(rhs()); - Matrix temp(rhs.cols()); - for (int k = 0; k < dec.rank(); ++k) + Matrix temp(rhs().cols()); + for (int k = 0; k < dec().rank(); ++k) { int remainingSize = rows-k; - c.row(k).swap(c.row(dec.rowsTranspositions().coeff(k))); - c.corner(BottomRight, remainingSize, rhs.cols()) - .applyHouseholderOnTheLeft(dec.matrixQR().col(k).end(remainingSize-1), - dec.hCoeffs().coeff(k), &temp.coeffRef(0)); + c.row(k).swap(c.row(dec().rowsTranspositions().coeff(k))); + c.corner(BottomRight, remainingSize, rhs().cols()) + .applyHouseholderOnTheLeft(dec().matrixQR().col(k).end(remainingSize-1), + dec().hCoeffs().coeff(k), &temp.coeffRef(0)); } - if(!dec.isSurjective()) + if(!dec().isSurjective()) { // is c is in the image of R ? - RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, dec.rank(), c.cols()).cwise().abs().maxCoeff(); - RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-dec.rank(), c.cols()).cwise().abs().maxCoeff(); + RealScalar biggest_in_upper_part_of_c = c.corner(TopLeft, dec().rank(), c.cols()).cwise().abs().maxCoeff(); + RealScalar biggest_in_lower_part_of_c = c.corner(BottomLeft, rows-dec().rank(), c.cols()).cwise().abs().maxCoeff(); // FIXME brain dead const RealScalar m_precision = epsilon() * std::min(rows,cols); if(!ei_isMuchSmallerThan(biggest_in_lower_part_of_c, biggest_in_upper_part_of_c, m_precision)) return; } - dec.matrixQR() - .corner(TopLeft, dec.rank(), dec.rank()) + dec().matrixQR() + .corner(TopLeft, dec().rank(), dec().rank()) .template triangularView() - .solveInPlace(c.corner(TopLeft, dec.rank(), c.cols())); + .solveInPlace(c.corner(TopLeft, dec().rank(), c.cols())); - for(int i = 0; i < dec.rank(); ++i) dst.row(dec.colsPermutation().coeff(i)) = c.row(i); - for(int i = dec.rank(); i < cols; ++i) dst.row(dec.colsPermutation().coeff(i)).setZero(); + for(int i = 0; i < dec().rank(); ++i) dst.row(dec().colsPermutation().coeff(i)) = c.row(i); + for(int i = dec().rank(); i < cols; ++i) dst.row(dec().colsPermutation().coeff(i)).setZero(); } }; diff --git a/Eigen/src/QR/HouseholderQR.h b/Eigen/src/QR/HouseholderQR.h index 6db0411d9..8742fdf71 100644 --- a/Eigen/src/QR/HouseholderQR.h +++ b/Eigen/src/QR/HouseholderQR.h @@ -209,28 +209,28 @@ HouseholderQR& HouseholderQR::compute(const MatrixType& return *this; } -template -struct ei_solve_impl, Rhs, Dest> - : ei_solve_return_value, Rhs> +template +struct ei_solve_impl, Rhs> + : ei_solve_return_value, Rhs> { - void evalTo(Dest& dst) const - { - const HouseholderQR& dec = this->m_dec; - const Rhs& rhs = this->m_rhs; - const int rows = dec.rows(), cols = dec.cols(); - dst.resize(cols, rhs.cols()); - const int rank = std::min(rows, cols); - ei_assert(rhs.rows() == rows); + EIGEN_MAKE_SOLVE_HELPERS(HouseholderQR<_MatrixType>,Rhs) - typename Rhs::PlainMatrixType c(rhs); + template void evalTo(Dest& dst) const + { + const int rows = dec().rows(), cols = dec().cols(); + dst.resize(cols, rhs().cols()); + const int rank = std::min(rows, cols); + ei_assert(rhs().rows() == rows); + + typename Rhs::PlainMatrixType c(rhs()); // Note that the matrix Q = H_0^* H_1^*... so its inverse is Q^* = (H_0 H_1 ...)^T c.applyOnTheLeft(makeHouseholderSequence( - dec.matrixQR().corner(TopLeft,rows,rank), - dec.hCoeffs().start(rank)).transpose() + dec().matrixQR().corner(TopLeft,rows,rank), + dec().hCoeffs().start(rank)).transpose() ); - dec.matrixQR() + dec().matrixQR() .corner(TopLeft, rank, rank) .template triangularView() .solveInPlace(c.corner(TopLeft, rank, c.cols())); diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h index 8ca425525..2ae7c4859 100644 --- a/Eigen/src/SVD/SVD.h +++ b/Eigen/src/SVD/SVD.h @@ -428,33 +428,31 @@ SVD& SVD::compute(const MatrixType& matrix) return *this; } -template -struct ei_solve_impl, Rhs, Dest> - : ei_solve_return_value, Rhs> +template +struct ei_solve_impl, Rhs> + : ei_solve_return_value, Rhs> { - void evalTo(Dest& dst) const + EIGEN_MAKE_SOLVE_HELPERS(SVD<_MatrixType>,Rhs) + + template void evalTo(Dest& dst) const { - typedef typename MatrixType::Scalar Scalar; - typedef typename MatrixType::RealScalar RealScalar; const int cols = this->cols(); - const SVD& svd = this->m_dec; - const Rhs& rhs = this->m_rhs; - ei_assert(rhs.rows() == svd.rows()); + ei_assert(rhs().rows() == dec().rows()); for (int j=0; j aux = svd.matrixU().adjoint() * rhs.col(j); + Matrix aux = dec().matrixU().adjoint() * rhs().col(j); - for (int i = 0; i struct ei_image_return_value template inline void evalTo(Dest& dst) const { - static_cast *> - (this)->evalTo(dst); + static_cast*>(this)->evalTo(dst); } }; +#define EIGEN_MAKE_IMAGE_HELPERS(DecompositionType) \ + typedef typename DecompositionType::MatrixType MatrixType; \ + typedef typename MatrixType::Scalar Scalar; \ + typedef typename MatrixType::RealScalar RealScalar; \ + inline const DecompositionType& dec() const { return this->m_dec; } \ + inline const MatrixType& originalMatrix() const { return this->m_originalMatrix; } \ + inline int rank() const { return this->m_rank; } + #endif // EIGEN_MISC_IMAGE_H diff --git a/Eigen/src/misc/Kernel.h b/Eigen/src/misc/Kernel.h index bfd75f54b..74ef16abc 100644 --- a/Eigen/src/misc/Kernel.h +++ b/Eigen/src/misc/Kernel.h @@ -63,9 +63,15 @@ template struct ei_kernel_return_value template inline void evalTo(Dest& dst) const { - static_cast *> - (this)->evalTo(dst); + static_cast*>(this)->evalTo(dst); } }; +#define EIGEN_MAKE_KERNEL_HELPERS(DecompositionType) \ + typedef typename DecompositionType::MatrixType MatrixType; \ + typedef typename MatrixType::Scalar Scalar; \ + typedef typename MatrixType::RealScalar RealScalar; \ + inline const DecompositionType& dec() const { return this->m_dec; } \ + inline int rank() const { return this->m_rank; } + #endif // EIGEN_MISC_KERNEL_H diff --git a/Eigen/src/misc/Solve.h b/Eigen/src/misc/Solve.h index c62e34b13..bf8f15773 100644 --- a/Eigen/src/misc/Solve.h +++ b/Eigen/src/misc/Solve.h @@ -57,9 +57,16 @@ template struct ei_solve_return_value template inline void evalTo(Dest& dst) const { - static_cast *> - (this)->evalTo(dst); + static_cast*>(this)->evalTo(dst); } }; +#define EIGEN_MAKE_SOLVE_HELPERS(DecompositionType,Rhs) \ + typedef typename DecompositionType::MatrixType MatrixType; \ + typedef typename MatrixType::Scalar Scalar; \ + typedef typename MatrixType::RealScalar RealScalar; \ + typedef typename ei_cleantype::type RhsNestedCleaned; \ + inline const DecompositionType& dec() const { return this->m_dec; } \ + inline const RhsNestedCleaned& rhs() const { return this->m_rhs; } + #endif // EIGEN_MISC_SOLVE_H diff --git a/doc/snippets/FullPivLU_image.cpp b/doc/snippets/FullPivLU_image.cpp index d3092e8b6..817bc1e2d 100644 --- a/doc/snippets/FullPivLU_image.cpp +++ b/doc/snippets/FullPivLU_image.cpp @@ -6,4 +6,4 @@ cout << "Here is the matrix m:" << endl << m << endl; cout << "Notice that the middle column is the sum of the two others, so the " << "columns are linearly dependent." << endl; cout << "Here is a matrix whose columns have the same span but are linearly independent:" - << endl << m.lu().image(m) << endl; + << endl << m.fullPivLu().image(m) << endl; diff --git a/doc/snippets/FullPivLU_kernel.cpp b/doc/snippets/FullPivLU_kernel.cpp index e01186d38..7086e01e2 100644 --- a/doc/snippets/FullPivLU_kernel.cpp +++ b/doc/snippets/FullPivLU_kernel.cpp @@ -1,6 +1,6 @@ MatrixXf m = MatrixXf::Random(3,5); cout << "Here is the matrix m:" << endl << m << endl; -MatrixXf ker = m.lu().kernel(); +MatrixXf ker = m.fullPivLu().kernel(); cout << "Here is a matrix whose columns form a basis of the kernel of m:" << endl << ker << endl; cout << "By definition of the kernel, m*ker is zero:" diff --git a/doc/snippets/FullPivLU_solve.cpp b/doc/snippets/FullPivLU_solve.cpp index ade269789..696f414b3 100644 --- a/doc/snippets/FullPivLU_solve.cpp +++ b/doc/snippets/FullPivLU_solve.cpp @@ -2,7 +2,7 @@ Matrix m = Matrix::Random(); Matrix2f y = Matrix2f::Random(); cout << "Here is the matrix m:" << endl << m << endl; cout << "Here is the matrix y:" << endl << y << endl; -Matrix x = m.lu().solve(y); +Matrix x = m.fillPivLu().solve(y); if((m*x).isApprox(y)) { cout << "Here is a solution x to the equation mx=y:" << endl << x << endl; diff --git a/doc/snippets/HouseholderQR_solve.cpp b/doc/snippets/HouseholderQR_solve.cpp index 429bd81e3..8cce6ce6c 100644 --- a/doc/snippets/HouseholderQR_solve.cpp +++ b/doc/snippets/HouseholderQR_solve.cpp @@ -4,6 +4,6 @@ Matrix3f y = Matrix3f::Random(); cout << "Here is the matrix m:" << endl << m << endl; cout << "Here is the matrix y:" << endl << y << endl; Matrix3f x; -m.householderQr().solve(y, &x); +x = m.householderQr().solve(y); assert(y.isApprox(m*x)); cout << "Here is a solution x to the equation mx=y:" << endl << x << endl; From 670651e2e0932c5edfe2a2da4b9f3c42af3b7dec Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 9 Nov 2009 10:48:18 +0100 Subject: [PATCH 32/34] Quaternion: fix compilation, cleaning --- Eigen/src/Geometry/Quaternion.h | 392 +++++++++++++++++--------------- 1 file changed, 213 insertions(+), 179 deletions(-) diff --git a/Eigen/src/Geometry/Quaternion.h b/Eigen/src/Geometry/Quaternion.h index 67b040165..12c07d76c 100644 --- a/Eigen/src/Geometry/Quaternion.h +++ b/Eigen/src/Geometry/Quaternion.h @@ -26,6 +26,149 @@ #ifndef EIGEN_QUATERNION_H #define EIGEN_QUATERNION_H +/*************************************************************************** +* Definition of QuaternionBase +* The implementation is at the end of the file +***************************************************************************/ + +template +struct ei_quaternionbase_assign_impl; + +template +class QuaternionBase : public RotationBase +{ + typedef RotationBase Base; +public: + using Base::operator*; + using Base::derived; + + typedef typename ei_traits::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef typename ei_traits::Coefficients Coefficients; + + // typedef typename Matrix Coefficients; + /** the type of a 3D vector */ + typedef Matrix Vector3; + /** the equivalent rotation matrix type */ + typedef Matrix Matrix3; + /** the equivalent angle-axis type */ + typedef AngleAxis AngleAxisType; + + + + /** \returns the \c x coefficient */ + inline Scalar x() const { return this->derived().coeffs().coeff(0); } + /** \returns the \c y coefficient */ + inline Scalar y() const { return this->derived().coeffs().coeff(1); } + /** \returns the \c z coefficient */ + inline Scalar z() const { return this->derived().coeffs().coeff(2); } + /** \returns the \c w coefficient */ + inline Scalar w() const { return this->derived().coeffs().coeff(3); } + + /** \returns a reference to the \c x coefficient */ + inline Scalar& x() { return this->derived().coeffs().coeffRef(0); } + /** \returns a reference to the \c y coefficient */ + inline Scalar& y() { return this->derived().coeffs().coeffRef(1); } + /** \returns a reference to the \c z coefficient */ + inline Scalar& z() { return this->derived().coeffs().coeffRef(2); } + /** \returns a reference to the \c w coefficient */ + inline Scalar& w() { return this->derived().coeffs().coeffRef(3); } + + /** \returns a read-only vector expression of the imaginary part (x,y,z) */ + inline const VectorBlock vec() const { return coeffs().template start<3>(); } + + /** \returns a vector expression of the imaginary part (x,y,z) */ + inline VectorBlock vec() { return coeffs().template start<3>(); } + + /** \returns a read-only vector expression of the coefficients (x,y,z,w) */ + inline const typename ei_traits::Coefficients& coeffs() const { return derived().coeffs(); } + + /** \returns a vector expression of the coefficients (x,y,z,w) */ + inline typename ei_traits::Coefficients& coeffs() { return derived().coeffs(); } + + template Derived& operator=(const QuaternionBase& other); + Derived& operator=(const QuaternionBase& other) + { return operator=(other); } + Derived& operator=(const AngleAxisType& aa); + template Derived& operator=(const MatrixBase& m); + + /** \returns a quaternion representing an identity rotation + * \sa MatrixBase::Identity() + */ + inline static Quaternion Identity() { return Quaternion(1, 0, 0, 0); } + + /** \sa QuaternionBase::Identity(), MatrixBase::setIdentity() + */ + inline QuaternionBase& setIdentity() { coeffs() << 0, 0, 0, 1; return *this; } + + /** \returns the squared norm of the quaternion's coefficients + * \sa QuaternionBase::norm(), MatrixBase::squaredNorm() + */ + inline Scalar squaredNorm() const { return coeffs().squaredNorm(); } + + /** \returns the norm of the quaternion's coefficients + * \sa QuaternionBase::squaredNorm(), MatrixBase::norm() + */ + inline Scalar norm() const { return coeffs().norm(); } + + /** Normalizes the quaternion \c *this + * \sa normalized(), MatrixBase::normalize() */ + inline void normalize() { coeffs().normalize(); } + /** \returns a normalized copy of \c *this + * \sa normalize(), MatrixBase::normalized() */ + inline Quaternion normalized() const { return Quaternion(coeffs().normalized()); } + + /** \returns the dot product of \c *this and \a other + * Geometrically speaking, the dot product of two unit quaternions + * corresponds to the cosine of half the angle between the two rotations. + * \sa angularDistance() + */ + template inline Scalar dot(const QuaternionBase& other) const { return coeffs().dot(other.coeffs()); } + + template inline Scalar angularDistance(const QuaternionBase& other) const; + + Matrix3 toRotationMatrix() const; + + template + Derived& setFromTwoVectors(const MatrixBase& a, const MatrixBase& b); + + template inline Quaternion operator* (const QuaternionBase& q) const; + template inline Derived& operator*= (const QuaternionBase& q); + + Quaternion inverse() const; + Quaternion conjugate() const; + + template Quaternion slerp(Scalar t, const QuaternionBase& other) const; + + /** \returns \c true if \c *this is approximately equal to \a other, within the precision + * determined by \a prec. + * + * \sa MatrixBase::isApprox() */ + template + bool isApprox(const QuaternionBase& other, RealScalar prec = precision()) const + { return coeffs().isApprox(other.coeffs(), prec); } + + Vector3 _transformVector(Vector3 v) const; + + /** \returns \c *this with scalar type casted to \a NewScalarType + * + * Note that if \a NewScalarType is equal to the current scalar type of \c *this + * then this function smartly returns a const reference to \c *this. + */ + template + inline typename ei_cast_return_type >::type cast() const + { + return typename ei_cast_return_type >::type( + coeffs().template cast()); + } +}; + +/*************************************************************************** +* Definition/implementation of Quaternion +***************************************************************************/ + /** \geometry_module \ingroup Geometry_Module * * \class Quaternion @@ -48,152 +191,13 @@ * \sa class AngleAxis, class Transform */ -template -struct ei_quaternionbase_assign_impl; - -template class Quaternion; // [XXX] => remove when Quaternion becomes Quaternion - -template -struct ei_traits > -{ - typedef typename ei_traits::Scalar Scalar; - enum { - PacketAccess = ei_traits::PacketAccess - }; -}; - -template -class QuaternionBase : public RotationBase -{ - typedef RotationBase Base; -public: - using Base::operator*; - - typedef typename ei_traits >::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - - // typedef typename Matrix Coefficients; - /** the type of a 3D vector */ - typedef Matrix Vector3; - /** the equivalent rotation matrix type */ - typedef Matrix Matrix3; - /** the equivalent angle-axis type */ - typedef AngleAxis AngleAxisType; - - /** \returns the \c x coefficient */ - inline Scalar x() const { return this->derived().coeffs().coeff(0); } - /** \returns the \c y coefficient */ - inline Scalar y() const { return this->derived().coeffs().coeff(1); } - /** \returns the \c z coefficient */ - inline Scalar z() const { return this->derived().coeffs().coeff(2); } - /** \returns the \c w coefficient */ - inline Scalar w() const { return this->derived().coeffs().coeff(3); } - - /** \returns a reference to the \c x coefficient */ - inline Scalar& x() { return this->derived().coeffs().coeffRef(0); } - /** \returns a reference to the \c y coefficient */ - inline Scalar& y() { return this->derived().coeffs().coeffRef(1); } - /** \returns a reference to the \c z coefficient */ - inline Scalar& z() { return this->derived().coeffs().coeffRef(2); } - /** \returns a reference to the \c w coefficient */ - inline Scalar& w() { return this->derived().coeffs().coeffRef(3); } - - /** \returns a read-only vector expression of the imaginary part (x,y,z) */ - inline const VectorBlock::Coefficients,3> vec() const { return this->derived().coeffs().template start<3>(); } - - /** \returns a vector expression of the imaginary part (x,y,z) */ - inline VectorBlock::Coefficients,3> vec() { return this->derived().coeffs().template start<3>(); } - - /** \returns a read-only vector expression of the coefficients (x,y,z,w) */ - inline const typename ei_traits::Coefficients& coeffs() const { return this->derived().coeffs(); } - - /** \returns a vector expression of the coefficients (x,y,z,w) */ - inline typename ei_traits::Coefficients& coeffs() { return this->derived().coeffs(); } - - template QuaternionBase& operator=(const QuaternionBase& other); - QuaternionBase& operator=(const AngleAxisType& aa); - template - QuaternionBase& operator=(const MatrixBase& m); - - /** \returns a quaternion representing an identity rotation - * \sa MatrixBase::Identity() - */ - inline static Quaternion Identity() { return Quaternion(1, 0, 0, 0); } - - /** \sa Quaternion2::Identity(), MatrixBase::setIdentity() - */ - inline QuaternionBase& setIdentity() { coeffs() << 0, 0, 0, 1; return *this; } - - /** \returns the squared norm of the quaternion's coefficients - * \sa Quaternion2::norm(), MatrixBase::squaredNorm() - */ - inline Scalar squaredNorm() const { return coeffs().squaredNorm(); } - - /** \returns the norm of the quaternion's coefficients - * \sa Quaternion2::squaredNorm(), MatrixBase::norm() - */ - inline Scalar norm() const { return coeffs().norm(); } - - /** Normalizes the quaternion \c *this - * \sa normalized(), MatrixBase::normalize() */ - inline void normalize() { coeffs().normalize(); } - /** \returns a normalized version of \c *this - * \sa normalize(), MatrixBase::normalized() */ - inline Quaternion normalized() const { return Quaternion(coeffs().normalized()); } - - /** \returns the dot product of \c *this and \a other - * Geometrically speaking, the dot product of two unit quaternions - * corresponds to the cosine of half the angle between the two rotations. - * \sa angularDistance() - */ - template inline Scalar dot(const QuaternionBase& other) const { return coeffs().dot(other.coeffs()); } - - template inline Scalar angularDistance(const QuaternionBase& other) const; - - Matrix3 toRotationMatrix(void) const; - - template - QuaternionBase& setFromTwoVectors(const MatrixBase& a, const MatrixBase& b); - - template inline Quaternion operator* (const QuaternionBase& q) const; - template inline QuaternionBase& operator*= (const QuaternionBase& q); - - Quaternion inverse(void) const; - Quaternion conjugate(void) const; - - template Quaternion slerp(Scalar t, const QuaternionBase& other) const; - - /** \returns \c true if \c *this is approximately equal to \a other, within the precision - * determined by \a prec. - * - * \sa MatrixBase::isApprox() */ - bool isApprox(const QuaternionBase& other, RealScalar prec = precision()) const - { return coeffs().isApprox(other.coeffs(), prec); } - - Vector3 _transformVector(Vector3 v) const; - - /** \returns \c *this with scalar type casted to \a NewScalarType - * - * Note that if \a NewScalarType is equal to the current scalar type of \c *this - * then this function smartly returns a const reference to \c *this. - */ - template - inline typename ei_cast_return_type >::type cast() const - { - return typename ei_cast_return_type >::type( - coeffs().template cast()); - } -}; - template struct ei_traits > { typedef _Scalar Scalar; typedef Matrix<_Scalar,4,1> Coefficients; enum{ - PacketAccess = Aligned + PacketAccess = Aligned }; }; @@ -250,16 +254,29 @@ protected: Coefficients m_coeffs; }; -/* ########### Map */ +/** \ingroup Geometry_Module + * single precision quaternion type */ +typedef Quaternion Quaternionf; +/** \ingroup Geometry_Module + * double precision quaternion type */ +typedef Quaternion Quaterniond; + +/*************************************************************************** +* Specialization of Map> +***************************************************************************/ /** \class Map * \nonstableyet * - * \brief Expression of a quaternion + * \brief Expression of a quaternion from a memory buffer * - * \param Scalar the type of the vector of diagonal coefficients + * \param _Scalar the type of the Quaternion coefficients + * \param PacketAccess see class Map * - * \sa class Quaternion, class QuaternionBase + * This is a specialization of class Map for Quaternion. This class allows to view + * a 4 scalar memory buffer as an Eigen's Quaternion object. + * + * \sa class Map, class Quaternion, class QuaternionBase */ template struct ei_traits, _PacketAccess> >: @@ -273,15 +290,23 @@ ei_traits > }; template -class Map, PacketAccess > : public QuaternionBase, PacketAccess> >, ei_no_assignment_operator { +class Map, PacketAccess > + : public QuaternionBase, PacketAccess> >, + ei_no_assignment_operator +{ public: - + typedef _Scalar Scalar; + typedef typename ei_traits::Coefficients Coefficients; - typedef typename ei_traits, PacketAccess> >::Coefficients Coefficients; + /** Constructs a Mapped Quaternion object from the pointer \a coeffs + * + * The pointer \a coeffs must reference the four coeffecients of Quaternion in the following order: + * \code *coeffs == {x, y, z, w} \endcode + * + * If the template paramter PacketAccess is set to Aligned, then the pointer coeffs must be aligned. */ + inline Map(const Scalar* coeffs) : m_coeffs(coeffs) {} - inline Map, PacketAccess >(const Scalar* coeffs) : m_coeffs(coeffs) {} - inline Coefficients& coeffs() { return m_coeffs;} inline const Coefficients& coeffs() const { return m_coeffs;} @@ -289,15 +314,20 @@ class Map, PacketAccess > : public QuaternionBase > QuaternionMapd; -typedef Map > QuaternionMapf; -typedef Map, Aligned> QuaternionMapAlignedd; -typedef Map, Aligned> QuaternionMapAlignedf; +typedef Map > QuaternionMapd; +typedef Map > QuaternionMapf; +typedef Map, Aligned> QuaternionMapAlignedd; +typedef Map, Aligned> QuaternionMapAlignedf; + +/*************************************************************************** +* Implementation of QuaternionBase methods +***************************************************************************/ // Generic Quaternion * Quaternion product -template struct ei_quat_product +// This product can be specialized for a given architecture via the Arch template argument. +template struct ei_quat_product { - inline static Quaternion run(const QuaternionBase& a, const QuaternionBase& b){ + inline static Quaternion run(const QuaternionBase& a, const QuaternionBase& b){ return Quaternion ( a.w() * b.w() - a.x() * b.x() - a.y() * b.y() - a.z() * b.z(), @@ -311,21 +341,22 @@ template template -inline Quaternion >::Scalar> QuaternionBase::operator* (const QuaternionBase& other) const +inline Quaternion::Scalar> +QuaternionBase::operator* (const QuaternionBase& other) const { EIGEN_STATIC_ASSERT((ei_is_same_type::ret), YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) - return ei_quat_product::Scalar, - ei_traits::PacketAccess && ei_traits::PacketAccess>::run(*this, other); + return ei_quat_product::Scalar, + ei_traits::PacketAccess && ei_traits::PacketAccess>::run(*this, other); } /** \sa operator*(Quaternion) */ template template -inline QuaternionBase& QuaternionBase::operator*= (const QuaternionBase& other) +inline Derived& QuaternionBase::operator*= (const QuaternionBase& other) { - return (*this = *this * other); + return (derived() = derived() * other.derived()); } /** Rotation of a vector by a quaternion. @@ -350,21 +381,21 @@ QuaternionBase::_transformVector(Vector3 v) const template template -inline QuaternionBase& QuaternionBase::operator=(const QuaternionBase& other) +inline Derived& QuaternionBase::operator=(const QuaternionBase& other) { coeffs() = other.coeffs(); - return *this; + return derived(); } /** Set \c *this from an angle-axis \a aa and returns a reference to \c *this */ template -inline QuaternionBase& QuaternionBase::operator=(const AngleAxisType& aa) +inline Derived& QuaternionBase::operator=(const AngleAxisType& aa) { Scalar ha = Scalar(0.5)*aa.angle(); // Scalar(0.5) to suppress precision loss warnings this->w() = ei_cos(ha); this->vec() = ei_sin(ha) * aa.axis(); - return *this; + return derived(); } /** Set \c *this from the expression \a xpr: @@ -375,12 +406,12 @@ inline QuaternionBase& QuaternionBase::operator=(const AngleAx template template -inline QuaternionBase& QuaternionBase::operator=(const MatrixBase& xpr) +inline Derived& QuaternionBase::operator=(const MatrixBase& xpr) { EIGEN_STATIC_ASSERT((ei_is_same_type::ret), YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) ei_quaternionbase_assign_impl::run(*this, xpr.derived()); - return *this; + return derived(); } /** Convert the quaternion to a 3x3 rotation matrix. The quaternion is required to @@ -434,7 +465,7 @@ QuaternionBase::toRotationMatrix(void) const */ template template -inline QuaternionBase& QuaternionBase::setFromTwoVectors(const MatrixBase& a, const MatrixBase& b) +inline Derived& QuaternionBase::setFromTwoVectors(const MatrixBase& a, const MatrixBase& b) { Vector3 v0 = a.normalized(); Vector3 v1 = b.normalized(); @@ -458,7 +489,7 @@ inline QuaternionBase& QuaternionBase::setFromTwoVectors(const Scalar w2 = (Scalar(1)+c)*Scalar(0.5); this->w() = ei_sqrt(w2); this->vec() = axis * ei_sqrt(Scalar(1) - w2); - return *this; + return derived(); } Vector3 axis = v0.cross(v1); Scalar s = ei_sqrt((Scalar(1)+c)*Scalar(2)); @@ -466,17 +497,17 @@ inline QuaternionBase& QuaternionBase::setFromTwoVectors(const this->vec() = axis * invs; this->w() = s * Scalar(0.5); - return *this; + return derived(); } /** \returns the multiplicative inverse of \c *this * Note that in most cases, i.e., if you simply want the opposite rotation, * and/or the quaternion is normalized, then it is enough to use the conjugate. * - * \sa Quaternion2::conjugate() + * \sa QuaternionBase::conjugate() */ template -inline Quaternion >::Scalar> QuaternionBase::inverse() const +inline Quaternion::Scalar> QuaternionBase::inverse() const { // FIXME should this function be called multiplicativeInverse and conjugate() be called inverse() or opposite() ?? Scalar n2 = this->squaredNorm(); @@ -485,7 +516,7 @@ inline Quaternion >::Scalar> Quaterni else { // return an invalid result to flag the error - return Quaternion(ei_traits::Coefficients::Zero()); + return Quaternion(Coefficients::Zero()); } } @@ -496,7 +527,8 @@ inline Quaternion >::Scalar> Quaterni * \sa Quaternion2::inverse() */ template -inline Quaternion >::Scalar> QuaternionBase::conjugate() const +inline Quaternion::Scalar> +QuaternionBase::conjugate() const { return Quaternion(this->w(),-this->x(),-this->y(),-this->z()); } @@ -506,11 +538,12 @@ inline Quaternion >::Scalar> Quaterni */ template template -inline typename ei_traits >::Scalar QuaternionBase::angularDistance(const QuaternionBase& other) const +inline typename ei_traits::Scalar +QuaternionBase::angularDistance(const QuaternionBase& other) const { double d = ei_abs(this->dot(other)); if (d>=1.0) - return 0; + return Scalar(0); return Scalar(2) * std::acos(d); } @@ -519,13 +552,14 @@ inline typename ei_traits >::Scalar QuaternionBase template -Quaternion >::Scalar> QuaternionBase::slerp(Scalar t, const QuaternionBase& other) const +Quaternion::Scalar> +QuaternionBase::slerp(Scalar t, const QuaternionBase& other) const { static const Scalar one = Scalar(1) - precision(); Scalar d = this->dot(other); Scalar absD = ei_abs(d); if (absD>=one) - return Quaternion(*this); + return Quaternion(derived()); // theta is the angle between the 2 quaternions Scalar theta = std::acos(absD); @@ -549,7 +583,7 @@ struct ei_quaternionbase_assign_impl // This algorithm comes from "Quaternion Calculus and Fast Animation", // Ken Shoemake, 1987 SIGGRAPH course notes Scalar t = mat.trace(); - if (t > 0) + if (t > Scalar(0)) { t = ei_sqrt(t + Scalar(1.0)); q.w() = Scalar(0.5)*t; From 9a0900e33e9ca4bc174cfccc26dbf4bdc38f7627 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 9 Nov 2009 07:51:31 -0500 Subject: [PATCH 33/34] last round of changes, mainly to return derived types instead of base types, and fix various compilation issues --- Eigen/src/Cholesky/LDLT.h | 8 +++--- Eigen/src/Cholesky/LLT.h | 8 +++--- Eigen/src/Core/util/ForwardDeclarations.h | 12 ++++---- Eigen/src/Geometry/arch/Geometry_SSE.h | 4 +-- Eigen/src/LU/FullPivLU.h | 28 +++++++++--------- Eigen/src/LU/PartialPivLU.h | 12 ++++---- Eigen/src/QR/ColPivHouseholderQR.h | 12 ++++---- Eigen/src/QR/FullPivHouseholderQR.h | 12 ++++---- Eigen/src/QR/HouseholderQR.h | 8 +++--- Eigen/src/SVD/SVD.h | 8 +++--- Eigen/src/misc/Image.h | 35 ++++++++++++++--------- Eigen/src/misc/Kernel.h | 28 +++++++++++------- Eigen/src/misc/Solve.h | 30 ++++++++++++------- doc/snippets/FullPivLU_solve.cpp | 2 +- test/lu.cpp | 4 +-- 15 files changed, 118 insertions(+), 93 deletions(-) diff --git a/Eigen/src/Cholesky/LDLT.h b/Eigen/src/Cholesky/LDLT.h index a1faae49c..d0f292634 100644 --- a/Eigen/src/Cholesky/LDLT.h +++ b/Eigen/src/Cholesky/LDLT.h @@ -124,13 +124,13 @@ template class LDLT * \sa solveInPlace(), MatrixBase::ldlt() */ template - inline const ei_solve_return_value + inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "LDLT is not initialized."); ei_assert(m_matrix.rows()==b.rows() && "LDLT::solve(): invalid number of rows of the right hand side matrix b"); - return ei_solve_return_value(*this, b.derived()); + return ei_solve_retval(*this, b.derived()); } template @@ -265,8 +265,8 @@ LDLT& LDLT::compute(const MatrixType& a) } template -struct ei_solve_impl, Rhs> - : ei_solve_return_value, Rhs> +struct ei_solve_retval, Rhs> + : ei_solve_retval_base, Rhs> { EIGEN_MAKE_SOLVE_HELPERS(LDLT<_MatrixType>,Rhs) diff --git a/Eigen/src/Cholesky/LLT.h b/Eigen/src/Cholesky/LLT.h index 30c48578a..a1706e53e 100644 --- a/Eigen/src/Cholesky/LLT.h +++ b/Eigen/src/Cholesky/LLT.h @@ -109,13 +109,13 @@ template class LLT * \sa solveInPlace(), MatrixBase::llt() */ template - inline const ei_solve_return_value + inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "LLT is not initialized."); ei_assert(m_matrix.rows()==b.rows() && "LLT::solve(): invalid number of rows of the right hand side matrix b"); - return ei_solve_return_value(*this, b.derived()); + return ei_solve_retval(*this, b.derived()); } template @@ -259,8 +259,8 @@ LLT& LLT::compute(const MatrixType& a) } template -struct ei_solve_impl, Rhs> - : ei_solve_return_value, Rhs> +struct ei_solve_retval, Rhs> + : ei_solve_retval_base, Rhs> { typedef LLT<_MatrixType,UpLo> LLTType; EIGEN_MAKE_SOLVE_HELPERS(LLTType,Rhs) diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index 323848919..86df5395e 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -66,12 +66,12 @@ template class WithFormat; template struct CommaInitializer; template class ReturnByValue; -template struct ei_solve_return_value; -template struct ei_solve_impl; -template struct ei_kernel_return_value; -template struct ei_kernel_impl; -template struct ei_image_return_value; -template struct ei_image_impl; +template struct ei_solve_retval_base; +template struct ei_solve_retval; +template struct ei_kernel_retval_base; +template struct ei_kernel_retval; +template struct ei_image_retval_base; +template struct ei_image_retval; template class BandMatrix; diff --git a/Eigen/src/Geometry/arch/Geometry_SSE.h b/Eigen/src/Geometry/arch/Geometry_SSE.h index 1b8f6aead..a6ed10d82 100644 --- a/Eigen/src/Geometry/arch/Geometry_SSE.h +++ b/Eigen/src/Geometry/arch/Geometry_SSE.h @@ -32,8 +32,8 @@ template struct ei_quat_product res; - __m128 a = _a.coeffs().packet(0); - __m128 b = _b.coeffs().packet(0); + __m128 a = _a.coeffs().template packet(0); + __m128 b = _b.coeffs().template packet(0); __m128 flip1 = _mm_xor_ps(_mm_mul_ps(ei_vec4f_swizzle1(a,1,2,0,2), ei_vec4f_swizzle1(b,2,0,1,2)),mask); __m128 flip2 = _mm_xor_ps(_mm_mul_ps(ei_vec4f_swizzle1(a,3,3,3,1), diff --git a/Eigen/src/LU/FullPivLU.h b/Eigen/src/LU/FullPivLU.h index c38789bd9..d8975b0b6 100644 --- a/Eigen/src/LU/FullPivLU.h +++ b/Eigen/src/LU/FullPivLU.h @@ -158,10 +158,10 @@ template class FullPivLU * * \sa image() */ - inline const ei_kernel_return_value kernel() const + inline const ei_kernel_retval kernel() const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_kernel_return_value(*this); + return ei_kernel_retval(*this); } /** \returns the image of the matrix, also called its column-space. The columns of the returned matrix @@ -183,11 +183,11 @@ template class FullPivLU * * \sa kernel() */ - inline const ei_image_return_value + inline const ei_image_retval image(const MatrixType& originalMatrix) const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_image_return_value(*this, originalMatrix); + return ei_image_retval(*this, originalMatrix); } /** \return a solution x to the equation Ax=b, where A is the matrix of which @@ -210,11 +210,11 @@ template class FullPivLU * \sa TriangularView::solve(), kernel(), inverse() */ template - inline const ei_solve_return_value + inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "LU is not initialized."); - return ei_solve_return_value(*this, b.derived()); + return ei_solve_retval(*this, b.derived()); } /** \returns the determinant of the matrix of which @@ -355,11 +355,11 @@ template class FullPivLU * * \sa MatrixBase::inverse() */ - inline const ei_solve_return_value > inverse() const + inline const ei_solve_retval > inverse() const { ei_assert(m_isInitialized && "LU is not initialized."); ei_assert(m_lu.rows() == m_lu.cols() && "You can't take the inverse of a non-square matrix!"); - return ei_solve_return_value > + return ei_solve_retval > (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); } @@ -486,8 +486,8 @@ typename ei_traits::Scalar FullPivLU::determinant() cons /********* Implementation of kernel() **************************************************/ template -struct ei_kernel_impl > - : ei_kernel_return_value > +struct ei_kernel_retval > + : ei_kernel_retval_base > { EIGEN_MAKE_KERNEL_HELPERS(FullPivLU<_MatrixType>) @@ -571,8 +571,8 @@ struct ei_kernel_impl > /***** Implementation of image() *****************************************************/ template -struct ei_image_impl > - : ei_image_return_value > +struct ei_image_retval > + : ei_image_retval_base > { EIGEN_MAKE_IMAGE_HELPERS(FullPivLU<_MatrixType>) @@ -608,8 +608,8 @@ struct ei_image_impl > /***** Implementation of solve() *****************************************************/ template -struct ei_solve_impl, Rhs> - : ei_solve_return_value, Rhs> +struct ei_solve_retval, Rhs> + : ei_solve_retval_base, Rhs> { EIGEN_MAKE_SOLVE_HELPERS(FullPivLU<_MatrixType>,Rhs) diff --git a/Eigen/src/LU/PartialPivLU.h b/Eigen/src/LU/PartialPivLU.h index 4f6cda68f..03c91456a 100644 --- a/Eigen/src/LU/PartialPivLU.h +++ b/Eigen/src/LU/PartialPivLU.h @@ -133,11 +133,11 @@ template class PartialPivLU * \sa TriangularView::solve(), inverse(), computeInverse() */ template - inline const ei_solve_return_value + inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "PartialPivLU is not initialized."); - return ei_solve_return_value(*this, b.derived()); + return ei_solve_retval(*this, b.derived()); } /** \returns the inverse of the matrix of which *this is the LU decomposition. @@ -147,10 +147,10 @@ template class PartialPivLU * * \sa MatrixBase::inverse(), LU::inverse() */ - inline const ei_solve_return_value > inverse() const + inline const ei_solve_retval > inverse() const { ei_assert(m_isInitialized && "PartialPivLU is not initialized."); - return ei_solve_return_value > + return ei_solve_retval > (*this, MatrixType::Identity(m_lu.rows(), m_lu.cols()).nestByValue()); } @@ -408,8 +408,8 @@ typename ei_traits::Scalar PartialPivLU::determinant() c /***** Implementation of solve() *****************************************************/ template -struct ei_solve_impl, Rhs> - : ei_solve_return_value, Rhs> +struct ei_solve_retval, Rhs> + : ei_solve_retval_base, Rhs> { EIGEN_MAKE_SOLVE_HELPERS(PartialPivLU<_MatrixType>,Rhs) diff --git a/Eigen/src/QR/ColPivHouseholderQR.h b/Eigen/src/QR/ColPivHouseholderQR.h index 4e2359e0b..d6be9a506 100644 --- a/Eigen/src/QR/ColPivHouseholderQR.h +++ b/Eigen/src/QR/ColPivHouseholderQR.h @@ -98,11 +98,11 @@ template class ColPivHouseholderQR * Output: \verbinclude ColPivHouseholderQR_solve.out */ template - inline const ei_solve_return_value + inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); - return ei_solve_return_value(*this, b.derived()); + return ei_solve_retval(*this, b.derived()); } HouseholderSequenceType matrixQ(void) const; @@ -215,11 +215,11 @@ template class ColPivHouseholderQR * Use isInvertible() to first determine whether this matrix is invertible. */ inline const - ei_solve_return_value > + ei_solve_retval > inverse() const { ei_assert(m_isInitialized && "ColPivHouseholderQR is not initialized."); - return ei_solve_return_value > + return ei_solve_retval > (*this, MatrixType::Identity(m_qr.rows(), m_qr.cols()).nestByValue()); } @@ -325,8 +325,8 @@ ColPivHouseholderQR& ColPivHouseholderQR::compute(const } template -struct ei_solve_impl, Rhs> - : ei_solve_return_value, Rhs> +struct ei_solve_retval, Rhs> + : ei_solve_retval_base, Rhs> { EIGEN_MAKE_SOLVE_HELPERS(ColPivHouseholderQR<_MatrixType>,Rhs) diff --git a/Eigen/src/QR/FullPivHouseholderQR.h b/Eigen/src/QR/FullPivHouseholderQR.h index ba6866fc0..8be90a3c9 100644 --- a/Eigen/src/QR/FullPivHouseholderQR.h +++ b/Eigen/src/QR/FullPivHouseholderQR.h @@ -93,11 +93,11 @@ template class FullPivHouseholderQR * Output: \verbinclude FullPivHouseholderQR_solve.out */ template - inline const ei_solve_return_value + inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); - return ei_solve_return_value(*this, b.derived()); + return ei_solve_retval(*this, b.derived()); } MatrixQType matrixQ(void) const; @@ -215,11 +215,11 @@ template class FullPivHouseholderQR * \note If this matrix is not invertible, the returned matrix has undefined coefficients. * Use isInvertible() to first determine whether this matrix is invertible. */ inline const - ei_solve_return_value > + ei_solve_retval > inverse() const { ei_assert(m_isInitialized && "FullPivHouseholderQR is not initialized."); - return ei_solve_return_value > + return ei_solve_retval > (*this, MatrixType::Identity(m_qr.rows(), m_qr.cols()).nestByValue()); } @@ -333,8 +333,8 @@ FullPivHouseholderQR& FullPivHouseholderQR::compute(cons } template -struct ei_solve_impl, Rhs> - : ei_solve_return_value, Rhs> +struct ei_solve_retval, Rhs> + : ei_solve_retval_base, Rhs> { EIGEN_MAKE_SOLVE_HELPERS(FullPivHouseholderQR<_MatrixType>,Rhs) diff --git a/Eigen/src/QR/HouseholderQR.h b/Eigen/src/QR/HouseholderQR.h index 8742fdf71..04b153cc4 100644 --- a/Eigen/src/QR/HouseholderQR.h +++ b/Eigen/src/QR/HouseholderQR.h @@ -98,11 +98,11 @@ template class HouseholderQR * Output: \verbinclude HouseholderQR_solve.out */ template - inline const ei_solve_return_value + inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "HouseholderQR is not initialized."); - return ei_solve_return_value(*this, b.derived()); + return ei_solve_retval(*this, b.derived()); } MatrixQType matrixQ() const; @@ -210,8 +210,8 @@ HouseholderQR& HouseholderQR::compute(const MatrixType& } template -struct ei_solve_impl, Rhs> - : ei_solve_return_value, Rhs> +struct ei_solve_retval, Rhs> + : ei_solve_retval_base, Rhs> { EIGEN_MAKE_SOLVE_HELPERS(HouseholderQR<_MatrixType>,Rhs) diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h index 2ae7c4859..3b424be04 100644 --- a/Eigen/src/SVD/SVD.h +++ b/Eigen/src/SVD/SVD.h @@ -90,11 +90,11 @@ template class SVD * \sa MatrixBase::svd(), */ template - inline const ei_solve_return_value + inline const ei_solve_retval solve(const MatrixBase& b) const { ei_assert(m_isInitialized && "SVD is not initialized."); - return ei_solve_return_value(*this, b.derived()); + return ei_solve_retval(*this, b.derived()); } const MatrixUType& matrixU() const @@ -429,8 +429,8 @@ SVD& SVD::compute(const MatrixType& matrix) } template -struct ei_solve_impl, Rhs> - : ei_solve_return_value, Rhs> +struct ei_solve_retval, Rhs> + : ei_solve_retval_base, Rhs> { EIGEN_MAKE_SOLVE_HELPERS(SVD<_MatrixType>,Rhs) diff --git a/Eigen/src/misc/Image.h b/Eigen/src/misc/Image.h index 01e2b160d..9ed5d5f70 100644 --- a/Eigen/src/misc/Image.h +++ b/Eigen/src/misc/Image.h @@ -25,11 +25,11 @@ #ifndef EIGEN_MISC_IMAGE_H #define EIGEN_MISC_IMAGE_H -/** \class ei_image_return_value +/** \class ei_image_retval_base * */ template -struct ei_traits > +struct ei_traits > { typedef typename DecompositionType::MatrixType MatrixType; typedef Matrix< @@ -43,17 +43,13 @@ struct ei_traits > > ReturnMatrixType; }; -template struct ei_image_return_value - : public ReturnByValue > +template struct ei_image_retval_base + : public ReturnByValue > { typedef _DecompositionType DecompositionType; typedef typename DecompositionType::MatrixType MatrixType; - const DecompositionType& m_dec; - int m_rank, m_cols; - const MatrixType& m_originalMatrix; - - ei_image_return_value(const DecompositionType& dec, const MatrixType& originalMatrix) + ei_image_retval_base(const DecompositionType& dec, const MatrixType& originalMatrix) : m_dec(dec), m_rank(dec.rank()), m_cols(m_rank == 0 ? 1 : m_rank), m_originalMatrix(originalMatrix) @@ -61,19 +57,32 @@ template struct ei_image_return_value inline int rows() const { return m_dec.rows(); } inline int cols() const { return m_cols; } + inline int rank() const { return m_rank; } + inline const DecompositionType& dec() const { return m_dec; } + inline const MatrixType& originalMatrix() const { return m_originalMatrix; } template inline void evalTo(Dest& dst) const { - static_cast*>(this)->evalTo(dst); + static_cast*>(this)->evalTo(dst); } + + protected: + const DecompositionType& m_dec; + int m_rank, m_cols; + const MatrixType& m_originalMatrix; }; #define EIGEN_MAKE_IMAGE_HELPERS(DecompositionType) \ typedef typename DecompositionType::MatrixType MatrixType; \ typedef typename MatrixType::Scalar Scalar; \ typedef typename MatrixType::RealScalar RealScalar; \ - inline const DecompositionType& dec() const { return this->m_dec; } \ - inline const MatrixType& originalMatrix() const { return this->m_originalMatrix; } \ - inline int rank() const { return this->m_rank; } + typedef ei_image_retval_base Base; \ + using Base::dec; \ + using Base::originalMatrix; \ + using Base::rank; \ + using Base::rows; \ + using Base::cols; \ + ei_image_retval(const DecompositionType& dec, const MatrixType& originalMatrix) \ + : Base(dec, originalMatrix) {} #endif // EIGEN_MISC_IMAGE_H diff --git a/Eigen/src/misc/Kernel.h b/Eigen/src/misc/Kernel.h index 74ef16abc..717eef450 100644 --- a/Eigen/src/misc/Kernel.h +++ b/Eigen/src/misc/Kernel.h @@ -25,11 +25,11 @@ #ifndef EIGEN_MISC_KERNEL_H #define EIGEN_MISC_KERNEL_H -/** \class ei_kernel_return_value +/** \class ei_kernel_retval_base * */ template -struct ei_traits > +struct ei_traits > { typedef typename DecompositionType::MatrixType MatrixType; typedef Matrix< @@ -45,14 +45,12 @@ struct ei_traits > > ReturnMatrixType; }; -template struct ei_kernel_return_value - : public ReturnByValue > +template struct ei_kernel_retval_base + : public ReturnByValue > { typedef _DecompositionType DecompositionType; - const DecompositionType& m_dec; - int m_rank, m_cols; - ei_kernel_return_value(const DecompositionType& dec) + ei_kernel_retval_base(const DecompositionType& dec) : m_dec(dec), m_rank(dec.rank()), m_cols(m_rank==dec.cols() ? 1 : dec.cols() - m_rank) @@ -60,18 +58,28 @@ template struct ei_kernel_return_value inline int rows() const { return m_dec.cols(); } inline int cols() const { return m_cols; } + inline int rank() const { return m_rank; } + inline const DecompositionType& dec() const { return m_dec; } template inline void evalTo(Dest& dst) const { - static_cast*>(this)->evalTo(dst); + static_cast*>(this)->evalTo(dst); } + + protected: + const DecompositionType& m_dec; + int m_rank, m_cols; }; #define EIGEN_MAKE_KERNEL_HELPERS(DecompositionType) \ typedef typename DecompositionType::MatrixType MatrixType; \ typedef typename MatrixType::Scalar Scalar; \ typedef typename MatrixType::RealScalar RealScalar; \ - inline const DecompositionType& dec() const { return this->m_dec; } \ - inline int rank() const { return this->m_rank; } + typedef ei_kernel_retval_base Base; \ + using Base::dec; \ + using Base::rank; \ + using Base::rows; \ + using Base::cols; \ + ei_kernel_retval(const DecompositionType& dec) : Base(dec) {} #endif // EIGEN_MISC_KERNEL_H diff --git a/Eigen/src/misc/Solve.h b/Eigen/src/misc/Solve.h index bf8f15773..d93869121 100644 --- a/Eigen/src/misc/Solve.h +++ b/Eigen/src/misc/Solve.h @@ -25,11 +25,11 @@ #ifndef EIGEN_MISC_SOLVE_H #define EIGEN_MISC_SOLVE_H -/** \class ei_solve_return_value +/** \class ei_solve_retval_base * */ template -struct ei_traits > +struct ei_traits > { typedef typename DecompositionType::MatrixType MatrixType; typedef Matrix > Rhs::MaxColsAtCompileTime> ReturnMatrixType; }; -template struct ei_solve_return_value - : public ReturnByValue > +template struct ei_solve_retval_base + : public ReturnByValue > { typedef typename ei_cleantype::type RhsNestedCleaned; typedef _DecompositionType DecompositionType; - const DecompositionType& m_dec; - const typename Rhs::Nested m_rhs; - ei_solve_return_value(const DecompositionType& dec, const Rhs& rhs) + ei_solve_retval_base(const DecompositionType& dec, const Rhs& rhs) : m_dec(dec), m_rhs(rhs) {} inline int rows() const { return m_dec.cols(); } inline int cols() const { return m_rhs.cols(); } + inline const DecompositionType& dec() const { return m_dec; } + inline const RhsNestedCleaned& rhs() const { return m_rhs; } template inline void evalTo(Dest& dst) const { - static_cast*>(this)->evalTo(dst); + static_cast*>(this)->evalTo(dst); } + + protected: + const DecompositionType& m_dec; + const typename Rhs::Nested m_rhs; }; #define EIGEN_MAKE_SOLVE_HELPERS(DecompositionType,Rhs) \ typedef typename DecompositionType::MatrixType MatrixType; \ typedef typename MatrixType::Scalar Scalar; \ typedef typename MatrixType::RealScalar RealScalar; \ - typedef typename ei_cleantype::type RhsNestedCleaned; \ - inline const DecompositionType& dec() const { return this->m_dec; } \ - inline const RhsNestedCleaned& rhs() const { return this->m_rhs; } + typedef ei_solve_retval_base Base; \ + using Base::dec; \ + using Base::rhs; \ + using Base::rows; \ + using Base::cols; \ + ei_solve_retval(const DecompositionType& dec, const Rhs& rhs) \ + : Base(dec, rhs) {} #endif // EIGEN_MISC_SOLVE_H diff --git a/doc/snippets/FullPivLU_solve.cpp b/doc/snippets/FullPivLU_solve.cpp index 696f414b3..c1f88235e 100644 --- a/doc/snippets/FullPivLU_solve.cpp +++ b/doc/snippets/FullPivLU_solve.cpp @@ -2,7 +2,7 @@ Matrix m = Matrix::Random(); Matrix2f y = Matrix2f::Random(); cout << "Here is the matrix m:" << endl << m << endl; cout << "Here is the matrix y:" << endl << y << endl; -Matrix x = m.fillPivLu().solve(y); +Matrix x = m.fullPivLu().solve(y); if((m*x).isApprox(y)) { cout << "Here is a solution x to the equation mx=y:" << endl << x << endl; diff --git a/test/lu.cpp b/test/lu.cpp index 954893651..9dcebbeaa 100644 --- a/test/lu.cpp +++ b/test/lu.cpp @@ -49,8 +49,8 @@ template void lu_non_invertible() cols2 = cols = MatrixType::ColsAtCompileTime; } - typedef typename ei_kernel_return_value >::ReturnMatrixType KernelMatrixType; - typedef typename ei_image_return_value >::ReturnMatrixType ImageMatrixType; + typedef typename ei_kernel_retval_base >::ReturnMatrixType KernelMatrixType; + typedef typename ei_image_retval_base >::ReturnMatrixType ImageMatrixType; typedef Matrix DynamicMatrixType; typedef Matrix CMatrixType; From 4b366b07be4e409239c61158a23d93e8ebf3811b Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 9 Nov 2009 08:04:20 -0500 Subject: [PATCH 34/34] add missing includes --- Eigen/QR | 1 + Eigen/SVD | 1 + 2 files changed, 2 insertions(+) diff --git a/Eigen/QR b/Eigen/QR index eaa99793e..de0179865 100644 --- a/Eigen/QR +++ b/Eigen/QR @@ -33,6 +33,7 @@ namespace Eigen { * \endcode */ +#include "src/misc/Solve.h" #include "src/QR/HouseholderQR.h" #include "src/QR/FullPivHouseholderQR.h" #include "src/QR/ColPivHouseholderQR.h" diff --git a/Eigen/SVD b/Eigen/SVD index 8d66d0736..44f0f4b31 100644 --- a/Eigen/SVD +++ b/Eigen/SVD @@ -22,6 +22,7 @@ namespace Eigen { * \endcode */ +#include "src/misc/Solve.h" #include "src/SVD/SVD.h" #include "src/SVD/JacobiSVD.h"