Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 1 | // This file is part of Eigen, a lightweight C++ template library |
| 2 | // for linear algebra. |
| 3 | // |
| 4 | // Copyright (C) 2011 Gael Guennebaud <g.gael@free.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 | #include "sparse.h" |
| 11 | #include <Eigen/SparseCore> |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 12 | #include <sstream> |
| 13 | |
| 14 | template<typename Solver, typename Rhs, typename Guess,typename Result> |
| 15 | void solve_with_guess(IterativeSolverBase<Solver>& solver, const MatrixBase<Rhs>& b, const Guess& g, Result &x) { |
| 16 | if(internal::random<bool>()) |
| 17 | { |
| 18 | // With a temporary through evaluator<SolveWithGuess> |
| 19 | x = solver.derived().solveWithGuess(b,g) + Result::Zero(x.rows(), x.cols()); |
| 20 | } |
| 21 | else |
| 22 | { |
| 23 | // direct evaluation within x through Assignment<Result,SolveWithGuess> |
| 24 | x = solver.derived().solveWithGuess(b.derived(),g); |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | template<typename Solver, typename Rhs, typename Guess,typename Result> |
| 29 | void solve_with_guess(SparseSolverBase<Solver>& solver, const MatrixBase<Rhs>& b, const Guess& , Result& x) { |
| 30 | if(internal::random<bool>()) |
| 31 | x = solver.derived().solve(b) + Result::Zero(x.rows(), x.cols()); |
| 32 | else |
| 33 | x = solver.derived().solve(b); |
| 34 | } |
| 35 | |
| 36 | template<typename Solver, typename Rhs, typename Guess,typename Result> |
| 37 | void solve_with_guess(SparseSolverBase<Solver>& solver, const SparseMatrixBase<Rhs>& b, const Guess& , Result& x) { |
| 38 | x = solver.derived().solve(b); |
| 39 | } |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 40 | |
| 41 | template<typename Solver, typename Rhs, typename DenseMat, typename DenseRhs> |
| 42 | void check_sparse_solving(Solver& solver, const typename Solver::MatrixType& A, const Rhs& b, const DenseMat& dA, const DenseRhs& db) |
| 43 | { |
| 44 | typedef typename Solver::MatrixType Mat; |
| 45 | typedef typename Mat::Scalar Scalar; |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 46 | typedef typename Mat::StorageIndex StorageIndex; |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 47 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 48 | DenseRhs refX = dA.householderQr().solve(db); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 49 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 50 | Rhs x(A.cols(), b.cols()); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 51 | Rhs oldb = b; |
| 52 | |
| 53 | solver.compute(A); |
| 54 | if (solver.info() != Success) |
| 55 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 56 | std::cerr << "ERROR | sparse solver testing, factorization failed (" << typeid(Solver).name() << ")\n"; |
| 57 | VERIFY(solver.info() == Success); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 58 | } |
| 59 | x = solver.solve(b); |
| 60 | if (solver.info() != Success) |
| 61 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 62 | std::cerr << "WARNING | sparse solver testing: solving failed (" << typeid(Solver).name() << ")\n"; |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 63 | return; |
| 64 | } |
| 65 | VERIFY(oldb.isApprox(b) && "sparse solver testing: the rhs should not be modified!"); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 66 | VERIFY(x.isApprox(refX,test_precision<Scalar>())); |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 67 | |
| 68 | x.setZero(); |
| 69 | solve_with_guess(solver, b, x, x); |
| 70 | VERIFY(solver.info() == Success && "solving failed when using analyzePattern/factorize API"); |
| 71 | VERIFY(oldb.isApprox(b) && "sparse solver testing: the rhs should not be modified!"); |
| 72 | VERIFY(x.isApprox(refX,test_precision<Scalar>())); |
| 73 | |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 74 | x.setZero(); |
| 75 | // test the analyze/factorize API |
| 76 | solver.analyzePattern(A); |
| 77 | solver.factorize(A); |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 78 | VERIFY(solver.info() == Success && "factorization failed when using analyzePattern/factorize API"); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 79 | x = solver.solve(b); |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 80 | VERIFY(solver.info() == Success && "solving failed when using analyzePattern/factorize API"); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 81 | VERIFY(oldb.isApprox(b) && "sparse solver testing: the rhs should not be modified!"); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 82 | VERIFY(x.isApprox(refX,test_precision<Scalar>())); |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 83 | |
| 84 | x.setZero(); |
| 85 | // test with Map |
| 86 | MappedSparseMatrix<Scalar,Mat::Options,StorageIndex> Am(A.rows(), A.cols(), A.nonZeros(), const_cast<StorageIndex*>(A.outerIndexPtr()), const_cast<StorageIndex*>(A.innerIndexPtr()), const_cast<Scalar*>(A.valuePtr())); |
| 87 | solver.compute(Am); |
| 88 | VERIFY(solver.info() == Success && "factorization failed when using Map"); |
| 89 | DenseRhs dx(refX); |
| 90 | dx.setZero(); |
| 91 | Map<DenseRhs> xm(dx.data(), dx.rows(), dx.cols()); |
| 92 | Map<const DenseRhs> bm(db.data(), db.rows(), db.cols()); |
| 93 | xm = solver.solve(bm); |
| 94 | VERIFY(solver.info() == Success && "solving failed when using Map"); |
| 95 | VERIFY(oldb.isApprox(bm) && "sparse solver testing: the rhs should not be modified!"); |
| 96 | VERIFY(xm.isApprox(refX,test_precision<Scalar>())); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 97 | } |
| 98 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 99 | // if not too large, do some extra check: |
| 100 | if(A.rows()<2000) |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 101 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 102 | // test initialization ctor |
| 103 | { |
| 104 | Rhs x(b.rows(), b.cols()); |
| 105 | Solver solver2(A); |
| 106 | VERIFY(solver2.info() == Success); |
| 107 | x = solver2.solve(b); |
| 108 | VERIFY(x.isApprox(refX,test_precision<Scalar>())); |
| 109 | } |
| 110 | |
| 111 | // test dense Block as the result and rhs: |
| 112 | { |
| 113 | DenseRhs x(refX.rows(), refX.cols()); |
| 114 | DenseRhs oldb(db); |
| 115 | x.setZero(); |
| 116 | x.block(0,0,x.rows(),x.cols()) = solver.solve(db.block(0,0,db.rows(),db.cols())); |
| 117 | VERIFY(oldb.isApprox(db) && "sparse solver testing: the rhs should not be modified!"); |
| 118 | VERIFY(x.isApprox(refX,test_precision<Scalar>())); |
| 119 | } |
| 120 | |
| 121 | // test uncompressed inputs |
| 122 | { |
| 123 | Mat A2 = A; |
| 124 | A2.reserve((ArrayXf::Random(A.outerSize())+2).template cast<typename Mat::StorageIndex>().eval()); |
| 125 | solver.compute(A2); |
| 126 | Rhs x = solver.solve(b); |
| 127 | VERIFY(x.isApprox(refX,test_precision<Scalar>())); |
| 128 | } |
| 129 | |
| 130 | // test expression as input |
| 131 | { |
| 132 | solver.compute(0.5*(A+A)); |
| 133 | Rhs x = solver.solve(b); |
| 134 | VERIFY(x.isApprox(refX,test_precision<Scalar>())); |
| 135 | |
| 136 | Solver solver2(0.5*(A+A)); |
| 137 | Rhs x2 = solver2.solve(b); |
| 138 | VERIFY(x2.isApprox(refX,test_precision<Scalar>())); |
| 139 | } |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 140 | } |
| 141 | } |
| 142 | |
| 143 | template<typename Solver, typename Rhs> |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 144 | void check_sparse_solving_real_cases(Solver& solver, const typename Solver::MatrixType& A, const Rhs& b, const typename Solver::MatrixType& fullA, const Rhs& refX) |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 145 | { |
| 146 | typedef typename Solver::MatrixType Mat; |
| 147 | typedef typename Mat::Scalar Scalar; |
| 148 | typedef typename Mat::RealScalar RealScalar; |
| 149 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 150 | Rhs x(A.cols(), b.cols()); |
| 151 | |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 152 | solver.compute(A); |
| 153 | if (solver.info() != Success) |
| 154 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 155 | std::cerr << "ERROR | sparse solver testing, factorization failed (" << typeid(Solver).name() << ")\n"; |
| 156 | VERIFY(solver.info() == Success); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 157 | } |
| 158 | x = solver.solve(b); |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 159 | |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 160 | if (solver.info() != Success) |
| 161 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 162 | std::cerr << "WARNING | sparse solver testing, solving failed (" << typeid(Solver).name() << ")\n"; |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 163 | return; |
| 164 | } |
| 165 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 166 | RealScalar res_error = (fullA*x-b).norm()/b.norm(); |
| 167 | VERIFY( (res_error <= test_precision<Scalar>() ) && "sparse solver failed without noticing it"); |
| 168 | |
| 169 | |
| 170 | if(refX.size() != 0 && (refX - x).norm()/refX.norm() > test_precision<Scalar>()) |
| 171 | { |
| 172 | std::cerr << "WARNING | found solution is different from the provided reference one\n"; |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | } |
| 176 | template<typename Solver, typename DenseMat> |
| 177 | void check_sparse_determinant(Solver& solver, const typename Solver::MatrixType& A, const DenseMat& dA) |
| 178 | { |
| 179 | typedef typename Solver::MatrixType Mat; |
| 180 | typedef typename Mat::Scalar Scalar; |
| 181 | |
| 182 | solver.compute(A); |
| 183 | if (solver.info() != Success) |
| 184 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 185 | std::cerr << "WARNING | sparse solver testing: factorization failed (check_sparse_determinant)\n"; |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 186 | return; |
| 187 | } |
| 188 | |
| 189 | Scalar refDet = dA.determinant(); |
| 190 | VERIFY_IS_APPROX(refDet,solver.determinant()); |
| 191 | } |
| 192 | template<typename Solver, typename DenseMat> |
| 193 | void check_sparse_abs_determinant(Solver& solver, const typename Solver::MatrixType& A, const DenseMat& dA) |
| 194 | { |
| 195 | using std::abs; |
| 196 | typedef typename Solver::MatrixType Mat; |
| 197 | typedef typename Mat::Scalar Scalar; |
| 198 | |
| 199 | solver.compute(A); |
| 200 | if (solver.info() != Success) |
| 201 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 202 | std::cerr << "WARNING | sparse solver testing: factorization failed (check_sparse_abs_determinant)\n"; |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 203 | return; |
| 204 | } |
| 205 | |
| 206 | Scalar refDet = abs(dA.determinant()); |
| 207 | VERIFY_IS_APPROX(refDet,solver.absDeterminant()); |
| 208 | } |
| 209 | |
| 210 | template<typename Solver, typename DenseMat> |
| 211 | int generate_sparse_spd_problem(Solver& , typename Solver::MatrixType& A, typename Solver::MatrixType& halfA, DenseMat& dA, int maxSize = 300) |
| 212 | { |
| 213 | typedef typename Solver::MatrixType Mat; |
| 214 | typedef typename Mat::Scalar Scalar; |
| 215 | typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; |
| 216 | |
| 217 | int size = internal::random<int>(1,maxSize); |
| 218 | double density = (std::max)(8./(size*size), 0.01); |
| 219 | |
| 220 | Mat M(size, size); |
| 221 | DenseMatrix dM(size, size); |
| 222 | |
| 223 | initSparse<Scalar>(density, dM, M, ForceNonZeroDiag); |
| 224 | |
| 225 | A = M * M.adjoint(); |
| 226 | dA = dM * dM.adjoint(); |
| 227 | |
| 228 | halfA.resize(size,size); |
| 229 | if(Solver::UpLo==(Lower|Upper)) |
| 230 | halfA = A; |
| 231 | else |
| 232 | halfA.template selfadjointView<Solver::UpLo>().rankUpdate(M); |
| 233 | |
| 234 | return size; |
| 235 | } |
| 236 | |
| 237 | |
| 238 | #ifdef TEST_REAL_CASES |
| 239 | template<typename Scalar> |
| 240 | inline std::string get_matrixfolder() |
| 241 | { |
| 242 | std::string mat_folder = TEST_REAL_CASES; |
| 243 | if( internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value ) |
| 244 | mat_folder = mat_folder + static_cast<std::string>("/complex/"); |
| 245 | else |
| 246 | mat_folder = mat_folder + static_cast<std::string>("/real/"); |
| 247 | return mat_folder; |
| 248 | } |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 249 | std::string sym_to_string(int sym) |
| 250 | { |
| 251 | if(sym==Symmetric) return "Symmetric "; |
| 252 | if(sym==SPD) return "SPD "; |
| 253 | return ""; |
| 254 | } |
| 255 | template<typename Derived> |
| 256 | std::string solver_stats(const IterativeSolverBase<Derived> &solver) |
| 257 | { |
| 258 | std::stringstream ss; |
| 259 | ss << solver.iterations() << " iters, error: " << solver.error(); |
| 260 | return ss.str(); |
| 261 | } |
| 262 | template<typename Derived> |
| 263 | std::string solver_stats(const SparseSolverBase<Derived> &/*solver*/) |
| 264 | { |
| 265 | return ""; |
| 266 | } |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 267 | #endif |
| 268 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 269 | template<typename Solver> void check_sparse_spd_solving(Solver& solver, int maxSize = 300, int maxRealWorldSize = 100000) |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 270 | { |
| 271 | typedef typename Solver::MatrixType Mat; |
| 272 | typedef typename Mat::Scalar Scalar; |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 273 | typedef typename Mat::StorageIndex StorageIndex; |
| 274 | typedef SparseMatrix<Scalar,ColMajor, StorageIndex> SpMat; |
| 275 | typedef SparseVector<Scalar, 0, StorageIndex> SpVec; |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 276 | typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; |
| 277 | typedef Matrix<Scalar,Dynamic,1> DenseVector; |
| 278 | |
| 279 | // generate the problem |
| 280 | Mat A, halfA; |
| 281 | DenseMatrix dA; |
| 282 | for (int i = 0; i < g_repeat; i++) { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 283 | int size = generate_sparse_spd_problem(solver, A, halfA, dA, maxSize); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 284 | |
| 285 | // generate the right hand sides |
| 286 | int rhsCols = internal::random<int>(1,16); |
| 287 | double density = (std::max)(8./(size*rhsCols), 0.1); |
| 288 | SpMat B(size,rhsCols); |
| 289 | DenseVector b = DenseVector::Random(size); |
| 290 | DenseMatrix dB(size,rhsCols); |
| 291 | initSparse<Scalar>(density, dB, B, ForceNonZeroDiag); |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 292 | SpVec c = B.col(0); |
| 293 | DenseVector dc = dB.col(0); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 294 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 295 | CALL_SUBTEST( check_sparse_solving(solver, A, b, dA, b) ); |
| 296 | CALL_SUBTEST( check_sparse_solving(solver, halfA, b, dA, b) ); |
| 297 | CALL_SUBTEST( check_sparse_solving(solver, A, dB, dA, dB) ); |
| 298 | CALL_SUBTEST( check_sparse_solving(solver, halfA, dB, dA, dB) ); |
| 299 | CALL_SUBTEST( check_sparse_solving(solver, A, B, dA, dB) ); |
| 300 | CALL_SUBTEST( check_sparse_solving(solver, halfA, B, dA, dB) ); |
| 301 | CALL_SUBTEST( check_sparse_solving(solver, A, c, dA, dc) ); |
| 302 | CALL_SUBTEST( check_sparse_solving(solver, halfA, c, dA, dc) ); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 303 | |
| 304 | // check only once |
| 305 | if(i==0) |
| 306 | { |
| 307 | b = DenseVector::Zero(size); |
| 308 | check_sparse_solving(solver, A, b, dA, b); |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | // First, get the folder |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 313 | #ifdef TEST_REAL_CASES |
| 314 | // Test real problems with double precision only |
| 315 | if (internal::is_same<typename NumTraits<Scalar>::Real, double>::value) |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 316 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 317 | std::string mat_folder = get_matrixfolder<Scalar>(); |
| 318 | MatrixMarketIterator<Scalar> it(mat_folder); |
| 319 | for (; it; ++it) |
| 320 | { |
| 321 | if (it.sym() == SPD){ |
| 322 | A = it.matrix(); |
| 323 | if(A.diagonal().size() <= maxRealWorldSize) |
| 324 | { |
| 325 | DenseVector b = it.rhs(); |
| 326 | DenseVector refX = it.refX(); |
| 327 | PermutationMatrix<Dynamic, Dynamic, StorageIndex> pnull; |
| 328 | halfA.resize(A.rows(), A.cols()); |
| 329 | if(Solver::UpLo == (Lower|Upper)) |
| 330 | halfA = A; |
| 331 | else |
| 332 | halfA.template selfadjointView<Solver::UpLo>() = A.template triangularView<Eigen::Lower>().twistedBy(pnull); |
| 333 | |
| 334 | std::cout << "INFO | Testing " << sym_to_string(it.sym()) << "sparse problem " << it.matname() |
| 335 | << " (" << A.rows() << "x" << A.cols() << ") using " << typeid(Solver).name() << "..." << std::endl; |
| 336 | CALL_SUBTEST( check_sparse_solving_real_cases(solver, A, b, A, refX) ); |
| 337 | std::string stats = solver_stats(solver); |
| 338 | if(stats.size()>0) |
| 339 | std::cout << "INFO | " << stats << std::endl; |
| 340 | CALL_SUBTEST( check_sparse_solving_real_cases(solver, halfA, b, A, refX) ); |
| 341 | } |
| 342 | else |
| 343 | { |
| 344 | std::cout << "INFO | Skip sparse problem \"" << it.matname() << "\" (too large)" << std::endl; |
| 345 | } |
| 346 | } |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 347 | } |
| 348 | } |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 349 | #else |
| 350 | EIGEN_UNUSED_VARIABLE(maxRealWorldSize); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 351 | #endif |
| 352 | } |
| 353 | |
| 354 | template<typename Solver> void check_sparse_spd_determinant(Solver& solver) |
| 355 | { |
| 356 | typedef typename Solver::MatrixType Mat; |
| 357 | typedef typename Mat::Scalar Scalar; |
| 358 | typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; |
| 359 | |
| 360 | // generate the problem |
| 361 | Mat A, halfA; |
| 362 | DenseMatrix dA; |
| 363 | generate_sparse_spd_problem(solver, A, halfA, dA, 30); |
| 364 | |
| 365 | for (int i = 0; i < g_repeat; i++) { |
| 366 | check_sparse_determinant(solver, A, dA); |
| 367 | check_sparse_determinant(solver, halfA, dA ); |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | template<typename Solver, typename DenseMat> |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 372 | Index generate_sparse_square_problem(Solver&, typename Solver::MatrixType& A, DenseMat& dA, int maxSize = 300, int options = ForceNonZeroDiag) |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 373 | { |
| 374 | typedef typename Solver::MatrixType Mat; |
| 375 | typedef typename Mat::Scalar Scalar; |
| 376 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 377 | Index size = internal::random<int>(1,maxSize); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 378 | double density = (std::max)(8./(size*size), 0.01); |
| 379 | |
| 380 | A.resize(size,size); |
| 381 | dA.resize(size,size); |
| 382 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 383 | initSparse<Scalar>(density, dA, A, options); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 384 | |
| 385 | return size; |
| 386 | } |
| 387 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 388 | |
| 389 | struct prune_column { |
| 390 | Index m_col; |
| 391 | prune_column(Index col) : m_col(col) {} |
| 392 | template<class Scalar> |
| 393 | bool operator()(Index, Index col, const Scalar&) const { |
| 394 | return col != m_col; |
| 395 | } |
| 396 | }; |
| 397 | |
| 398 | |
| 399 | template<typename Solver> void check_sparse_square_solving(Solver& solver, int maxSize = 300, int maxRealWorldSize = 100000, bool checkDeficient = false) |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 400 | { |
| 401 | typedef typename Solver::MatrixType Mat; |
| 402 | typedef typename Mat::Scalar Scalar; |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 403 | typedef SparseMatrix<Scalar,ColMajor, typename Mat::StorageIndex> SpMat; |
| 404 | typedef SparseVector<Scalar, 0, typename Mat::StorageIndex> SpVec; |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 405 | typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; |
| 406 | typedef Matrix<Scalar,Dynamic,1> DenseVector; |
| 407 | |
| 408 | int rhsCols = internal::random<int>(1,16); |
| 409 | |
| 410 | Mat A; |
| 411 | DenseMatrix dA; |
| 412 | for (int i = 0; i < g_repeat; i++) { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 413 | Index size = generate_sparse_square_problem(solver, A, dA, maxSize); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 414 | |
| 415 | A.makeCompressed(); |
| 416 | DenseVector b = DenseVector::Random(size); |
| 417 | DenseMatrix dB(size,rhsCols); |
| 418 | SpMat B(size,rhsCols); |
| 419 | double density = (std::max)(8./(size*rhsCols), 0.1); |
| 420 | initSparse<Scalar>(density, dB, B, ForceNonZeroDiag); |
| 421 | B.makeCompressed(); |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 422 | SpVec c = B.col(0); |
| 423 | DenseVector dc = dB.col(0); |
| 424 | CALL_SUBTEST(check_sparse_solving(solver, A, b, dA, b)); |
| 425 | CALL_SUBTEST(check_sparse_solving(solver, A, dB, dA, dB)); |
| 426 | CALL_SUBTEST(check_sparse_solving(solver, A, B, dA, dB)); |
| 427 | CALL_SUBTEST(check_sparse_solving(solver, A, c, dA, dc)); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 428 | |
| 429 | // check only once |
| 430 | if(i==0) |
| 431 | { |
| 432 | b = DenseVector::Zero(size); |
| 433 | check_sparse_solving(solver, A, b, dA, b); |
| 434 | } |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 435 | // regression test for Bug 792 (structurally rank deficient matrices): |
| 436 | if(checkDeficient && size>1) { |
| 437 | Index col = internal::random<int>(0,int(size-1)); |
| 438 | A.prune(prune_column(col)); |
| 439 | solver.compute(A); |
| 440 | VERIFY_IS_EQUAL(solver.info(), NumericalIssue); |
| 441 | } |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 442 | } |
| 443 | |
| 444 | // First, get the folder |
| 445 | #ifdef TEST_REAL_CASES |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 446 | // Test real problems with double precision only |
| 447 | if (internal::is_same<typename NumTraits<Scalar>::Real, double>::value) |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 448 | { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 449 | std::string mat_folder = get_matrixfolder<Scalar>(); |
| 450 | MatrixMarketIterator<Scalar> it(mat_folder); |
| 451 | for (; it; ++it) |
| 452 | { |
| 453 | A = it.matrix(); |
| 454 | if(A.diagonal().size() <= maxRealWorldSize) |
| 455 | { |
| 456 | DenseVector b = it.rhs(); |
| 457 | DenseVector refX = it.refX(); |
| 458 | std::cout << "INFO | Testing " << sym_to_string(it.sym()) << "sparse problem " << it.matname() |
| 459 | << " (" << A.rows() << "x" << A.cols() << ") using " << typeid(Solver).name() << "..." << std::endl; |
| 460 | CALL_SUBTEST(check_sparse_solving_real_cases(solver, A, b, A, refX)); |
| 461 | std::string stats = solver_stats(solver); |
| 462 | if(stats.size()>0) |
| 463 | std::cout << "INFO | " << stats << std::endl; |
| 464 | } |
| 465 | else |
| 466 | { |
| 467 | std::cout << "INFO | SKIP sparse problem \"" << it.matname() << "\" (too large)" << std::endl; |
| 468 | } |
| 469 | } |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 470 | } |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 471 | #else |
| 472 | EIGEN_UNUSED_VARIABLE(maxRealWorldSize); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 473 | #endif |
| 474 | |
| 475 | } |
| 476 | |
| 477 | template<typename Solver> void check_sparse_square_determinant(Solver& solver) |
| 478 | { |
| 479 | typedef typename Solver::MatrixType Mat; |
| 480 | typedef typename Mat::Scalar Scalar; |
| 481 | typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 482 | |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 483 | for (int i = 0; i < g_repeat; i++) { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 484 | // generate the problem |
| 485 | Mat A; |
| 486 | DenseMatrix dA; |
| 487 | |
| 488 | int size = internal::random<int>(1,30); |
| 489 | dA.setRandom(size,size); |
| 490 | |
| 491 | dA = (dA.array().abs()<0.3).select(0,dA); |
| 492 | dA.diagonal() = (dA.diagonal().array()==0).select(1,dA.diagonal()); |
| 493 | A = dA.sparseView(); |
| 494 | A.makeCompressed(); |
| 495 | |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 496 | check_sparse_determinant(solver, A, dA); |
| 497 | } |
| 498 | } |
| 499 | |
| 500 | template<typename Solver> void check_sparse_square_abs_determinant(Solver& solver) |
| 501 | { |
| 502 | typedef typename Solver::MatrixType Mat; |
| 503 | typedef typename Mat::Scalar Scalar; |
| 504 | typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; |
| 505 | |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 506 | for (int i = 0; i < g_repeat; i++) { |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 507 | // generate the problem |
| 508 | Mat A; |
| 509 | DenseMatrix dA; |
| 510 | generate_sparse_square_problem(solver, A, dA, 30); |
| 511 | A.makeCompressed(); |
Brian Silverman | 72890c2 | 2015-09-19 14:37:37 -0400 | [diff] [blame] | 512 | check_sparse_abs_determinant(solver, A, dA); |
| 513 | } |
| 514 | } |
| 515 | |
Austin Schuh | 189376f | 2018-12-20 22:11:15 +1100 | [diff] [blame] | 516 | template<typename Solver, typename DenseMat> |
| 517 | void generate_sparse_leastsquare_problem(Solver&, typename Solver::MatrixType& A, DenseMat& dA, int maxSize = 300, int options = ForceNonZeroDiag) |
| 518 | { |
| 519 | typedef typename Solver::MatrixType Mat; |
| 520 | typedef typename Mat::Scalar Scalar; |
| 521 | |
| 522 | int rows = internal::random<int>(1,maxSize); |
| 523 | int cols = internal::random<int>(1,rows); |
| 524 | double density = (std::max)(8./(rows*cols), 0.01); |
| 525 | |
| 526 | A.resize(rows,cols); |
| 527 | dA.resize(rows,cols); |
| 528 | |
| 529 | initSparse<Scalar>(density, dA, A, options); |
| 530 | } |
| 531 | |
| 532 | template<typename Solver> void check_sparse_leastsquare_solving(Solver& solver) |
| 533 | { |
| 534 | typedef typename Solver::MatrixType Mat; |
| 535 | typedef typename Mat::Scalar Scalar; |
| 536 | typedef SparseMatrix<Scalar,ColMajor, typename Mat::StorageIndex> SpMat; |
| 537 | typedef Matrix<Scalar,Dynamic,Dynamic> DenseMatrix; |
| 538 | typedef Matrix<Scalar,Dynamic,1> DenseVector; |
| 539 | |
| 540 | int rhsCols = internal::random<int>(1,16); |
| 541 | |
| 542 | Mat A; |
| 543 | DenseMatrix dA; |
| 544 | for (int i = 0; i < g_repeat; i++) { |
| 545 | generate_sparse_leastsquare_problem(solver, A, dA); |
| 546 | |
| 547 | A.makeCompressed(); |
| 548 | DenseVector b = DenseVector::Random(A.rows()); |
| 549 | DenseMatrix dB(A.rows(),rhsCols); |
| 550 | SpMat B(A.rows(),rhsCols); |
| 551 | double density = (std::max)(8./(A.rows()*rhsCols), 0.1); |
| 552 | initSparse<Scalar>(density, dB, B, ForceNonZeroDiag); |
| 553 | B.makeCompressed(); |
| 554 | check_sparse_solving(solver, A, b, dA, b); |
| 555 | check_sparse_solving(solver, A, dB, dA, dB); |
| 556 | check_sparse_solving(solver, A, B, dA, dB); |
| 557 | |
| 558 | // check only once |
| 559 | if(i==0) |
| 560 | { |
| 561 | b = DenseVector::Zero(A.rows()); |
| 562 | check_sparse_solving(solver, A, b, dA, b); |
| 563 | } |
| 564 | } |
| 565 | } |