Skip to content

Commit 44bcee0

Browse files
Merge pull request #949 from Iddingsite/CSRMatrixmode
CSR matrix and docs for PETSc extension
2 parents bc79223 + 0133e9c commit 44bcee0

6 files changed

Lines changed: 768 additions & 80 deletions

File tree

Project.toml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
2727
StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
2828

2929
[weakdeps]
30-
AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c"
31-
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
3230
AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e"
31+
AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c"
3332
BandedMatrices = "aae01518-5342-5314-be14-df237901396f"
3433
BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0"
3534
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
3635
CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e"
3736
CUSOLVERRF = "a8cc9031-bad2-4722-94f5-40deabb4245c"
37+
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
3838
CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8"
3939
Elemental = "902c3f28-d1ec-5e7e-8399-a24c3845ee38"
4040
EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869"
@@ -54,21 +54,22 @@ ParU_jll = "9e0b026c-e8ce-559c-a2c4-6a3d5c955bc9"
5454
Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2"
5555
RecursiveFactorization = "f2c3362d-daeb-58d1-803e-2bc74f2840b4"
5656
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
57+
SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1"
5758
Sparspak = "e56a9233-b9d6-4f03-8d0f-1825330902ac"
5859
blis_jll = "6136c539-28a5-5bf0-87cc-b183200dce32"
5960

6061
[extensions]
61-
LinearSolveAlgebraicMultigridExt = "AlgebraicMultigrid"
6262
LinearSolveAMDGPUExt = "AMDGPU"
63+
LinearSolveAlgebraicMultigridExt = "AlgebraicMultigrid"
6364
LinearSolveBLISExt = ["blis_jll", "LAPACK_jll"]
6465
LinearSolveBandedMatricesExt = "BandedMatrices"
6566
LinearSolveBlockDiagonalsExt = "BlockDiagonals"
6667
LinearSolveCUDAExt = "CUDA"
6768
LinearSolveCUDSSExt = "CUDSS"
6869
LinearSolveCUSOLVERRFExt = ["CUSOLVERRF", "SparseArrays"]
70+
LinearSolveChainRulesCoreExt = "ChainRulesCore"
6971
LinearSolveCliqueTreesExt = ["CliqueTrees", "SparseArrays"]
7072
LinearSolveElementalExt = "Elemental"
71-
LinearSolveChainRulesCoreExt = "ChainRulesCore"
7273
LinearSolveEnzymeExt = ["EnzymeCore", "SparseArrays"]
7374
LinearSolveFastAlmostBandedMatricesExt = "FastAlmostBandedMatrices"
7475
LinearSolveFastLapackInterfaceExt = "FastLapackInterface"
@@ -80,6 +81,7 @@ LinearSolveKernelAbstractionsExt = "KernelAbstractions"
8081
LinearSolveKrylovKitExt = "KrylovKit"
8182
LinearSolveMetalExt = "Metal"
8283
LinearSolveMooncakeExt = "Mooncake"
84+
LinearSolvePETScCSRExt = ["PETSc", "SparseArrays", "SparseMatricesCSR"]
8385
LinearSolvePETScExt = ["PETSc", "SparseArrays"]
8486
LinearSolveParUExt = ["ParU_jll", "SparseArrays"]
8587
LinearSolvePardisoExt = ["Pardiso", "SparseArrays"]
@@ -88,8 +90,8 @@ LinearSolveSparseArraysExt = "SparseArrays"
8890
LinearSolveSparspakExt = ["SparseArrays", "Sparspak"]
8991

9092
[compat]
91-
AlgebraicMultigrid = "1"
9293
AMDGPU = "1.2, 2"
94+
AlgebraicMultigrid = "1"
9395
AllocCheck = "0.2"
9496
Aqua = "0.8"
9597
ArrayInterface = "7.19"
@@ -130,7 +132,7 @@ Metal = "1.4"
130132
Mooncake = "0.5.15"
131133
MultiFloats = "2.3, 3"
132134
OpenBLAS_jll = "0.3"
133-
PETSc = "0.4.6"
135+
PETSc = "0.4.8"
134136
ParU_jll = "1"
135137
Pardiso = "1"
136138
Pkg = "1.10"
@@ -146,6 +148,7 @@ SciMLLogging = "1.7"
146148
SciMLOperators = "1.15"
147149
Setfield = "1.1.1"
148150
SparseArrays = "1.10"
151+
SparseMatricesCSR = "0.6.12"
149152
Sparspak = "0.3.9"
150153
StableRNGs = "1.0"
151154
StaticArrays = "1.9"

docs/src/solvers/solvers.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,198 @@ GinkgoJL_CG
375375
GinkgoJL_GMRES
376376
```
377377

378+
### PETSc.jl
379+
380+
!!! note
381+
382+
Using this solver requires loading PETSc.jl and MPI.jl, and initialising MPI:
383+
```julia
384+
using PETSc, MPI, SparseMatricesCSR # SparseMatricesCSR is optional but recommended for best performance
385+
MPI.Init()
386+
```
387+
388+
!!! warning "Serial only"
389+
390+
The current implementation supports only **single-process** solves (`MPI.COMM_SELF`).
391+
Passing a multi-rank communicator will raise an error. MPI-parallel support is planned
392+
for a future release.
393+
394+
[PETSc](https://petsc.org) (Portable, Extensible Toolkit for Scientific Computation) is a
395+
library for the parallel numerical solution of scientific applications. Its KSP
396+
component provides a comprehensive suite of Krylov iterative solvers paired with a large
397+
selection of preconditioners.
398+
399+
`PETScAlgorithm` wraps PETSc's KSP solvers via [PETSc.jl](https://github.com/JuliaParallel/PETSc.jl)
400+
and exposes the full preconditioner interface. It works with **dense matrices**,
401+
**`SparseMatrixCSC`**, and **`SparseMatrixCSR`** (from
402+
[SparseMatricesCSR.jl](https://github.com/gridap/SparseMatricesCSR.jl)).
403+
404+
**When to choose PETSc over the pure-Julia Krylov solvers:**
405+
- You want to test a wide variety of Krylov methods and preconditioners without needing to add multiple Julia packages.
406+
- You want direct access to PETSc's Options Database to fine-tune solver behavior at runtime
407+
without recompiling.
408+
409+
#### Solver type
410+
411+
The first positional argument selects the KSP algorithm. Any string accepted by
412+
[`KSPSetType`](https://petsc.org/release/manualpages/KSP/KSPSetType/) can be passed as a `Symbol`.
413+
The most commonly used options are:
414+
415+
| Symbol | Method | Notes |
416+
| :--- | :--- | :--- |
417+
| `:gmres` (default) | GMRES | General non-symmetric systems |
418+
| `:fgmres` | Flexible GMRES | Allows variable preconditioner |
419+
| `:lgmres` | LGMRES | Augmented GMRES, better for restarting |
420+
| `:cg` | Conjugate Gradient | SPD systems only |
421+
| `:fcg` | Flexible CG | CG with variable preconditioner |
422+
| `:minres` | MINRES | Symmetric indefinite systems |
423+
| `:symmlq` | SYMMLQ | Symmetric indefinite systems |
424+
| `:bcgs` | BiCGStab | Non-symmetric, more stable than BiCG |
425+
| `:fbcgs` | Flexible BiCGStab | BiCGStab with variable preconditioner |
426+
| `:bcgsl` | BiCGStab(ℓ) | Stabilised BiCGStab variant |
427+
| `:bicg` | BiConjugate Gradient | Non-symmetric |
428+
| `:cgs` | CGS | Non-symmetric, faster but less stable |
429+
| `:tfqmr` | TFQMR | Transpose-free QMR |
430+
| `:tcqmr` | TCQMR | Transpose-free QMR variant |
431+
| `:cr` | Conjugate Residuals | Symmetric systems |
432+
| `:gcr` | GCR | Generalized CR, flexible preconditioner |
433+
| `:chebyshev` | Chebyshev iteration | Requires eigenvalue bounds; good for smoothing |
434+
| `:richardson` | Richardson iteration | Stationary; mainly used as smoother |
435+
| `:lsqr` | LSQR | Least-squares problems |
436+
| `:cgls` | CGLS | Least-squares problems |
437+
| `:preonly` | Preconditioner only | Use with `:lu` for a direct solve |
438+
| `:none` | No solver | Identity; useful for debugging |
439+
440+
#### Preconditioners
441+
442+
Preconditioners are selected via the `pc_type` keyword. Any string accepted by
443+
[`PCSetType`](https://petsc.org/release/manualpages/PC/PCSetType/) can be passed as a `Symbol`.
444+
The most commonly used options are:
445+
446+
| Symbol | Preconditioner | Notes |
447+
| :--- | :--- | :--- |
448+
| `:none` (default) | No preconditioner | Useful for well-conditioned problems |
449+
| `:jacobi` | Diagonal (Jacobi) scaling | Cheap; good for diagonally dominant systems |
450+
| `:pbjacobi` | Point Block Jacobi | Fixed-size dense blocks along the diagonal |
451+
| `:sor` | SOR / Gauss-Seidel | Successive over-relaxation |
452+
| `:eisenstat` | Eisenstat SSOR | Symmetric SOR; cheaper than a full SSOR sweep |
453+
| `:ilu` | Incomplete LU | General sparse systems |
454+
| `:icc` | Incomplete Cholesky | SPD systems; symmetric analogue of ILU |
455+
| `:lu` | Exact LU (direct) | Use with `:preonly` for a direct solve |
456+
| `:cholesky` | Exact Cholesky (direct) | SPD systems; use with `:preonly` |
457+
| `:bjacobi` | Block Jacobi | Applies an independent ILU/LU solve per block |
458+
| `:asm` | Additive Schwarz | Overlapping domain decomposition |
459+
| `:gasm` | Generalized Additive Schwarz | Multi-level ASM variant |
460+
| `:gamg` | Algebraic Multigrid (GAMG) | No hierarchy needed; good for PDEs |
461+
| `:hypre` | Hypre BoomerAMG | Excellent AMG for large ill-conditioned systems |
462+
| `:kaczmarz` | Kaczmarz | Row-projection smoother |
463+
464+
A separate matrix for building the preconditioner can be supplied via `prec_matrix`:
465+
466+
```julia
467+
PETScAlgorithm(:gmres; prec_matrix = P)
468+
```
469+
470+
#### Matrix format recommendations
471+
472+
PETSc operates internally on 0-based CSR arrays. The recommended matrix format is
473+
**`SparseMatrixCSR{0}`** (from SparseMatricesCSR.jl), which matches PETSc's native layout
474+
exactly:
475+
476+
- **`SparseMatrixCSR{0}`***fastest*: zero-copy path on construction; direct `copyto!`
477+
on value-only updates.
478+
- **`SparseMatrixCSR{1}`** — slightly slower than `{0}` on construction (index shift on
479+
cold start), same fast value-update path.
480+
- **`SparseMatrixCSC`** — supported, but requires a CSC→CSR permutation and scatter on
481+
every value update.
482+
- **Dense `Matrix`** — supported via `MatSeqDense`; works out of the box.
483+
484+
```julia
485+
using SparseMatricesCSR, SparseArrays
486+
487+
A_csc = spdiagm(-1 => -ones(n-1), 0 => 2ones(n), 1 => -ones(n-1))
488+
489+
# Recommended: one-liner to build SparseMatrixCSR{0} from a CSC matrix.
490+
# Note: this mutates A_csc's internal storage (colptr/rowvals are shifted in-place).
491+
# Use a copy if you need to keep A_csc usable afterwards.
492+
A = SparseMatrixCSR{0}(transpose(sparse(transpose(A_csc))))
493+
```
494+
495+
#### Basic usage
496+
497+
```julia
498+
using LinearSolve, PETSc, MPI, SparseArrays, LinearAlgebra
499+
MPI.Init()
500+
501+
n = 200
502+
A = sprand(n, n, 0.05); A = A + A' + 20I
503+
b = rand(n)
504+
505+
# Simple one-shot solve with ILU preconditioner
506+
sol = solve(LinearProblem(A, b), PETScAlgorithm(:gmres; pc_type = :ilu))
507+
@show norm(A * sol.u - b) / norm(b)
508+
```
509+
510+
#### Repeated solves (same sparsity pattern, values change)
511+
512+
When the sparsity pattern is fixed across calls,
513+
the KSP is reused and only the matrix values are updated.
514+
515+
```julia
516+
using LinearSolve, PETSc, MPI, SparseArrays, SparseMatricesCSR, LinearAlgebra
517+
import SciMLBase
518+
MPI.Init()
519+
520+
n = 200
521+
A_csc = sprand(n, n, 0.05); A_csc = A_csc + A_csc' + 20I
522+
b = rand(n)
523+
524+
# Convert to SparseMatrixCSR{0} once — getrowptr/getcolval require a CSR matrix
525+
A = SparseMatrixCSR{0}(transpose(sparse(transpose(A_csc))))
526+
527+
cache = SciMLBase.init(LinearProblem(A, b), PETScAlgorithm(:gmres; pc_type = :ilu))
528+
solve!(cache)
529+
530+
# Extract the fixed sparsity structure once (0-based row pointers and column indices)
531+
rowptr0 = copy(SparseMatricesCSR.getrowptr(A))
532+
colval0 = copy(SparseMatricesCSR.getcolval(A))
533+
534+
# Iterate: only nzval changes, sparsity pattern is fixed
535+
for t in 1:10
536+
new_vals = A.nzval .* (1 + 0.1 * t) # e.g. time-varying coefficients
537+
A_new = SparseMatrixCSR{0}(n, n, rowptr0, colval0, new_vals)
538+
SciMLBase.reinit!(cache; A = A_new, b = rand(n))
539+
solve!(cache)
540+
end
541+
```
542+
543+
#### Extra PETSc options
544+
545+
Any PETSc Options Database key can be forwarded via `ksp_options`:
546+
547+
```julia
548+
PETScAlgorithm(:gmres;
549+
pc_type = :ilu,
550+
ksp_options = (ksp_monitor = "", ksp_rtol = 1e-12, pc_factor_levels = 2))
551+
```
552+
553+
#### Memory management
554+
555+
PETSc objects live in C-managed memory outside Julia's GC. Call
556+
`cleanup_petsc_cache!` explicitly when finished to release resources promptly:
557+
558+
```julia
559+
PETScExt = Base.get_extension(LinearSolve, :LinearSolvePETScExt)
560+
PETScExt.cleanup_petsc_cache!(sol) # after solve(...)
561+
PETScExt.cleanup_petsc_cache!(cache) # after init/solve! cycle
562+
```
563+
564+
A GC finalizer is registered as a safety net, but explicit cleanup is strongly preferred.
565+
566+
```@docs
567+
PETScAlgorithm
568+
```
569+
378570
### LinearSolvePyAMG.jl
379571

380572
!!! note

0 commit comments

Comments
 (0)