Conversation
There was a problem hiding this comment.
Pull request overview
This PR extends ReSolve’s GMRES implementations to support left preconditioning (M^{-1}Ax = M^{-1}b), adds a user-supplied explicit preconditioner matrix via PreconditionerUserMatrix, and wires the feature through examples/tests and CLI (-p left|right).
Changes:
- Add
Preconditioner::getSide()/setSide()and plumb side selection throughSystemSolver::preconditionerSetup(...). - Implement left-preconditioned variants of the GMRES/Randomized GMRES iteration logic (with fallback to right preconditioning for flexible GMRES).
- Add unit/functionality tests and example updates for left preconditioning and the new user-matrix preconditioner.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
resolve/Preconditioner.hpp |
Adds preconditioning side API (left/right). |
resolve/Preconditioner.cpp |
Implements getSide() / setSide(). |
resolve/SystemSolver.hpp |
Extends preconditioner setup signature and exposes getPreconditioner(). |
resolve/SystemSolver.cpp |
Applies preconditioner setup with chosen side; exposes preconditioner getter. |
resolve/PreconditionerUserMatrix.hpp |
Declares user-provided explicit preconditioner matrix wrapper. |
resolve/PreconditionerUserMatrix.cpp |
Implements user-matrix preconditioner via MatrixHandler::matvec. |
resolve/LinSolverIterativeFGMRES.hpp/.cpp |
Implements left-preconditioning behavior (non-flexible GMRES) and warns/falls back for flexible GMRES. |
resolve/LinSolverIterativeRandFGMRES.hpp/.cpp |
Implements left-preconditioning behavior in randomized GMRES variant. |
resolve/PreconditionerLU.hpp |
Updates file header comment. |
resolve/PreconditionerLU.cpp |
Updates documentation strings/comments for LU preconditioner. |
resolve/CMakeLists.txt |
Adds new preconditioner source/header to build/install. |
tests/unit/preconditioner/* |
Adds LU and user-matrix preconditioner unit tests and CMake integration. |
tests/unit/CMakeLists.txt |
Adds preconditioner unit test subdirectory. |
tests/functionality/testSysGmres.cpp |
Adds -p parsing and conditional residual checking for left preconditioning. |
tests/functionality/CMakeLists.txt |
Adds functionality tests covering left-preconditioned runs. |
examples/sysGmres.cpp |
Adds -p parsing and passes side into preconditionerSetup. |
examples/randGmres.cpp |
Refactors preconditioner setup to use PreconditionerLU consistently. |
CHANGELOG.md |
Adds entry documenting left preconditioning + user-matrix preconditioner. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| matrix_handler_->matvec(B_, rhs, x, &ONE, &ZERO, memspace_); | ||
| return 0; | ||
| } | ||
|
|
||
| void PreconditionerUserMatrix::setMemorySpace() | ||
| { | ||
| bool is_matrix_handler_cuda = matrix_handler_->getIsCudaEnabled(); | ||
| bool is_matrix_handler_hip = matrix_handler_->getIsHipEnabled(); | ||
|
|
There was a problem hiding this comment.
PreconditionerUserMatrix::apply() unconditionally calls MatrixHandler::matvec() and then returns 0. If B_ or matrix_handler_ is null this will crash, and any nonzero status from matvec is currently ignored. Please add null checks (e.g., require setPrecMatrix() to have been called) and propagate/return the matvec status so callers can detect failures.
| matrix_handler_->matvec(B_, rhs, x, &ONE, &ZERO, memspace_); | |
| return 0; | |
| } | |
| void PreconditionerUserMatrix::setMemorySpace() | |
| { | |
| bool is_matrix_handler_cuda = matrix_handler_->getIsCudaEnabled(); | |
| bool is_matrix_handler_hip = matrix_handler_->getIsHipEnabled(); | |
| // Ensure that the matrix handler and preconditioner matrix have been set | |
| if (matrix_handler_ == nullptr || B_ == nullptr) | |
| { | |
| // Nonzero return value indicates failure (e.g., missing setup) | |
| return 1; | |
| } | |
| int status = matrix_handler_->matvec(B_, rhs, x, &ONE, &ZERO, memspace_); | |
| return status; | |
| } | |
| void PreconditionerUserMatrix::setMemorySpace() | |
| { | |
| bool is_matrix_handler_cuda = false; | |
| bool is_matrix_handler_hip = false; | |
| if (matrix_handler_ != nullptr) | |
| { | |
| is_matrix_handler_cuda = matrix_handler_->getIsCudaEnabled(); | |
| is_matrix_handler_hip = matrix_handler_->getIsHipEnabled(); | |
| } |
|
Thank you for making the unit tests for the preconditioners! I have code that implements regularization in Eigen that needs to be translated that would be better for testTripsProblem. I think these tests work better for testing just the preconditioners. |
Sure, let me first finish this PR and I'll take a look at your implementation . |
|
@kakeueda I believe Dr Peles does not want to use Eigen as a dependency. Right now I only use Eigen to calculate the SVD of the Hessenberg Matrix for GMRES but I believe there are alternatives like LAPACK that can also do this. |
Hessenberg matrix is dense, so SVD from LAPACK should work in this case. I wouldn't add Eigen as a dependency unless there is compelling enough reason to do so. |
Co-authored-by: Kakeru Ueda <kakecoco2580@icloud.com>
Co-authored-by: Kakeru Ueda <kakecoco2580@icloud.com>
Co-authored-by: Kakeru Ueda <kakecoco2580@icloud.com>
Fixed some small bugs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
e866f59 to
4d36a83
Compare
|
For now the default behavior for left preconditioning is to use the preconditioned residual |
|
This is outside the scope of this PR but 'solve()' in GMRES is starting to get a bit cluttered. It might be worth considering adding a base GMRES class and having the current RandFGMRES and regularized GMRES derive from it. We could also clean up the convergence check for GMRES which I believe this was mentioned before. |
| true_rnorm = vector_handler_->dot(vec_V_, vec_V_, memspace_); | ||
| true_rnorm = std::sqrt(true_rnorm); |
There was a problem hiding this comment.
maybe we should change this to true_res_norm? r can be anything, it's not clear from context (and there's no separator from norm)
There was a problem hiding this comment.
Agree, also changed bnorm to rhs_norm
| /** | ||
| * @brief Test apply() performs correct matvec with B. | ||
| * | ||
| * B = diag(2, 3, 4), x = [1, 1, 1]^T => y = [2, 3, 4]^T. |
There was a problem hiding this comment.
It was meant to show a column vector, but maybe unnecessary
There was a problem hiding this comment.
Worse, it's misleading. Vectors are by default column, unless you put a transpose. That's what didn't make sense to me. I knew it had to be a column, but this is the convention for row.
Co-authored-by: Shaked Regev <35384901+shakedregev@users.noreply.github.com>
shakedregev
left a comment
There was a problem hiding this comment.
Some minor comments. I need to test and I will change this to approve.
|
returned 503: Egress is over the account limit. I got this error in the test. Any idea what might be causing this? @pelesh |
Looks like a temporary failure in GH-Actions. It's working now! |
Description
This change allows users to solve a left-preconditioned system$M^{-1}Ax = M^{-1}b$ using GMRES. It also adds a PreconditionerUserMatrix class.
@JeffZ594 My understanding is that we eventually want to add regularization to the GMRES solvers. We can follow up with another PR that adds regularization support with testTripsProblem.
Proposed changes
-p left (or right)for the GMRES solver (default: right).Checklist
make testandmake test_installper testing instructions). Code tested on./examples/<your_example>.exe -hto get instructions how to run examples). Code tested on:-Wall -Wpedantic -Wconversion -Wextra.Further comments
Left preconditioning for FGMRES is more involved, so for now non-flexible GMRES only. If FGMRES is selected with
-p left, the solver reports an error.