Skip to content

Commit 4c8d145

Browse files
Add comprehensive docstrings for core LinearSolve types and functions
- Document LinearCache struct with detailed field descriptions - Add docstrings for utility functions: init_cacheval, default_tol, default_alias_* - Document preconditioner types: ComposePreconditioner, InvPreconditioner - Add comprehensive docs for LinearSolveFunction and DirectLdiv! solve functions - Improve RFLUFactorization docs with parameter and performance details - Enhance FastLUFactorization and FastQRFactorization documentation - Document SimpleLUFactorization and LUSolver with usage examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent c2f135a commit 4c8d145

File tree

5 files changed

+428
-19
lines changed

5 files changed

+428
-19
lines changed

src/common.jl

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,46 @@ end
6565
__issquare(assump::OperatorAssumptions) = assump.issq
6666
__conditioning(assump::OperatorAssumptions) = assump.condition
6767

68+
"""
69+
LinearCache{TA, Tb, Tu, Tp, Talg, Tc, Tl, Tr, Ttol, issq, S}
70+
71+
The core cache structure used by LinearSolve for storing and managing the state of linear
72+
solver computations. This mutable struct acts as the primary interface for iterative
73+
solving and caching of factorizations and intermediate results.
74+
75+
## Fields
76+
77+
- `A::TA`: The matrix operator of the linear system.
78+
- `b::Tb`: The right-hand side vector of the linear system.
79+
- `u::Tu`: The solution vector (preallocated storage for the result).
80+
- `p::Tp`: Parameters passed to the linear solver algorithm.
81+
- `alg::Talg`: The linear solver algorithm instance.
82+
- `cacheval::Tc`: Algorithm-specific cache storage for factorizations and intermediate computations.
83+
- `isfresh::Bool`: Cache validity flag for the matrix `A`. `false` means `cacheval` is up-to-date
84+
with respect to `A`, `true` means `cacheval` needs to be updated.
85+
- `precsisfresh::Bool`: Cache validity flag for preconditioners. `false` means `Pl` and `Pr`
86+
are up-to-date with respect to `A`, `true` means they need to be updated.
87+
- `Pl::Tl`: Left preconditioner operator.
88+
- `Pr::Tr`: Right preconditioner operator.
89+
- `abstol::Ttol`: Absolute tolerance for iterative solvers.
90+
- `reltol::Ttol`: Relative tolerance for iterative solvers.
91+
- `maxiters::Int`: Maximum number of iterations for iterative solvers.
92+
- `verbose::Bool`: Whether to print verbose output during solving.
93+
- `assumptions::OperatorAssumptions{issq}`: Assumptions about the operator properties.
94+
- `sensealg::S`: Sensitivity analysis algorithm for automatic differentiation.
95+
96+
## Usage
97+
98+
The `LinearCache` is typically created via `init(::LinearProblem, ::SciMLLinearSolveAlgorithm)`
99+
and then used with `solve!(cache)` for efficient repeated solves with the same matrix structure
100+
but potentially different right-hand sides or parameter values.
101+
102+
## Cache Management
103+
104+
The cache automatically tracks when matrix `A` or parameters `p` change by setting the
105+
appropriate freshness flags. When `solve!` is called, stale cache entries are automatically
106+
recomputed as needed.
107+
"""
68108
mutable struct LinearCache{TA, Tb, Tu, Tp, Talg, Tc, Tl, Tr, Ttol, issq, S}
69109
A::TA
70110
b::Tb
@@ -106,19 +146,81 @@ function update_cacheval!(cache::LinearCache, name::Symbol, x)
106146
end
107147
update_cacheval!(cache, cacheval, name::Symbol, x) = cacheval
108148

149+
"""
150+
init_cacheval(alg::SciMLLinearSolveAlgorithm, args...)
151+
152+
Initialize algorithm-specific cache values for the given linear solver algorithm.
153+
This function returns `nothing` by default and is intended to be overloaded by
154+
specific algorithm implementations that need to store intermediate computations
155+
or factorizations.
156+
157+
## Arguments
158+
- `alg`: The linear solver algorithm instance
159+
- `args...`: Additional arguments passed to the cache initialization
160+
161+
## Returns
162+
Algorithm-specific cache value or `nothing` for algorithms that don't require caching.
163+
"""
109164
init_cacheval(alg::SciMLLinearSolveAlgorithm, args...) = nothing
110165

111166
function SciMLBase.init(prob::LinearProblem, args...; kwargs...)
112167
SciMLBase.init(prob, nothing, args...; kwargs...)
113168
end
114169

170+
"""
171+
default_tol(T)
172+
173+
Compute the default tolerance for iterative linear solvers based on the element type.
174+
The tolerance is typically set as the square root of the machine epsilon for the
175+
given floating point type, ensuring numerical accuracy appropriate for that precision.
176+
177+
## Arguments
178+
- `T`: The element type of the linear system
179+
180+
## Returns
181+
- For floating point types: `√(eps(T))`
182+
- For exact types (Rational, Integer): `0` (exact arithmetic)
183+
- For Any type: `0` (conservative default)
184+
"""
115185
default_tol(::Type{T}) where {T} = (eps(T))
116186
default_tol(::Type{Complex{T}}) where {T} = (eps(T))
117187
default_tol(::Type{<:Rational}) = 0
118188
default_tol(::Type{<:Integer}) = 0
119189
default_tol(::Type{Any}) = 0
120190

191+
"""
192+
default_alias_A(alg, A, b) -> Bool
193+
194+
Determine the default aliasing behavior for the matrix `A` given the algorithm type.
195+
Aliasing allows the algorithm to modify the original matrix in-place for efficiency,
196+
but this may not be desirable or safe for all algorithm types.
197+
198+
## Arguments
199+
- `alg`: The linear solver algorithm
200+
- `A`: The matrix operator
201+
- `b`: The right-hand side vector
202+
203+
## Returns
204+
- `false`: Safe default, algorithm will not modify the original matrix `A`
205+
- `true`: Algorithm may modify `A` in-place for efficiency
206+
207+
## Algorithm-Specific Behavior
208+
- Dense factorizations: `false` (destructive, need to preserve original)
209+
- Krylov methods: `true` (non-destructive, safe to alias)
210+
- Sparse factorizations: `true` (typically preserve sparsity structure)
211+
"""
121212
default_alias_A(::Any, ::Any, ::Any) = false
213+
214+
"""
215+
default_alias_b(alg, A, b) -> Bool
216+
217+
Determine the default aliasing behavior for the right-hand side vector `b` given the
218+
algorithm type. Similar to `default_alias_A` but for the RHS vector.
219+
220+
## Returns
221+
- `false`: Safe default, algorithm will not modify the original vector `b`
222+
- `true`: Algorithm may modify `b` in-place for efficiency
223+
"""
122224
default_alias_b(::Any, ::Any, ::Any) = false
123225

124226
# Non-destructive algorithms default to true
@@ -130,6 +232,24 @@ default_alias_b(::AbstractSparseFactorization, ::Any, ::Any) = true
130232

131233
DEFAULT_PRECS(A, p) = IdentityOperator(size(A)[1]), IdentityOperator(size(A)[2])
132234

235+
"""
236+
__init_u0_from_Ab(A, b)
237+
238+
Initialize the solution vector `u0` with appropriate size and type based on the
239+
matrix `A` and right-hand side `b`. The solution vector is allocated with the
240+
same element type as `b` and sized to match the number of columns in `A`.
241+
242+
## Arguments
243+
- `A`: The matrix operator (determines solution vector size)
244+
- `b`: The right-hand side vector (determines element type)
245+
246+
## Returns
247+
A zero-initialized vector of size `(size(A, 2),)` with element type matching `b`.
248+
249+
## Specializations
250+
- For static matrices (`SMatrix`): Returns a static vector (`SVector`)
251+
- For regular matrices: Returns a similar vector to `b` with appropriate size
252+
"""
133253
function __init_u0_from_Ab(A, b)
134254
u0 = similar(b, size(A, 2))
135255
fill!(u0, false)

src/extension_algs.jl

Lines changed: 101 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,42 @@ end
174174
## RFLUFactorization
175175

176176
"""
177-
`RFLUFactorization()`
177+
RFLUFactorization{P, T}(; pivot = Val(true), thread = Val(true))
178+
179+
A fast pure Julia LU-factorization implementation using RecursiveFactorization.jl.
180+
This is by far the fastest LU-factorization implementation, usually outperforming
181+
OpenBLAS and MKL for smaller matrices (<500x500), but currently optimized only for
182+
Base `Array` with `Float32` or `Float64`. Additional optimization for complex matrices
183+
is in the works.
184+
185+
## Type Parameters
186+
- `P`: Pivoting strategy as `Val{Bool}`. `Val{true}` enables partial pivoting for stability.
187+
- `T`: Threading strategy as `Val{Bool}`. `Val{true}` enables multi-threading for performance.
188+
189+
## Constructor Arguments
190+
- `pivot = Val(true)`: Enable partial pivoting. Set to `Val{false}` to disable for speed
191+
at the cost of numerical stability.
192+
- `thread = Val(true)`: Enable multi-threading. Set to `Val{false}` for single-threaded
193+
execution.
194+
- `throwerror = true`: Whether to throw an error if RecursiveFactorization.jl is not loaded.
195+
196+
## Performance Notes
197+
- Fastest for dense matrices with dimensions roughly < 500×500
198+
- Optimized specifically for Float32 and Float64 element types
199+
- Recursive blocking strategy provides excellent cache performance
200+
- Multi-threading can provide significant speedups on multi-core systems
201+
202+
## Requirements
203+
Using this solver requires that RecursiveFactorization.jl is loaded: `using RecursiveFactorization`
178204
179-
A fast pure Julia LU-factorization implementation
180-
using RecursiveFactorization.jl. This is by far the fastest LU-factorization
181-
implementation, usually outperforming OpenBLAS and MKL for smaller matrices
182-
(<500x500), but currently optimized only for Base `Array` with `Float32` or `Float64`.
183-
Additional optimization for complex matrices is in the works.
205+
## Example
206+
```julia
207+
using RecursiveFactorization
208+
# Fast, stable (with pivoting)
209+
alg1 = RFLUFactorization()
210+
# Fastest (no pivoting), less stable
211+
alg2 = RFLUFactorization(pivot=Val(false))
212+
```
184213
"""
185214
struct RFLUFactorization{P, T} <: AbstractDenseFactorization
186215
function RFLUFactorization(::Val{P}, ::Val{T}; throwerror = true) where {P, T}
@@ -200,17 +229,78 @@ end
200229
# But I'm not sure it makes sense as a GenericFactorization
201230
# since it just uses `LAPACK.getrf!`.
202231
"""
203-
`FastLUFactorization()`
232+
FastLUFactorization()
233+
234+
A high-performance LU factorization using the FastLapackInterface.jl package.
235+
This provides an optimized interface to LAPACK routines with reduced overhead
236+
compared to the standard LinearAlgebra LAPACK wrappers.
237+
238+
## Features
239+
- Reduced function call overhead compared to standard LAPACK wrappers
240+
- Optimized for performance-critical applications
241+
- Uses partial pivoting (no choice of pivoting method available)
242+
- Suitable for dense matrices where maximum performance is required
204243
205-
The FastLapackInterface.jl version of the LU factorization. Notably,
206-
this version does not allow for choice of pivoting method.
244+
## Limitations
245+
- Does not allow customization of pivoting strategy (always uses partial pivoting)
246+
- Requires FastLapackInterface.jl to be loaded
247+
- Limited to dense matrix types supported by LAPACK
248+
249+
## Requirements
250+
Using this solver requires that FastLapackInterface.jl is loaded: `using FastLapackInterface`
251+
252+
## Performance Notes
253+
This factorization is optimized for cases where the overhead of standard LAPACK
254+
function calls becomes significant, typically for moderate-sized dense matrices
255+
or when performing many factorizations.
256+
257+
## Example
258+
```julia
259+
using FastLapackInterface
260+
alg = FastLUFactorization()
261+
sol = solve(prob, alg)
262+
```
207263
"""
208264
struct FastLUFactorization <: AbstractDenseFactorization end
209265

210266
"""
211-
`FastQRFactorization()`
267+
FastQRFactorization{P}(; pivot = ColumnNorm(), blocksize = 36)
268+
269+
A high-performance QR factorization using the FastLapackInterface.jl package.
270+
This provides an optimized interface to LAPACK QR routines with reduced overhead
271+
compared to the standard LinearAlgebra LAPACK wrappers.
272+
273+
## Type Parameters
274+
- `P`: The type of pivoting strategy used
275+
276+
## Fields
277+
- `pivot::P`: Pivoting strategy (e.g., `ColumnNorm()` for column pivoting, `nothing` for no pivoting)
278+
- `blocksize::Int`: Block size for the blocked QR algorithm (default: 36)
279+
280+
## Features
281+
- Reduced function call overhead compared to standard LAPACK wrappers
282+
- Supports various pivoting strategies for numerical stability
283+
- Configurable block size for optimal performance
284+
- Suitable for dense matrices, especially overdetermined systems
212285
213-
The FastLapackInterface.jl version of the QR factorization.
286+
## Performance Notes
287+
The block size can be tuned for optimal performance depending on matrix size and architecture.
288+
The default value of 36 is generally good for most cases, but experimentation may be beneficial
289+
for specific applications.
290+
291+
## Requirements
292+
Using this solver requires that FastLapackInterface.jl is loaded: `using FastLapackInterface`
293+
294+
## Example
295+
```julia
296+
using FastLapackInterface
297+
# QR with column pivoting
298+
alg1 = FastQRFactorization()
299+
# QR without pivoting for speed
300+
alg2 = FastQRFactorization(pivot=nothing)
301+
# Custom block size
302+
alg3 = FastQRFactorization(blocksize=64)
303+
```
214304
"""
215305
struct FastQRFactorization{P} <: AbstractDenseFactorization
216306
pivot::P

src/preconditioners.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
# Tooling Preconditioners
22

3+
"""
4+
ComposePreconditioner{Ti, To}
5+
6+
A preconditioner that composes two preconditioners by applying them sequentially.
7+
The inner preconditioner is applied first, followed by the outer preconditioner.
8+
This allows for building complex preconditioning strategies by combining simpler ones.
9+
10+
## Fields
11+
- `inner::Ti`: The inner (first) preconditioner to apply
12+
- `outer::To`: The outer (second) preconditioner to apply
13+
14+
## Usage
15+
16+
```julia
17+
# Compose a diagonal preconditioner with an ILU preconditioner
18+
inner_prec = DiagonalPreconditioner(diag(A))
19+
outer_prec = ILUFactorization()
20+
composed = ComposePreconditioner(inner_prec, outer_prec)
21+
```
22+
23+
The composed preconditioner applies: `outer(inner(x))` for any vector `x`.
24+
25+
## Mathematical Interpretation
26+
27+
For a linear system `Ax = b`, if `P₁` is the inner and `P₂` is the outer preconditioner,
28+
then the composed preconditioner effectively applies `P₂P₁` as the combined preconditioner.
29+
"""
330
struct ComposePreconditioner{Ti, To}
431
inner::Ti
532
outer::To
@@ -21,6 +48,39 @@ function LinearAlgebra.ldiv!(y, A::ComposePreconditioner, x)
2148
ldiv!(outer, y)
2249
end
2350

51+
"""
52+
InvPreconditioner{T}
53+
54+
A preconditioner wrapper that treats a matrix or operator as if it represents
55+
the inverse of the actual preconditioner. Instead of solving `Px = y`, it
56+
computes `P*y` where `P` is stored as the "inverse" preconditioner matrix.
57+
58+
## Fields
59+
- `P::T`: The stored preconditioner matrix/operator (representing `P⁻¹`)
60+
61+
## Usage
62+
63+
This is useful when you have a matrix that approximates the inverse of your
64+
desired preconditioner. For example, if you have computed an approximate
65+
inverse matrix `Ainv ≈ A⁻¹`, you can use:
66+
67+
```julia
68+
prec = InvPreconditioner(Ainv)
69+
```
70+
71+
## Mathematical Interpretation
72+
73+
For a linear system `Ax = b` with preconditioner `M`, normally we solve `M⁻¹Ax = M⁻¹b`.
74+
With `InvPreconditioner`, the stored matrix `P` represents `M⁻¹` directly, so
75+
applying the preconditioner becomes a matrix-vector multiplication rather than
76+
a linear solve.
77+
78+
## Methods
79+
80+
- `ldiv!(A::InvPreconditioner, x)`: Computes `x ← P*x` (in-place)
81+
- `ldiv!(y, A::InvPreconditioner, x)`: Computes `y ← P*x`
82+
- `mul!(y, A::InvPreconditioner, x)`: Computes `y ← P⁻¹*x` (inverse operation)
83+
"""
2484
struct InvPreconditioner{T}
2585
P::T
2686
end

0 commit comments

Comments
 (0)