Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add chebyshev Iteration #1289

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
Open

Add chebyshev Iteration #1289

wants to merge 13 commits into from

Conversation

yhmtsai
Copy link
Member

@yhmtsai yhmtsai commented Feb 22, 2023

It adds the Chebshev iteration in https://en.wikipedia.org/wiki/Chebyshev_iteration
The second-order richardson uses a similar formula but the scalars are constant in all iteration. Chebyshev Iteration update the scalar from the previous one (the scalar are the same if upper/lower eigval does not change)

@yhmtsai yhmtsai added the 1:ST:WIP This PR is a work in progress. Not ready for review. label Feb 22, 2023
@yhmtsai yhmtsai self-assigned this Feb 22, 2023
@ginkgo-bot ginkgo-bot added reg:build This is related to the build system. reg:testing This is related to testing. mod:core This is related to the core module. mod:reference This is related to the reference module. type:solver This is related to the solvers labels Feb 22, 2023
@MarcelKoch
Copy link
Member

MarcelKoch commented Mar 14, 2023

Do you intend adding an eigenvalue estimation? I think that would be very helpful, because most times users don't have that available. I think PETSc is also doing that.
I briefly searched for that and here are some references that might be interesting for that:

I think this is the GMRES version used by PETSC to compute the estimate: https://petsc.org/release/docs/manualpages/KSP/KSPAGMRES/

@yhmtsai
Copy link
Member Author

yhmtsai commented Mar 20, 2023

@MarcelKoch thanks for providing these reference.

I do not think I will put them in this pull request.

From https://doi.org/10.1137/0907057, they introduce the algorithm
adaptive chebyshev iteration
Perform m-step GMRES and use the information from the Hessenberg matrix to get the estimation of eigenvalue (I may be wrong),
and then perform https://link.springer.com/article/10.1007/BF01389971 to get the parameter for chebchev
Repeat these two steps until converge
This will be more like a standalone solver not a smoother for multigrid because it will require several steps on GMRES, which is too expensive IMO.
We can introduce by with_adaptive(GMRES LinOpFactory)

From https://petsc.org/release/docs/manualpages/KSP/KSPCHEBYSHEV/#kspchebyshev,
More precisely, https://petsc.org/release/docs/manualpages/KSP/KSPChebyshevEstEigSet/
They use Lanczo or GMRES to get the maximum estimation to decide the parameter by following
min-eig-boundary = a * min-eigest + b * max-eigest = 0.1 max-eigest (default), and
max-eig-boundary = c * min-eigest + d * max-eigest = 1.1 max-eigest (default)
(because min_eigest is usually inaccurate)
By this, user can use the information by GMRES and generate min/max eig boundary for Chebyshev, so we do not need additional parameter?
It only does once in generation, so this is more usable for Multigrid

For GMRES, we need to get the Hessenberg out and compute eigenvalue on it.
GMRES eigenvalue and Lanczo are related to #135

@yhmtsai yhmtsai added 1:ST:ready-for-review This PR is ready for review and removed 1:ST:WIP This PR is a work in progress. Not ready for review. labels Mar 21, 2023
@sonarcloud
Copy link

sonarcloud bot commented Mar 21, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 8 Code Smells

44.3% 44.3% Coverage
7.1% 7.1% Duplication

Copy link
Member

@MarcelKoch MarcelKoch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the comments left below, I want to mention the num_keep and num_generated mechanic. That needs some explaination, because right now I don't understand what that is for. It seems like some sort of restart mechanic, but I might be wrong there. Also, exposing this to the user seems confusing to me.

Comment on lines 185 to 188
/**
* The number of scalar to keep
*/
int GKO_FACTORY_PARAMETER_SCALAR(num_keep, 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear what this parameter is for.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this means, construct Chebyshev polynomials until degree num_keep, and after that just do IR with these polynomial?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, num_keep is to keep the generated scalar in the storage.
alpha and beta are changed by iteration, but they are fixed by the given bound.
It's used for fixed iteration runs because we do not need to refill these scalars for different apply

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But shouldn't we then just keep all scalars?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when using it as normal solver, we may not have the maximum iteration information.
allocating one big dense matrix and then moving them to another one when full may not be a good approach.
we can also add the ability of workspace such that it can handle the std::vector then it should be more flexible for uncertain size.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can not assume that there's an iteration criterion. It can contain the residual norm or time criterion.
If we assume that, we should provide the standalone iteration parameter.
Users do not need to know the implementation details.
The statement is to keep how many scalars for chebyshev to avoid the refill overhead.
I can introduce this usage later when we have the use case.
I think it should help the situation when kernel launch overhead is noticeable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, this is very useful to negate the overhead. Still, I think we can assume that there is an iteration criterion somewhere in the combined one and use that. If not, we would just throw.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could set the default value to unspecified, which you define before. When the solver is generated, you can check if the parameter is unspecified and then try to extract an iteration criterion from the criteria. If that doesn't work, you throw with a message to either pass an iteration criterion or set this parameter to something else.
Or alternatively, just replace the criteria parameter with a iterations parameter and move the class into the preconditioner namespace. I think that is also a fine option, since that would be the main use case anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved it to based on the given iteration from stopping criterion.
It is only increased after creating object.
I also think whether staying a fixed number is enough or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I think it should be fine the way it is now.

/**
* Chebyshev iteration is an iterative method that uses another coarse
* method to approximate the error of the current solution via the current
* residual. It has another term for the difference of solution. Moreover, this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* residual. It has another term for the difference of solution. Moreover, this
* residual. The solution is then updated using the Chebyshev polynomials. Moreover, this

Is that what the sentence is trying to say? Anyway, I think it should be mentioned somewhere that this uses these polynomials.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the solution x_i is also based on the $x_{i-1} - x_{i-2}$

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I don't see the relevance of that. Has that any effect for the user?

Copy link
Member Author

@yhmtsai yhmtsai Aug 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, I only try to explain the algorithm's difference from IR. IR uses $x += \alpha M^{-1}r$, but chebyshev uses $x += \alpha_i M^{-1} r + \beta_i (x_{i-1} - x_{i-2})$ $(x_{i-1} - x_{i-2})$ is the additional term against IR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this has to be rephrased

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try to rephrase it again. Could you take a look?

include/ginkgo/core/solver/chebyshev.hpp Show resolved Hide resolved
include/ginkgo/core/solver/chebyshev.hpp Show resolved Hide resolved
core/solver/chebyshev.cpp Show resolved Hide resolved
include/ginkgo/core/solver/chebyshev.hpp Show resolved Hide resolved
reference/test/solver/chebyshev_kernels.cpp Outdated Show resolved Hide resolved
core/test/solver/chebyshev.cpp Outdated Show resolved Hide resolved
core/test/solver/chebyshev.cpp Outdated Show resolved Hide resolved
core/test/solver/chebyshev.cpp Show resolved Hide resolved
@upsj upsj requested a review from vasilisge0 April 6, 2023 15:51
Copy link
Contributor

@vasilisge0 vasilisge0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with merging this PR when corrections in parts of the documentation that are pointed out (comments) take place. I could understand what num_keep variable was used for but perhaps a more descriptive name would be better.

@yhmtsai yhmtsai force-pushed the cheb_iter branch 3 times, most recently from c88ea63 to f9d0e28 Compare August 8, 2023 10:06
@sonarcloud
Copy link

sonarcloud bot commented Aug 8, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 21 Code Smells

42.6% 42.6% Coverage
4.4% 4.4% Duplication

warning The version of Java (11.0.3) you have used to run this analysis is deprecated and we will stop accepting it soon. Please update to at least Java 17.
Read more here

Copy link
Member

@MarcelKoch MarcelKoch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but I would like to resolve some of my earlier issues. Other than that only minor nits.

core/solver/chebyshev.cpp Outdated Show resolved Hide resolved
/**
* Chebyshev iteration is an iterative method that uses another coarse
* method to approximate the error of the current solution via the current
* residual. It has another term for the difference of solution. Moreover, this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this has to be rephrased

include/ginkgo/core/solver/chebyshev.hpp Outdated Show resolved Hide resolved
include/ginkgo/core/solver/chebyshev.hpp Outdated Show resolved Hide resolved
include/ginkgo/core/solver/chebyshev.hpp Outdated Show resolved Hide resolved
core/test/solver/chebyshev.cpp Show resolved Hide resolved
core/test/solver/chebyshev.cpp Outdated Show resolved Hide resolved
core/test/solver/chebyshev.cpp Show resolved Hide resolved
reference/test/solver/chebyshev_kernels.cpp Show resolved Hide resolved
reference/test/solver/chebyshev_kernels.cpp Show resolved Hide resolved
@sonarcloud
Copy link

sonarcloud bot commented Oct 19, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot E 1 Security Hotspot
Code Smell A 27 Code Smells

59.4% 59.4% Coverage
5.2% 5.2% Duplication

warning The version of Java (11.0.3) you have used to run this analysis is deprecated and we will stop accepting it soon. Please update to at least Java 17.
Read more here

include/ginkgo/core/solver/chebyshev.hpp Show resolved Hide resolved
include/ginkgo/core/solver/chebyshev.hpp Outdated Show resolved Hide resolved
core/test/solver/chebyshev.cpp Show resolved Hide resolved
Copy link

sonarcloud bot commented Jan 11, 2024

Quality Gate Passed Quality Gate passed

The SonarCloud Quality Gate passed, but some issues were introduced.

27 New issues
1 Security Hotspot
59.4% Coverage on New Code
5.7% Duplication on New Code

See analysis details on SonarCloud

Copy link

sonarcloud bot commented Jul 1, 2024

Copy link
Member

@upsj upsj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few questions around data recycling and mutability I would like to discuss before merging this

Comment on lines +29 to +36
* Chebyshev iteration is an iterative method that can solving nonsymeetric
* problems. Chebyshev Iterations avoids the inner products for computation
* which may be the bottleneck for distributed system. Chebyshev Iteration is
* developed based on the Chebyshev polynomials of the first kind. Moreover,
* this method requires knowledge about the spectrum of the preconditioned
* matrix. This implementation follows the algorithm in "Templates for the
* Solution of Linear Systems: Building Blocks for Iterative Methods, 2nd
* Edition".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rearrange this slightly (the fact that we need to know the spectrum is important, so we should mention it early), and try to be consistent with capitalization of iteration.

Suggested change
* Chebyshev iteration is an iterative method that can solving nonsymeetric
* problems. Chebyshev Iterations avoids the inner products for computation
* which may be the bottleneck for distributed system. Chebyshev Iteration is
* developed based on the Chebyshev polynomials of the first kind. Moreover,
* this method requires knowledge about the spectrum of the preconditioned
* matrix. This implementation follows the algorithm in "Templates for the
* Solution of Linear Systems: Building Blocks for Iterative Methods, 2nd
* Edition".
* Chebyshev iteration is an iterative method for solving nonsymmeetric
* problems based on some knowledge of the spectrum of the (preconditioned) system matrix. It avoids the computation of inner products,
* which may be a performance bottleneck for distributed system. Chebyshev iteration is
* developed based on Chebyshev polynomials of the first kind.
* This implementation follows the algorithm in "Templates for the
* Solution of Linear Systems: Building Blocks for Iterative Methods, 2nd
* Edition".

Comment on lines +187 to +190
mutable size_type num_generated_scalar_ = 0;
// num_max_generation_ is the number of generated scalar kept in the
// workspace.
mutable size_type num_max_generation_ = 3;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit wary of mutable members beyond well-controlled cases like the workspace. They might make future work on thread-safety more complicated. Not that I have a good alternative for it, but I want to bring it up.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't wokrspace handled in similar way?

Copy link
Member

@upsj upsj Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have any mutability in our LinOps right now beyond vector caches and workspaces, most of the mutable uses are in loggers etc. So the mutability is constrained into a single class. That's not the case here.

In general, if I write code, I expect a const object to not change internally, and the only reasons we don't follow this requirement fully right now seems to be the need for cache data structures to avoid reallocation, and the fact that loggers are const and can't change themselves without mutable.

auto chebyshev_factory =
gko::solver::Chebyshev<value_type>::build()
.with_criteria(
gko::stop::Iteration::build().with_max_iters(2u).on(ref))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this entire file can use deferred factories

return SimpleSolverTest<gko::solver::Chebyshev<solver_value_type>>::
build(exec, iteration_count, check_residual)
.with_preconditioner(
precond_type::build().with_max_block_size(1u).on(exec));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deferred?



namespace gko {
namespace solver {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming solver is a bit of a stretch here - maybe even more so than IR. It makes sense for us that solvers are iterative algorithms that use a preconditioner, but from the user perspective, it's confusing. Maybe we need a concept in-between, like smoother, but that could also include classical preconditioners like Gauß-Seidel

}


TYPED_TEST(Chebyshev, SolvesTriangularSystemUsingAdvancedApplyMixed)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually mixed precision? Doesn't look like it to me on a first glance

}


TYPED_TEST(Chebyshev, SolvesTriangularSystemUsingAdvancedApplyMixedComplex)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this mixed precision?

this->template log<log::Logger::iteration_complete>(
this, dense_b, dense_x, iter, residual_ptr, nullptr, nullptr,
solver, dense_b, dense_x, iter, residual_ptr, nullptr, nullptr,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here solver == this, so I would suggest removing the capture

auto old_num_max_generation = num_max_generation_;
// Use the scalar first
// get the iteration information from stopping criterion.
visit_criteria(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a separate parameter, not derived from the stopping criterion. The stopping criteria are not really intended to be queried this way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was. but @MarcelKoch suggested that getting it from stopping criterion to reduce the parameter, I think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we put it as a separate parameter, then we should not allow for a stopping criteria parameter. This would be fine with me, but I think it causes other issues, as this can't be considered a solver anymore.

Copy link
Member

@upsj upsj Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The smoother doesn't have much purpose with norm-based stopping criteria, because that would negate the benefit of avoiding reductions, right? And can it run correctly when it uses more iterations than the number of stored scalar values? If yes, then we could have two separate parameters iterations and subspace_dimension (or similar), if no, we could have just a single parameter iterations, and internally create an Iteration stopping criterion

Comment on lines +270 to +275
if (num_generated_scalar_ < num_max_generation_) {
alpha_scalar->fill(alpha_ref);
// unused beta for first iteration, but fill zero
beta_scalar->fill(zero<ValueType>());
num_generated_scalar_++;
}
Copy link
Member

@upsj upsj Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I understanding correctly that this us potentially re-using alpha and beta values from previous iterations? If so, that seems like it is using the workspace it was not originally intended for - it's intended for avoiding reallocation, not recycling values from previous iterations. That also relates to the mutable members I would prefer to avoid.

@yhmtsai
Copy link
Member Author

yhmtsai commented Nov 29, 2024

@upsj To avoid the mutable and reusing value from workspace, I think moving them into generatio to pre-fill all alpha and beta before apply. I think it will also give better performance because we do not need to do some tiny fill before the limitation during apply.
Also, refill it when the foci or criterion change (or specfic factor for that).

@upsj
Copy link
Member

upsj commented Nov 29, 2024

If those are just static coefficients independent of the matrix, that sounds very reasonable. I tend to agree with what I believe @MarcelKoch said, which is that we shouldn't consider changeable stopping criteria here. We also currently don't seem to have a way to propagate information from other parts of the solver hierarchy to compute appropriate focus values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1:ST:ready-for-review This PR is ready for review 1:ST:run-full-test mod:core This is related to the core module. mod:reference This is related to the reference module. reg:build This is related to the build system. reg:testing This is related to testing. type:solver This is related to the solvers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants