StatProfilerHTML.jl report
Generated on Thu, 21 Dec 2023 13:06:16
File source code
Line Exclusive Inclusive Code
1 """
2 `OperatorCondition`
3
4 Specifies the assumption of matrix conditioning for the default linear solver choices. Condition number
5 is defined as the ratio of eigenvalues. The numerical stability of many linear solver algorithms
6 can be dependent on the condition number of the matrix. The condition number can be computed as:
7
8 ```julia
9 using LinearAlgebra
10 cond(rand(100,100))
11 ```
12
13 However, in practice this computation is very expensive and thus not possible for most practical cases.
14 Therefore, OperatorCondition lets one share to LinearSolve the expected conditioning. The higher the
15 expected condition number, the safer the algorithm needs to be and thus there is a trade-off between
16 numerical performance and stability. By default the method assumes the operator may be ill-conditioned
17 for the standard linear solvers to converge (such as LU-factorization), though more extreme
18 ill-conditioning or well-conditioning could be the case and specified through this assumption.
19 """
20 EnumX.@enumx OperatorCondition begin
21 """
22 `OperatorCondition.IllConditioned`
23
24 The default assumption of LinearSolve. Assumes that the operator can have minor ill-conditioning
25 and thus needs to use safe algorithms.
26 """
27 IllConditioned
28 """
29 `OperatorCondition.VeryIllConditioned`
30
31 Assumes that the operator can have fairly major ill-conditioning and thus the standard linear algebra
32 algorithms cannot be used.
33 """
34 VeryIllConditioned
35 """
36 `OperatorCondition.SuperIllConditioned`
37
38 Assumes that the operator can have fairly extreme ill-conditioning and thus the most stable algorithm
39 is used.
40 """
41 SuperIllConditioned
42 """
43 `OperatorCondition.WellConditioned`
44
45 Assumes that the operator can have fairly contained conditioning and thus the fastest algorithm is
46 used.
47 """
48 WellConditioned
49 end
50
51 """
52 OperatorAssumptions(issquare = nothing; condition::OperatorCondition.T = IllConditioned)
53
54 Sets the operator `A` assumptions used as part of the default algorithm
55 """
56 struct OperatorAssumptions{T}
57 issq::T
58 condition::OperatorCondition.T
59 end
60
61 function OperatorAssumptions(issquare = nothing;
62 condition::OperatorCondition.T = OperatorCondition.IllConditioned)
63 OperatorAssumptions{typeof(issquare)}(issquare, condition)
64 end
65 __issquare(assump::OperatorAssumptions) = assump.issq
66 __conditioning(assump::OperatorAssumptions) = assump.condition
67
68 mutable struct LinearCache{TA, Tb, Tu, Tp, Talg, Tc, Tl, Tr, Ttol, issq}
69 A::TA
70 b::Tb
71 u::Tu
72 p::Tp
73 alg::Talg
74 cacheval::Tc # store alg cache here
75 isfresh::Bool # false => cacheval is set wrt A, true => update cacheval wrt A
76 Pl::Tl # preconditioners
77 Pr::Tr
78 abstol::Ttol
79 reltol::Ttol
80 maxiters::Int
81 verbose::Bool
82 assumptions::OperatorAssumptions{issq}
83 end
84
85 function Base.setproperty!(cache::LinearCache, name::Symbol, x)
86 if name === :A
87 setfield!(cache, :isfresh, true)
88 elseif name === :b
89 # In case there is something that needs to be done when b is updated
90 update_cacheval!(cache, :b, x)
91 elseif name === :cacheval && cache.alg isa DefaultLinearSolver
92 @assert cache.cacheval isa DefaultLinearSolverInit
93 return setfield!(cache.cacheval, Symbol(cache.alg.alg), x)
94 end
95 setfield!(cache, name, x)
96 end
97
98 function update_cacheval!(cache::LinearCache, name::Symbol, x)
99 return update_cacheval!(cache, cache.cacheval, name, x)
100 end
101 update_cacheval!(cache, cacheval, name::Symbol, x) = cacheval
102
103 init_cacheval(alg::SciMLLinearSolveAlgorithm, args...) = nothing
104
105 function SciMLBase.init(prob::LinearProblem, args...; kwargs...)
106 SciMLBase.init(prob, nothing, args...; kwargs...)
107 end
108
109 default_tol(::Type{T}) where {T} = √(eps(T))
110 default_tol(::Type{Complex{T}}) where {T} = √(eps(T))
111 default_tol(::Type{<:Rational}) = 0
112 default_tol(::Type{<:Integer}) = 0
113 default_tol(::Type{Any}) = 0
114
115 default_alias_A(::Any, ::Any, ::Any) = false
116 default_alias_b(::Any, ::Any, ::Any) = false
117
118 # Non-destructive algorithms default to true
119 default_alias_A(::AbstractKrylovSubspaceMethod, ::Any, ::Any) = true
120 default_alias_b(::AbstractKrylovSubspaceMethod, ::Any, ::Any) = true
121
122 function __init_u0_from_Ab(A, b)
123 u0 = similar(b, size(A, 2))
124 fill!(u0, false)
125 return u0
126 end
127 __init_u0_from_Ab(::SMatrix{S1, S2}, b) where {S1, S2} = zeros(SVector{S2, eltype(b)})
128
129 71 (26 %)
71 (26 %) samples spent in init
71 (100 %) (incl.) when called from #init#82 line 273
71 (100 %) samples spent calling #init#3
function SciMLBase.init(prob::LinearProblem, alg::SciMLLinearSolveAlgorithm,
130 args...;
131 alias_A = default_alias_A(alg, prob.A, prob.b),
132 alias_b = default_alias_b(alg, prob.A, prob.b),
133 abstol = default_tol(real(eltype(prob.b))),
134 reltol = default_tol(real(eltype(prob.b))),
135 maxiters::Int = length(prob.b),
136 verbose::Bool = false,
137 Pl = IdentityOperator(size(prob.A)[1]),
138 Pr = IdentityOperator(size(prob.A)[2]),
139 assumptions = OperatorAssumptions(issquare(prob.A)),
140 kwargs...)
141 @unpack A, b, u0, p = prob
142
143 A = if alias_A || A isa SMatrix
144 A
145 elseif A isa Array || A isa SparseMatrixCSC
146 copy(A)
147 else
148 deepcopy(A)
149 end
150
151 b = if b isa SparseArrays.AbstractSparseArray && !(A isa Diagonal)
152 Array(b) # the solution to a linear solve will always be dense!
153 elseif alias_b || b isa SVector
154 b
155 elseif b isa Array || b isa SparseMatrixCSC
156 copy(b)
157 else
158 deepcopy(b)
159 end
160
161 u0_ = u0 !== nothing ? u0 : __init_u0_from_Ab(A, b)
162
163 # Guard against type mismatch for user-specified reltol/abstol
164 reltol = real(eltype(prob.b))(reltol)
165 abstol = real(eltype(prob.b))(abstol)
166
167 71 (26 %)
71 (26 %) samples spent in #init#3
71 (100 %) (incl.) when called from init line 129
71 (100 %) samples spent calling init_cacheval
cacheval = init_cacheval(alg, A, b, u0_, Pl, Pr, maxiters, abstol, reltol, verbose,
168 assumptions)
169 isfresh = true
170 Tc = typeof(cacheval)
171
172 cache = LinearCache{typeof(A), typeof(b), typeof(u0_), typeof(p), typeof(alg), Tc,
173 typeof(Pl), typeof(Pr), typeof(reltol), typeof(assumptions.issq)}(A, b, u0_,
174 p, alg, cacheval, isfresh, Pl, Pr, abstol, reltol, maxiters, verbose, assumptions)
175 return cache
176 end
177
178 function SciMLBase.solve(prob::LinearProblem, args...; kwargs...)
179 solve!(init(prob, nothing, args...; kwargs...))
180 end
181
182 function SciMLBase.solve(prob::LinearProblem,
183 alg::Union{SciMLLinearSolveAlgorithm, Nothing},
184 args...; kwargs...)
185 solve!(init(prob, alg, args...; kwargs...))
186 end
187
188 81 (30 %)
81 (30 %) samples spent in solve!
81 (100 %) (incl.) when called from #dolinsolve#7 line 108
81 (100 %) samples spent calling #solve!#6
function SciMLBase.solve!(cache::LinearCache, args...; kwargs...)
189 81 (30 %)
81 (30 %) samples spent in #solve!#6
81 (100 %) (incl.) when called from solve! line 188
81 (100 %) samples spent calling solve!
solve!(cache, cache.alg, args...; kwargs...)
190 end
191
192 # Special Case for StaticArrays
193 const StaticLinearProblem = LinearProblem{uType, iip, <:SMatrix,
194 <:Union{<:SMatrix, <:SVector}} where {uType, iip}
195
196 function SciMLBase.solve(prob::StaticLinearProblem, args...; kwargs...)
197 return SciMLBase.solve(prob, nothing, args...; kwargs...)
198 end
199
200 function SciMLBase.solve(prob::StaticLinearProblem,
201 alg::Union{Nothing, SciMLLinearSolveAlgorithm}, args...; kwargs...)
202 if alg === nothing || alg isa DirectLdiv!
203 u = prob.A \ prob.b
204 elseif alg isa LUFactorization
205 u = lu(prob.A) \ prob.b
206 elseif alg isa QRFactorization
207 u = qr(prob.A) \ prob.b
208 elseif alg isa CholeskyFactorization
209 u = cholesky(prob.A) \ prob.b
210 elseif alg isa NormalCholeskyFactorization
211 u = cholesky(Symmetric(prob.A' * prob.A)) \ (prob.A' * prob.b)
212 elseif alg isa SVDFactorization
213 u = svd(prob.A) \ prob.b
214 else
215 # Slower Path but handles all cases
216 cache = init(prob, alg, args...; kwargs...)
217 return solve!(cache)
218 end
219 return SciMLBase.build_linear_solution(alg, u, nothing, prob)
220 end