1 // This file is part of Eigen, a lightweight C++ template library 2 // for linear algebra. 3 // 4 // Copyright (C) 2008 Gael Guennebaud <gael.guennebaud (at) inria.fr> 5 // 6 // This Source Code Form is subject to the terms of the Mozilla 7 // Public License v. 2.0. If a copy of the MPL was not distributed 8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 10 #ifndef EIGEN_NO_ASSERTION_CHECKING 11 #define EIGEN_NO_ASSERTION_CHECKING 12 #endif 13 14 static int nb_temporaries; 15 16 #define EIGEN_DENSE_STORAGE_CTOR_PLUGIN { if(size!=0) nb_temporaries++; } 17 18 #include "main.h" 19 #include <Eigen/Cholesky> 20 #include <Eigen/QR> 21 22 #define VERIFY_EVALUATION_COUNT(XPR,N) {\ 23 nb_temporaries = 0; \ 24 XPR; \ 25 if(nb_temporaries!=N) std::cerr << "nb_temporaries == " << nb_temporaries << "\n"; \ 26 VERIFY( (#XPR) && nb_temporaries==N ); \ 27 } 28 29 template<typename MatrixType,template <typename,int> class CholType> void test_chol_update(const MatrixType& symm) 30 { 31 typedef typename MatrixType::Scalar Scalar; 32 typedef typename MatrixType::RealScalar RealScalar; 33 typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; 34 35 MatrixType symmLo = symm.template triangularView<Lower>(); 36 MatrixType symmUp = symm.template triangularView<Upper>(); 37 MatrixType symmCpy = symm; 38 39 CholType<MatrixType,Lower> chollo(symmLo); 40 CholType<MatrixType,Upper> cholup(symmUp); 41 42 for (int k=0; k<10; ++k) 43 { 44 VectorType vec = VectorType::Random(symm.rows()); 45 RealScalar sigma = internal::random<RealScalar>(); 46 symmCpy += sigma * vec * vec.adjoint(); 47 48 // we are doing some downdates, so it might be the case that the matrix is not SPD anymore 49 CholType<MatrixType,Lower> chol(symmCpy); 50 if(chol.info()!=Success) 51 break; 52 53 chollo.rankUpdate(vec, sigma); 54 VERIFY_IS_APPROX(symmCpy, chollo.reconstructedMatrix()); 55 56 cholup.rankUpdate(vec, sigma); 57 VERIFY_IS_APPROX(symmCpy, cholup.reconstructedMatrix()); 58 } 59 } 60 61 template<typename MatrixType> void cholesky(const MatrixType& m) 62 { 63 typedef typename MatrixType::Index Index; 64 /* this test covers the following files: 65 LLT.h LDLT.h 66 */ 67 Index rows = m.rows(); 68 Index cols = m.cols(); 69 70 typedef typename MatrixType::Scalar Scalar; 71 typedef typename NumTraits<Scalar>::Real RealScalar; 72 typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> SquareMatrixType; 73 typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; 74 75 MatrixType a0 = MatrixType::Random(rows,cols); 76 VectorType vecB = VectorType::Random(rows), vecX(rows); 77 MatrixType matB = MatrixType::Random(rows,cols), matX(rows,cols); 78 SquareMatrixType symm = a0 * a0.adjoint(); 79 // let's make sure the matrix is not singular or near singular 80 for (int k=0; k<3; ++k) 81 { 82 MatrixType a1 = MatrixType::Random(rows,cols); 83 symm += a1 * a1.adjoint(); 84 } 85 86 // to test if really Cholesky only uses the upper triangular part, uncomment the following 87 // FIXME: currently that fails !! 88 //symm.template part<StrictlyLower>().setZero(); 89 90 { 91 SquareMatrixType symmUp = symm.template triangularView<Upper>(); 92 SquareMatrixType symmLo = symm.template triangularView<Lower>(); 93 94 LLT<SquareMatrixType,Lower> chollo(symmLo); 95 VERIFY_IS_APPROX(symm, chollo.reconstructedMatrix()); 96 vecX = chollo.solve(vecB); 97 VERIFY_IS_APPROX(symm * vecX, vecB); 98 matX = chollo.solve(matB); 99 VERIFY_IS_APPROX(symm * matX, matB); 100 101 // test the upper mode 102 LLT<SquareMatrixType,Upper> cholup(symmUp); 103 VERIFY_IS_APPROX(symm, cholup.reconstructedMatrix()); 104 vecX = cholup.solve(vecB); 105 VERIFY_IS_APPROX(symm * vecX, vecB); 106 matX = cholup.solve(matB); 107 VERIFY_IS_APPROX(symm * matX, matB); 108 109 MatrixType neg = -symmLo; 110 chollo.compute(neg); 111 VERIFY(chollo.info()==NumericalIssue); 112 113 VERIFY_IS_APPROX(MatrixType(chollo.matrixL().transpose().conjugate()), MatrixType(chollo.matrixU())); 114 VERIFY_IS_APPROX(MatrixType(chollo.matrixU().transpose().conjugate()), MatrixType(chollo.matrixL())); 115 VERIFY_IS_APPROX(MatrixType(cholup.matrixL().transpose().conjugate()), MatrixType(cholup.matrixU())); 116 VERIFY_IS_APPROX(MatrixType(cholup.matrixU().transpose().conjugate()), MatrixType(cholup.matrixL())); 117 118 // test some special use cases of SelfCwiseBinaryOp: 119 MatrixType m1 = MatrixType::Random(rows,cols), m2(rows,cols); 120 m2 = m1; 121 m2 += symmLo.template selfadjointView<Lower>().llt().solve(matB); 122 VERIFY_IS_APPROX(m2, m1 + symmLo.template selfadjointView<Lower>().llt().solve(matB)); 123 m2 = m1; 124 m2 -= symmLo.template selfadjointView<Lower>().llt().solve(matB); 125 VERIFY_IS_APPROX(m2, m1 - symmLo.template selfadjointView<Lower>().llt().solve(matB)); 126 m2 = m1; 127 m2.noalias() += symmLo.template selfadjointView<Lower>().llt().solve(matB); 128 VERIFY_IS_APPROX(m2, m1 + symmLo.template selfadjointView<Lower>().llt().solve(matB)); 129 m2 = m1; 130 m2.noalias() -= symmLo.template selfadjointView<Lower>().llt().solve(matB); 131 VERIFY_IS_APPROX(m2, m1 - symmLo.template selfadjointView<Lower>().llt().solve(matB)); 132 } 133 134 // LDLT 135 { 136 int sign = internal::random<int>()%2 ? 1 : -1; 137 138 if(sign == -1) 139 { 140 symm = -symm; // test a negative matrix 141 } 142 143 SquareMatrixType symmUp = symm.template triangularView<Upper>(); 144 SquareMatrixType symmLo = symm.template triangularView<Lower>(); 145 146 LDLT<SquareMatrixType,Lower> ldltlo(symmLo); 147 VERIFY_IS_APPROX(symm, ldltlo.reconstructedMatrix()); 148 vecX = ldltlo.solve(vecB); 149 VERIFY_IS_APPROX(symm * vecX, vecB); 150 matX = ldltlo.solve(matB); 151 VERIFY_IS_APPROX(symm * matX, matB); 152 153 LDLT<SquareMatrixType,Upper> ldltup(symmUp); 154 VERIFY_IS_APPROX(symm, ldltup.reconstructedMatrix()); 155 vecX = ldltup.solve(vecB); 156 VERIFY_IS_APPROX(symm * vecX, vecB); 157 matX = ldltup.solve(matB); 158 VERIFY_IS_APPROX(symm * matX, matB); 159 160 VERIFY_IS_APPROX(MatrixType(ldltlo.matrixL().transpose().conjugate()), MatrixType(ldltlo.matrixU())); 161 VERIFY_IS_APPROX(MatrixType(ldltlo.matrixU().transpose().conjugate()), MatrixType(ldltlo.matrixL())); 162 VERIFY_IS_APPROX(MatrixType(ldltup.matrixL().transpose().conjugate()), MatrixType(ldltup.matrixU())); 163 VERIFY_IS_APPROX(MatrixType(ldltup.matrixU().transpose().conjugate()), MatrixType(ldltup.matrixL())); 164 165 if(MatrixType::RowsAtCompileTime==Dynamic) 166 { 167 // note : each inplace permutation requires a small temporary vector (mask) 168 169 // check inplace solve 170 matX = matB; 171 VERIFY_EVALUATION_COUNT(matX = ldltlo.solve(matX), 0); 172 VERIFY_IS_APPROX(matX, ldltlo.solve(matB).eval()); 173 174 175 matX = matB; 176 VERIFY_EVALUATION_COUNT(matX = ldltup.solve(matX), 0); 177 VERIFY_IS_APPROX(matX, ldltup.solve(matB).eval()); 178 } 179 180 // restore 181 if(sign == -1) 182 symm = -symm; 183 184 // check matrices coming from linear constraints with Lagrange multipliers 185 if(rows>=3) 186 { 187 SquareMatrixType A = symm; 188 int c = internal::random<int>(0,rows-2); 189 A.bottomRightCorner(c,c).setZero(); 190 // Make sure a solution exists: 191 vecX.setRandom(); 192 vecB = A * vecX; 193 vecX.setZero(); 194 ldltlo.compute(A); 195 VERIFY_IS_APPROX(A, ldltlo.reconstructedMatrix()); 196 vecX = ldltlo.solve(vecB); 197 VERIFY_IS_APPROX(A * vecX, vecB); 198 } 199 200 // check non-full rank matrices 201 if(rows>=3) 202 { 203 int r = internal::random<int>(1,rows-1); 204 Matrix<Scalar,Dynamic,Dynamic> a = Matrix<Scalar,Dynamic,Dynamic>::Random(rows,r); 205 SquareMatrixType A = a * a.adjoint(); 206 // Make sure a solution exists: 207 vecX.setRandom(); 208 vecB = A * vecX; 209 vecX.setZero(); 210 ldltlo.compute(A); 211 VERIFY_IS_APPROX(A, ldltlo.reconstructedMatrix()); 212 vecX = ldltlo.solve(vecB); 213 VERIFY_IS_APPROX(A * vecX, vecB); 214 } 215 216 // check matrices with a wide spectrum 217 if(rows>=3) 218 { 219 RealScalar s = (std::min)(16,std::numeric_limits<RealScalar>::max_exponent10/8); 220 Matrix<Scalar,Dynamic,Dynamic> a = Matrix<Scalar,Dynamic,Dynamic>::Random(rows,rows); 221 Matrix<RealScalar,Dynamic,1> d = Matrix<RealScalar,Dynamic,1>::Random(rows); 222 for(int k=0; k<rows; ++k) 223 d(k) = d(k)*std::pow(RealScalar(10),internal::random<RealScalar>(-s,s)); 224 SquareMatrixType A = a * d.asDiagonal() * a.adjoint(); 225 // Make sure a solution exists: 226 vecX.setRandom(); 227 vecB = A * vecX; 228 vecX.setZero(); 229 ldltlo.compute(A); 230 VERIFY_IS_APPROX(A, ldltlo.reconstructedMatrix()); 231 vecX = ldltlo.solve(vecB); 232 VERIFY_IS_APPROX(A * vecX, vecB); 233 } 234 } 235 236 // update/downdate 237 CALL_SUBTEST(( test_chol_update<SquareMatrixType,LLT>(symm) )); 238 CALL_SUBTEST(( test_chol_update<SquareMatrixType,LDLT>(symm) )); 239 } 240 241 template<typename MatrixType> void cholesky_cplx(const MatrixType& m) 242 { 243 // classic test 244 cholesky(m); 245 246 // test mixing real/scalar types 247 248 typedef typename MatrixType::Index Index; 249 250 Index rows = m.rows(); 251 Index cols = m.cols(); 252 253 typedef typename MatrixType::Scalar Scalar; 254 typedef typename NumTraits<Scalar>::Real RealScalar; 255 typedef Matrix<RealScalar, MatrixType::RowsAtCompileTime, MatrixType::RowsAtCompileTime> RealMatrixType; 256 typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; 257 258 RealMatrixType a0 = RealMatrixType::Random(rows,cols); 259 VectorType vecB = VectorType::Random(rows), vecX(rows); 260 MatrixType matB = MatrixType::Random(rows,cols), matX(rows,cols); 261 RealMatrixType symm = a0 * a0.adjoint(); 262 // let's make sure the matrix is not singular or near singular 263 for (int k=0; k<3; ++k) 264 { 265 RealMatrixType a1 = RealMatrixType::Random(rows,cols); 266 symm += a1 * a1.adjoint(); 267 } 268 269 { 270 RealMatrixType symmLo = symm.template triangularView<Lower>(); 271 272 LLT<RealMatrixType,Lower> chollo(symmLo); 273 VERIFY_IS_APPROX(symm, chollo.reconstructedMatrix()); 274 vecX = chollo.solve(vecB); 275 VERIFY_IS_APPROX(symm * vecX, vecB); 276 // matX = chollo.solve(matB); 277 // VERIFY_IS_APPROX(symm * matX, matB); 278 } 279 280 // LDLT 281 { 282 int sign = internal::random<int>()%2 ? 1 : -1; 283 284 if(sign == -1) 285 { 286 symm = -symm; // test a negative matrix 287 } 288 289 RealMatrixType symmLo = symm.template triangularView<Lower>(); 290 291 LDLT<RealMatrixType,Lower> ldltlo(symmLo); 292 VERIFY_IS_APPROX(symm, ldltlo.reconstructedMatrix()); 293 vecX = ldltlo.solve(vecB); 294 VERIFY_IS_APPROX(symm * vecX, vecB); 295 // matX = ldltlo.solve(matB); 296 // VERIFY_IS_APPROX(symm * matX, matB); 297 } 298 } 299 300 // regression test for bug 241 301 template<typename MatrixType> void cholesky_bug241(const MatrixType& m) 302 { 303 eigen_assert(m.rows() == 2 && m.cols() == 2); 304 305 typedef typename MatrixType::Scalar Scalar; 306 typedef Matrix<Scalar, MatrixType::RowsAtCompileTime, 1> VectorType; 307 308 MatrixType matA; 309 matA << 1, 1, 1, 1; 310 VectorType vecB; 311 vecB << 1, 1; 312 VectorType vecX = matA.ldlt().solve(vecB); 313 VERIFY_IS_APPROX(matA * vecX, vecB); 314 } 315 316 // LDLT is not guaranteed to work for indefinite matrices, but happens to work fine if matrix is diagonal. 317 // This test checks that LDLT reports correctly that matrix is indefinite. 318 // See http://forum.kde.org/viewtopic.php?f=74&t=106942 and bug 736 319 template<typename MatrixType> void cholesky_definiteness(const MatrixType& m) 320 { 321 eigen_assert(m.rows() == 2 && m.cols() == 2); 322 MatrixType mat; 323 LDLT<MatrixType> ldlt(2); 324 325 { 326 mat << 1, 0, 0, -1; 327 ldlt.compute(mat); 328 VERIFY(!ldlt.isNegative()); 329 VERIFY(!ldlt.isPositive()); 330 } 331 { 332 mat << 1, 2, 2, 1; 333 ldlt.compute(mat); 334 VERIFY(!ldlt.isNegative()); 335 VERIFY(!ldlt.isPositive()); 336 } 337 { 338 mat << 0, 0, 0, 0; 339 ldlt.compute(mat); 340 VERIFY(ldlt.isNegative()); 341 VERIFY(ldlt.isPositive()); 342 } 343 { 344 mat << 0, 0, 0, 1; 345 ldlt.compute(mat); 346 VERIFY(!ldlt.isNegative()); 347 VERIFY(ldlt.isPositive()); 348 } 349 { 350 mat << -1, 0, 0, 0; 351 ldlt.compute(mat); 352 VERIFY(ldlt.isNegative()); 353 VERIFY(!ldlt.isPositive()); 354 } 355 } 356 357 template<typename MatrixType> void cholesky_verify_assert() 358 { 359 MatrixType tmp; 360 361 LLT<MatrixType> llt; 362 VERIFY_RAISES_ASSERT(llt.matrixL()) 363 VERIFY_RAISES_ASSERT(llt.matrixU()) 364 VERIFY_RAISES_ASSERT(llt.solve(tmp)) 365 VERIFY_RAISES_ASSERT(llt.solveInPlace(&tmp)) 366 367 LDLT<MatrixType> ldlt; 368 VERIFY_RAISES_ASSERT(ldlt.matrixL()) 369 VERIFY_RAISES_ASSERT(ldlt.permutationP()) 370 VERIFY_RAISES_ASSERT(ldlt.vectorD()) 371 VERIFY_RAISES_ASSERT(ldlt.isPositive()) 372 VERIFY_RAISES_ASSERT(ldlt.isNegative()) 373 VERIFY_RAISES_ASSERT(ldlt.solve(tmp)) 374 VERIFY_RAISES_ASSERT(ldlt.solveInPlace(&tmp)) 375 } 376 377 void test_cholesky() 378 { 379 int s = 0; 380 for(int i = 0; i < g_repeat; i++) { 381 CALL_SUBTEST_1( cholesky(Matrix<double,1,1>()) ); 382 CALL_SUBTEST_3( cholesky(Matrix2d()) ); 383 CALL_SUBTEST_3( cholesky_bug241(Matrix2d()) ); 384 CALL_SUBTEST_3( cholesky_definiteness(Matrix2d()) ); 385 CALL_SUBTEST_4( cholesky(Matrix3f()) ); 386 CALL_SUBTEST_5( cholesky(Matrix4d()) ); 387 s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE); 388 CALL_SUBTEST_2( cholesky(MatrixXd(s,s)) ); 389 s = internal::random<int>(1,EIGEN_TEST_MAX_SIZE/2); 390 CALL_SUBTEST_6( cholesky_cplx(MatrixXcd(s,s)) ); 391 } 392 393 CALL_SUBTEST_4( cholesky_verify_assert<Matrix3f>() ); 394 CALL_SUBTEST_7( cholesky_verify_assert<Matrix3d>() ); 395 CALL_SUBTEST_8( cholesky_verify_assert<MatrixXf>() ); 396 CALL_SUBTEST_2( cholesky_verify_assert<MatrixXd>() ); 397 398 // Test problem size constructors 399 CALL_SUBTEST_9( LLT<MatrixXf>(10) ); 400 CALL_SUBTEST_9( LDLT<MatrixXf>(10) ); 401 402 TEST_SET_BUT_UNUSED_VARIABLE(s) 403 TEST_SET_BUT_UNUSED_VARIABLE(nb_temporaries) 404 } 405