Skip to content
Snippets Groups Projects
Commit 3add9d5c authored by oliver.sander_at_tu-dresden.de's avatar oliver.sander_at_tu-dresden.de
Browse files

Merge branch 'feature/cholmod-reuse-factorization' into 'master'

CholmodSolver: Allow to reuse a factorization

See merge request agnumpde/dune-solvers!67
parents 48a882a2 bf6b2248
No related branches found
No related tags found
No related merge requests found
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
- CholmodSolver: `CholmodSolver` can be used with any blocked matrix/vector type supported by `flatMatrixForEach` and `flatVectorForEach` in dune-istl. - CholmodSolver: `CholmodSolver` can be used with any blocked matrix/vector type supported by `flatMatrixForEach` and `flatVectorForEach` in dune-istl.
- CholmodSolver: There are now various new methods that allow to factorize the matrix once,
and use the factorization to solve linear systems with several right hand sides.
## Deprecations and removals ## Deprecations and removals
- The file `blockgsstep.hh` has been removed after four years of deprecation. Use the replacement - The file `blockgsstep.hh` has been removed after four years of deprecation. Use the replacement
......
...@@ -46,7 +46,14 @@ public: ...@@ -46,7 +46,14 @@ public:
/** \brief Default constructor */ /** \brief Default constructor */
CholmodSolver () CholmodSolver ()
: LinearSolver<MatrixType,VectorType>(NumProc::FULL) : LinearSolver<MatrixType,VectorType>(NumProc::FULL)
{} {
// Bug in LibScotchMetis:
// metis_dswitch: see cholmod.h: LibScotchMetis throws segmentation faults for large problems.
// This caused a hard to understand CI problems and therefore, METIS is disabled for problems
// larger than defined in metis_nswitch for now (default 3000).
// Bug report: https://gitlab.inria.fr/scotch/scotch/issues/8
cholmodCommonObject().metis_dswitch = 0.0;
}
/** \brief Constructor for a linear problem */ /** \brief Constructor for a linear problem */
CholmodSolver (const MatrixType& matrix, CholmodSolver (const MatrixType& matrix,
...@@ -57,56 +64,117 @@ public: ...@@ -57,56 +64,117 @@ public:
matrix_(&matrix), matrix_(&matrix),
x_(&x), x_(&x),
rhs_(&rhs) rhs_(&rhs)
{} {
// Bug in LibScotchMetis:
// metis_dswitch: see cholmod.h: LibScotchMetis throws segmentation faults for large problems.
// This caused a hard to understand CI problems and therefore, METIS is disabled for problems
// larger than defined in metis_nswitch for now (default 3000).
// Bug report: https://gitlab.inria.fr/scotch/scotch/issues/8
cholmodCommonObject().metis_dswitch = 0.0;
}
/** \brief Set the linear problem to solve /** \brief Set the linear problem to solve
* *
* Warning! The matrix is assumed to be symmetric! * The matrix is assumed to be symmetric! CHOLMOD uses only the
* CHOLMOD uses only the upper part of the matrix, the lower part is ignored and can be empty for optimal memory usage. * upper part of the matrix, the lower part is ignored and can be
* empty for optimal memory use.
*/ */
void setProblem(const MatrixType& matrix, void setProblem(const MatrixType& matrix,
VectorType& x, VectorType& x,
const VectorType& rhs) override const VectorType& rhs) override
{
setMatrix(matrix);
setSolutionVector(x);
setRhs(rhs);
}
/** \brief Set the matrix for the linear linear problem to solve
*
* The matrix is assumed to be symmetric! CHOLMOD uses only the
* upper part of the matrix, the lower part is ignored and can be
* empty for optimal memory usage.
*
* \warning Unlike the setMatrix method of the dune-istl Cholmod class,
* the method here does not factorize the matrix! Call the factorize
* method for that.
*/
void setMatrix(const MatrixType& matrix)
{ {
matrix_ = &matrix; matrix_ = &matrix;
x_ = &x; isFactorized_ = false;
}
/** \brief Set the rhs vector for the linear problem
*/
void setRhs(const VectorType& rhs)
{
rhs_ = &rhs; rhs_ = &rhs;
} }
virtual void solve() /** \brief Set the vector where to write the solution of the linear problem
*/
void setSolutionVector(VectorType& x)
{
x_ = &x;
}
/** \brief Compute the Cholesky decomposition of the matrix
*
* You must have set a matrix to be able to call this,
* either by calling setProblem or the appropriate constructor.
*
* The degrees of freedom to ignore must have been set before
* calling the `factorize` method.
*/
void factorize()
{
if (not this->hasIgnore())
{
// setMatrix will decompose the matrix
istlCholmodSolver_.setMatrix(*matrix_);
// get the error code from Cholmod in case something happened
errorCode_ = cholmodCommonObject().status;
}
else
{
const auto& ignore = this->ignore();
// The setMatrix method stores only the selected entries of the matrix (determined by the ignore field)
istlCholmodSolver_.setMatrix(*matrix_,&ignore);
// get the error code from Cholmod in case something happened
errorCode_ = cholmodCommonObject().status;
}
if (errorCode_ >= 0) // okay or warning, but no error
isFactorized_ = true;
}
/** \brief Solve the linear system
*
* This factorizes the matrix if it hasn't been factorized earlier
*/
virtual void solve() override
{ {
// prepare the solver // prepare the solver
Dune::InverseOperatorResult statistics; Dune::InverseOperatorResult statistics;
Dune::Cholmod<VectorType> solver;
// Bug in LibScotchMetis: // Factorize the matrix now, if the caller hasn't done so explicitly earlier.
// metis_dswitch: see cholmod.h: LibScotchMetis throws segmentation faults for large problems. if (!isFactorized_)
// This caused a hard to understand CI problems and therefore, METIS is disabled for problems factorize();
// larger than defined in metis_nswitch for now (default 3000).
// Bug report: https://gitlab.inria.fr/scotch/scotch/issues/8
solver.cholmodCommonObject().metis_dswitch = 0.0;
if (not this->hasIgnore()) if (not this->hasIgnore())
{ {
// the apply() method doesn't like constant values // the apply() method doesn't like constant values
auto mutableRhs = *rhs_; auto mutableRhs = *rhs_;
// setMatrix will decompose the matrix // The back-substitution
solver.setMatrix(*matrix_); istlCholmodSolver_.apply(*x_, mutableRhs, statistics);
// get the error code from Cholmod in case something happened
errorCode_ = solver.cholmodCommonObject().status;
solver.apply(*x_, mutableRhs, statistics);
} }
else else
{ {
const auto& ignore = this->ignore(); const auto& ignore = this->ignore();
// The setMatrix method stores only the selected entries of the matrix (determined by the ignore field)
solver.setMatrix(*matrix_,&ignore);
// get the error code from Cholmod in case something happened
errorCode_ = solver.cholmodCommonObject().status;
// create flat vectors // create flat vectors
using T = typename VectorType::field_type; using T = typename VectorType::field_type;
std::size_t flatSize = flatVectorForEach(ignore, [](...){}); std::size_t flatSize = flatVectorForEach(ignore, [](...){});
...@@ -148,10 +216,15 @@ public: ...@@ -148,10 +216,15 @@ public:
}); });
// Solve the modified system // Solve the modified system
solver.apply(*x_, modifiedRhs, statistics); istlCholmodSolver_.apply(*x_, modifiedRhs, statistics);
} }
} }
cholmod_common& cholmodCommonObject()
{
return istlCholmodSolver_.cholmodCommonObject();
}
/** \brief return the error code of the Cholmod factorize call /** \brief return the error code of the Cholmod factorize call
* *
* In setMatrix() the matrix factorization takes place and Cholmod is * In setMatrix() the matrix factorization takes place and Cholmod is
...@@ -165,6 +238,9 @@ public: ...@@ -165,6 +238,9 @@ public:
return errorCode_; return errorCode_;
} }
//! dune-istl Cholmod solver object
Dune::Cholmod<VectorType> istlCholmodSolver_;
//! Pointer to the system matrix //! Pointer to the system matrix
const MatrixType* matrix_; const MatrixType* matrix_;
...@@ -176,6 +252,13 @@ public: ...@@ -176,6 +252,13 @@ public:
//! error code of Cholmod factorize call //! error code of Cholmod factorize call
int errorCode_ = 0; int errorCode_ = 0;
/** \brief Whether istlCholmodSolver_ currently holds a factorized matrix.
*
* isFactorized==true is equivalent to istlCholmodSolver_->L_ != nullptr,
* but the latter information is private to the dune-istl Cholmod class.
*/
bool isFactorized_ = false;
}; };
} // namespace Solvers } // namespace Solvers
......
...@@ -59,7 +59,80 @@ struct CHOLMODSolverTestSuite ...@@ -59,7 +59,80 @@ struct CHOLMODSolverTestSuite
} }
}; };
// Check whether we can reuse a factorization for several right hand sides
bool checkMultipleSolves()
{
bool passed = true;
using Matrix = BCRSMatrix<double>;
using Vector = BlockVector<double>;
// Construct an example matrix
Matrix matrix(4, 4,
3, // Expected average number of nonzeros
// per row
0.2, // Size of the compression buffer,
// as a fraction of the expected total number of nonzeros
BCRSMatrix<double>::implicit);
matrix.entry(0,0) = 2;
matrix.entry(0,1) = 1;
matrix.entry(1,0) = 1;
matrix.entry(1,1) = 2;
matrix.entry(1,2) = 1;
matrix.entry(2,1) = 1;
matrix.entry(2,2) = 2;
matrix.entry(2,3) = 1;
matrix.entry(3,2) = 1;
matrix.entry(3,3) = 2;
matrix.compress();
// Set up the solver
Solvers::CholmodSolver<Matrix,Vector> cholmodSolver;
// Where to write the solution
Vector x(4);
cholmodSolver.setSolutionVector(x);
// Factorize the matrix
cholmodSolver.setMatrix(matrix);
cholmodSolver.factorize();
// Test with a particular right-hand side
Vector rhs = {3,4,4,3};
// The corresponding solution
Vector reference = {1,1,1,1};
cholmodSolver.setRhs(rhs);
cholmodSolver.solve();
// Check whether result is correct
auto diff = reference;
diff -= x;
if (diff.infinity_norm() > 1e-8)
{
std::cerr << "CholmodSolver didn't produce the correct result!" << std::endl;
passed = false;
}
// Test with a different right-hand side
// This reuses the original factorization.
rhs = {4,6,6,5};
reference = {1,2,1,2};
cholmodSolver.solve();
diff = reference;
diff -= x;
if (diff.infinity_norm() > 1e-8)
{
std::cerr << "CholmodSolver didn't produce the correct result!" << std::endl;
passed = false;
}
return passed;
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
...@@ -69,8 +142,10 @@ int main(int argc, char** argv) ...@@ -69,8 +142,10 @@ int main(int argc, char** argv)
CHOLMODSolverTestSuite<1> testsuite1; CHOLMODSolverTestSuite<1> testsuite1;
CHOLMODSolverTestSuite<2> testsuite2; CHOLMODSolverTestSuite<2> testsuite2;
passed = checkWithStandardGrids(testsuite1); passed &= checkWithStandardGrids(testsuite1);
passed = checkWithStandardGrids(testsuite2); passed &= checkWithStandardGrids(testsuite2);
passed &= checkMultipleSolves();
return passed ? 0 : 1; return passed ? 0 : 1;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment