1
1
import LinearSolve: AbstractFactorization, DefaultAlgorithmChoice, DefaultLinearSolver
2
2
3
+ const LinearSolveFailureCode = isdefined (ReturnCode, :InternalLinearSolveFailure ) ?
4
+ ReturnCode. InternalLinearSolveFailure : ReturnCode. Failure
5
+
3
6
"""
4
7
LinearSolverCache(alg, linsolve, A, b, u; kwargs...)
5
8
@@ -23,6 +26,15 @@ handled:
23
26
24
27
Returns the solution of the system `u` and stores the updated cache in `cache.lincache`.
25
28
29
+ #### Special Handling for Rank-deficient Matrix `A`
30
+
31
+ If we detect a failure in the linear solve (mostly due to using an algorithm that doesn't
32
+ support rank-deficient matrices), we emit a warning and attempt to solve the problem using
33
+ Pivoted QR factorization. This is quite efficient if there are only a few rank-deficient
34
+ that originate in the problem. However, if these are quite frequent for the main nonlinear
35
+ system, then it is recommended to use a different linear solver that supports rank-deficient
36
+ matrices.
37
+
26
38
#### Keyword Arguments
27
39
28
40
- `reuse_A_if_factorization`: If `true`, then the factorization of `A` is reused if
@@ -36,6 +48,7 @@ not mutated, we do this by copying over `A` to a preconstructed cache.
36
48
@concrete mutable struct LinearSolverCache <: AbstractLinearSolverCache
37
49
lincache
38
50
linsolve
51
+ additional_lincache:: Any
39
52
A
40
53
b
41
54
precs
@@ -71,7 +84,7 @@ function LinearSolverCache(alg, linsolve, A, b, u; kwargs...)
71
84
(linsolve === nothing && A isa SMatrix) ||
72
85
(A isa Diagonal) ||
73
86
(linsolve isa typeof (\ ))
74
- return LinearSolverCache (nothing , nothing , A, b, nothing , 0 , 0 )
87
+ return LinearSolverCache (nothing , nothing , nothing , A, b, nothing , 0 , 0 )
75
88
end
76
89
@bb u_ = copy (u_fixed)
77
90
linprob = LinearProblem (A, b; u0 = u_, kwargs... )
@@ -89,7 +102,7 @@ function LinearSolverCache(alg, linsolve, A, b, u; kwargs...)
89
102
# Unalias here, we will later use these as caches
90
103
lincache = init (linprob, linsolve; alias_A = false , alias_b = false , Pl, Pr)
91
104
92
- return LinearSolverCache (lincache, linsolve, nothing , nothing , precs, 0 , 0 )
105
+ return LinearSolverCache (lincache, linsolve, nothing , nothing , nothing , precs, 0 , 0 )
93
106
end
94
107
95
108
# Direct Linear Solve Case without Caching
@@ -108,6 +121,7 @@ function (cache::LinearSolverCache{Nothing})(;
108
121
end
109
122
return res
110
123
end
124
+
111
125
# Use LinearSolve.jl
112
126
function (cache:: LinearSolverCache )(;
113
127
A = nothing , b = nothing , linu = nothing , du = nothing , p = nothing ,
@@ -139,8 +153,38 @@ function (cache::LinearSolverCache)(;
139
153
cache. lincache. Pr = Pr
140
154
end
141
155
156
+ # display(A)
157
+
142
158
linres = solve! (cache. lincache)
159
+ # @show cache.lincache.cacheval
160
+ # @show LinearAlgebra.issuccess(cache.lincache.cacheval)
143
161
cache. lincache = linres. cache
162
+ # Unfortunately LinearSolve.jl doesn't have the most uniform ReturnCode handling
163
+ if linres. retcode === ReturnCode. Failure
164
+ # TODO : We need to guard this somehow because this will surely fail if A is on GPU
165
+ # TODO : or some fancy Matrix type
166
+ if ! (cache. linsolve isa QRFactorization{ColumnNorm})
167
+ @warn " Potential Rank Deficient Matrix Detected. Attempting to solve using \
168
+ Pivoted QR Factorization."
169
+ @assert (A != = nothing )&& (b != = nothing ) " This case is not yet supported. \
170
+ Please open an issue at \
171
+ https://github.com/SciML/NonlinearSolve.jl"
172
+ if cache. additional_lincache === nothing # First time
173
+ linprob = LinearProblem (A, b; u0 = linres. u)
174
+ cache. additional_lincache = init (
175
+ linprob, QRFactorization (ColumnNorm ()); alias_u0 = false ,
176
+ alias_A = false , alias_b = false , cache. lincache. Pl, cache. lincache. Pr)
177
+ else
178
+ cache. additional_lincache. A = A
179
+ cache. additional_lincache. b = b
180
+ cache. additional_lincache. Pl = cache. lincache. Pl
181
+ cache. additional_lincache. Pr = cache. lincache. Pr
182
+ end
183
+ linres = solve! (cache. additional_lincache)
184
+ cache. additional_lincache = linres. cache
185
+ return linres. u
186
+ end
187
+ end
144
188
145
189
return linres. u
146
190
end
0 commit comments