// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2011 Gael Guennebaud // Copyright (C) 2012 desire Nuentsa template void test_dgmres_T() { DGMRES, DiagonalPreconditioner > dgmres_colmajor_diag; DGMRES, IdentityPreconditioner> dgmres_colmajor_I; DGMRES, IncompleteLUT > dgmres_colmajor_ilut; // GMRES, SSORPreconditioner > dgmres_colmajor_ssor; CALL_SUBTEST(check_sparse_square_solving(dgmres_colmajor_diag)); // CALL_SUBTEST( check_sparse_square_solving(dgmres_colmajor_I) ); CALL_SUBTEST(check_sparse_square_solving(dgmres_colmajor_ilut)); // CALL_SUBTEST( check_sparse_square_solving(dgmres_colmajor_ssor) ); } // Regression: Arnoldi breakdown used to divide by zero (producing NaN in the // Krylov basis) and solve a singular triangular system, silently returning // Inf with info() == Success. Exercise both the pathological (rank-deficient // pivot) and benign (exact Krylov subspace) breakdown paths. template void test_dgmres_breakdown_T() { typedef SparseMatrix Mat; typedef Matrix Vec; // Nilpotent A with singular Hessenberg pivot on the first step. Mat A(2, 2); A.insert(0, 1) = T(1); A.makeCompressed(); Vec b; b << T(1), T(0); DGMRES solver; solver.compute(A); Vec x = solver.solve(b); VERIFY(x.allFinite()); VERIFY(solver.info() != Success); // Diagonal A with b in an eigenspace: Arnoldi converges after one step. Mat D(2, 2); D.insert(0, 0) = T(2); D.insert(1, 1) = T(2); D.makeCompressed(); Vec d; d << T(2), T(2); DGMRES > solver2; solver2.compute(D); Vec y = solver2.solve(d); VERIFY_IS_EQUAL(solver2.info(), Success); VERIFY_IS_APPROX(y, (Vec() << T(1), T(1)).finished()); } // Regression: dgmres() used m_iterations only as the iteration cap and never // wrote the performed count back, so iterations() returned maxIterations() // after every solve, however quickly it converged. template void test_dgmres_iterations_T() { using Mat = SparseMatrix; using DenseMat = Matrix; using Vec = Matrix; using RealScalar = typename NumTraits::Real; // Well-conditioned tridiagonal system. Its size stays below the default // restart length of 30, so a converged solve ends inside the first cycle. const Index n = 20; Mat A(n, n); A.reserve(3 * n); for (Index i = 0; i < n; ++i) { if (i > 0) A.insert(i, i - 1) = T(-1); A.insert(i, i) = T(4); if (i + 1 < n) A.insert(i, i + 1) = T(-1); } A.makeCompressed(); const Index max_iters = 500; // The system is well conditioned, so the true residual tracks the tolerance // the solver converged to, which defaults to NumTraits::epsilon(). const RealScalar res_bound = RealScalar(64) * NumTraits::epsilon(); Vec b = Vec::Constant(n, T(1)); DGMRES > solver; solver.setMaxIterations(max_iters); solver.compute(A); Vec x = solver.solve(b); VERIFY_IS_EQUAL(solver.info(), Success); VERIFY(solver.iterations() > 0); VERIFY(solver.iterations() < solver.maxIterations()); VERIFY((A * x - b).norm() <= res_bound * b.norm()); // Zero right hand side: the early return reports no iteration at all. Vec zero = Vec::Zero(n); Vec x0 = solver.solve(zero); VERIFY(x0.isZero()); VERIFY_IS_EQUAL(solver.iterations(), Index(0)); // Several right hand sides: the cap is restored for every column, so a cheap // first column must not throttle a costlier second one. DenseMat B = DenseMat::Zero(n, 2); B.col(0).setConstant(T(1)); B(0, 1) = T(1); DGMRES > multi; multi.setMaxIterations(max_iters); multi.compute(A); DenseMat X = multi.solve(B); VERIFY_IS_EQUAL(multi.info(), Success); VERIFY(multi.iterations() > 0); VERIFY(multi.iterations() < multi.maxIterations()); VERIFY((A * X - B).norm() <= res_bound * B.norm()); // Non-converging direction: GMRES stagnates on a cyclic shift matrix, so the // reported count saturates at the cap and never exceeds it. const Index m = 16; Mat S(m, m); S.reserve(m); for (Index i = 0; i < m; ++i) S.insert((i + 1) % m, i) = T(1); S.makeCompressed(); Vec e = Vec::Zero(m); e(0) = T(1); for (Index k = 1; k <= 3; ++k) { DGMRES stalled; stalled.setMaxIterations(k); stalled.compute(S); Vec xs = stalled.solve(e); VERIFY(xs.allFinite()); VERIFY_IS_EQUAL(stalled.info(), NoConvergence); VERIFY(stalled.iterations() <= stalled.maxIterations()); VERIFY_IS_EQUAL(stalled.iterations(), k); } } EIGEN_DECLARE_TEST(dgmres) { CALL_SUBTEST_1(test_dgmres_T()); CALL_SUBTEST_2(test_dgmres_T >()); CALL_SUBTEST_3(test_dgmres_breakdown_T()); CALL_SUBTEST_4(test_dgmres_breakdown_T >()); CALL_SUBTEST_5(test_dgmres_iterations_T()); CALL_SUBTEST_6(test_dgmres_iterations_T >()); }