Linear solvers (LS)
If you provide your own linear solver, it must be a subtype of
AbstractLinearSolverotherwiseBifurcationKit.jlwill not recognize it. See example just below.
The linear solvers provide a way of inverting the Jacobian J, i.e. of solving $J\, x = \mathrm{rhs}$. Throughout the package, such a linear solver linsolve will be called like
sol, success, itnumber = linsolve(J, rhs; kwargs...)The three returned quantities are the solution sol, a boolean success telling whether the solve succeeded (resp. converged for iterative solvers) and itnumber the number of iterations required by the computation (equal to 1 for a direct solver).
Here is an example of the simplest one (see src/LinearSolver.jl for the true implementation) to give you an idea, the backslash operator:
struct DefaultLS <: AbstractLinearSolver endfunction (l::DefaultLS)(J, rhs; k...) return J \ rhs, true, 1endYou can then call it as follows (and it will be called like this in newton):
ls = DefaultLS()J = rand(2, 2) # example of linear operatorls(J, rand(2))All linear solvers also accept two right hand sides: linsolve(J, rhs1, rhs2; kwargs...) returns (sol1, sol2, success, itnumber) with itnumber a tuple. This interface is used internally, e.g. by the bordered solvers of Bordered linear solvers (BLS), and it is where a direct solver can reuse a factorization for the second solve.
The linear solvers can also be called with the keyword arguments aโ and aโ to solve the shifted system $(a_0 I + a_1 J)\, x = \mathrm{rhs}$. Their defaults are aโ = 0 and aโ = 1, i.e. one recovers $J\,x=\mathrm{rhs}$. This interface is used internally by the algorithms based on the Hopf problem, on Floquet multipliers or by the bordered solvers. If you provide your own linear solver, you can ignore these keywords as long as you do not need these features.
List of implemented linear solvers
The detailed information for each one of them is located in the API.
DefaultLSโ direct solver based on the default\operator, which dispatches toLU,Choleskyor a sparse factorization depending on the type of the Jacobian. It works for dense and sparse matrices. The optionuseFactorization = true(the default) caches the factorization so that a second linear solve with the same operator is very cheap; set it tofalsewhen your operator does not supportfactorize(e.g. some operators coming fromApproxFun.jl, or matrices such asStaticArrays.MMatrix). You can create one vialinsolver = DefaultLS().GMRESIterativeSolversโ iterative solver based ongmresfrom IterativeSolvers.jl. You can create one vialinsolver = GMRESIterativeSolvers()and tune it with the keyword argumentsabstol,reltol,restart,maxiter,N,verbose, ... It supports Matrix-Free Jacobians (withNthe dimension of the state space) and left/right preconditioners (fieldsPl,Pr). The struct is mutable so that you can change the preconditioners on the fly.GMRESKrylovKitโ iterative solver based onlinsolvefrom KrylovKit.jl. You can create one vialinsolver = GMRESKrylovKit()and tune it with the keyword argumentsdim,atol,rtol,maxiter,verbose, ...!!! tip "Different linear solvers" By tuning the options of
GMRESKrylovKit, you can select CG, GMRES... Indeed,KrylovKit.jldispatches on the optionsissymmetric,ishermitian,isposdef, see KrylovKit.jl. A left preconditioner can be set through the fieldPl.KrylovLS/KrylovLSInplaceโ interfaces to the many solvers of Krylov.jl. You can create one vialinsolver = KrylovLS()and pass the required Krylov method through the keywordKrylovAlg = :gmres(the default): you have access tocg,cr,gmres,symmlq,minres,cg_lanczos,cg_lanczos_shift_seq, ... The extra keyword arguments are forwarded to the chosen Krylov method, e.g.KrylovLS(atol = 1e-11, rtol = 1e-8). Left/right preconditioners are passed through the keywordsPl,Pr(see the Preconditioner section below).KrylovLSInplacepre-allocates the Krylov space (you then have to provide the workspace dimensions, see the docstring) which is very useful on the GPU or when many linear solves are performed. Both structs are mutable so that you can modify the preconditioners.!!! note "Other solvers" Thanks to the interface to
Krylov.jl, you do not need to implement the Conjugate Gradients from IterativeSolvers.jl,minres, ... yourself: just select the algorithm with the keywordKrylovAlgas explained above.
Preconditioner
Preconditioners should be considered when using Matrix Free methods such as GMRES. GMRESIterativeSolvers provides a very simple interface for using them (fields Pl, Pr). For GMRESKrylovKit, we implemented a left preconditioner (field Pl). Note that, for GMRESKrylovKit, you are not restricted to use Vectors anymore.
A curated list is provided by the package LinearSolve.jl.
Finally, here are some packages to use preconditioners:
Krylov.jlprovides some preconditioners.- IncompleteLU.jl an ILU like preconditioner
- AlgebraicMultigrid.jl Algebraic Multigrid (AMG) preconditioners. This works especially well for symmetric positive definite matrices.
- Preconditioners.jl A convenient interface to conveniently called most of the above preconditioners using a single syntax.
- We provide a preconditioner based on deflation of eigenvalues (also called preconditioner based on Leading Invariant Subspaces) using a partial Schur decomposition. There are two ways to define one i.e.
PrecPartialSchurKrylovKitandPrecPartialSchurArnoldiMethod.
Apart from setting a preconditioner for a linear solver, it can be advantageous to change the preconditioner during computations, e.g. during a call to continuation or newton. This can be achieved by taking advantage of the callbacks to these methods. See the example 2d Ginzburg-Landau equation (finite differences, codim 2, Hopf aBS).