Skip to content

Commit 3bc2191

Browse files
Merge pull request #631 from SciML/retcodes
Fix successful return codes for factorization methods
2 parents 31ade68 + f235234 commit 3bc2191

File tree

5 files changed

+38
-10
lines changed

5 files changed

+38
-10
lines changed

ext/LinearSolveRecursiveFactorizationExt.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function SciMLBase.solve!(cache::LinearSolve.LinearCache, alg::RFLUFactorization
2525
cache.isfresh = false
2626
end
2727
y = ldiv!(cache.u, LinearSolve.@get_cacheval(cache, :RFLUFactorization)[1], cache.b)
28-
SciMLBase.build_linear_solution(alg, y, nothing, cache)
28+
SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success)
2929
end
3030

3131
end

src/appleaccelerate.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ function SciMLBase.solve!(cache::LinearCache, alg::AppleAccelerateLUFactorizatio
243243
res = aa_getrf!(A; ipiv = cacheval[1].ipiv, info = cacheval[2])
244244
fact = LU(res[1:3]...), res[4]
245245
cache.cacheval = fact
246+
247+
if !LinearAlgebra.issuccess(fact)
248+
return SciMLBase.build_linear_solution(
249+
alg, cache.u, nothing, cache; retcode = ReturnCode.Failure)
250+
end
246251
cache.isfresh = false
247252
end
248253

@@ -258,5 +263,5 @@ function SciMLBase.solve!(cache::LinearCache, alg::AppleAccelerateLUFactorizatio
258263
aa_getrs!('N', A.factors, A.ipiv, cache.u; info)
259264
end
260265

261-
SciMLBase.build_linear_solution(alg, cache.u, nothing, cache)
266+
SciMLBase.build_linear_solution(alg, cache.u, nothing, cache; retcode = ReturnCode.Success)
262267
end

src/factorization.jl

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::LUFactorization; kwargs...)
150150

151151
F = @get_cacheval(cache, :LUFactorization)
152152
y = _ldiv!(cache.u, F, cache.b)
153-
SciMLBase.build_linear_solution(alg, y, nothing, cache)
153+
SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success)
154154
end
155155

156156
function do_factorization(alg::LUFactorization, A, b, u)
@@ -203,7 +203,7 @@ function SciMLBase.solve!(cache::LinearSolve.LinearCache, alg::GenericLUFactoriz
203203
cache.isfresh = false
204204
end
205205
y = ldiv!(cache.u, LinearSolve.@get_cacheval(cache, :GenericLUFactorization)[1], cache.b)
206-
SciMLBase.build_linear_solution(alg, y, nothing, cache)
206+
SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success)
207207
end
208208

209209
function init_cacheval(
@@ -953,6 +953,12 @@ A fast factorization which uses a Cholesky factorization on A * A'. Can be much
953953
faster than LU factorization, but is not as numerically stable and thus should only
954954
be applied to well-conditioned matrices.
955955
956+
!!! warn
957+
`NormalCholeskyFactorization` should only be applied to well-conditioned matrices. As a
958+
method it is not able to easily identify possible numerical issues. As a check it is
959+
recommended that the user checks `A*u-b` is approximately zero, as this may be untrue
960+
even if `sol.retcode === ReturnCode.Success` due to numerical stability issues.
961+
956962
## Positional Arguments
957963
958964
- pivot: Defaults to RowMaximum(), but can be NoPivot()
@@ -1010,6 +1016,12 @@ function SciMLBase.solve!(cache::LinearCache, alg::NormalCholeskyFactorization;
10101016
fact = cholesky(Symmetric((A)' * A), alg.pivot; check = false)
10111017
end
10121018
cache.cacheval = fact
1019+
1020+
if hasmethod(LinearAlgebra.issuccess, Tuple{typeof(fact)}) && !LinearAlgebra.issuccess(fact)
1021+
return SciMLBase.build_linear_solution(
1022+
alg, cache.u, nothing, cache; retcode = ReturnCode.Failure)
1023+
end
1024+
10131025
cache.isfresh = false
10141026
end
10151027
if issparsematrixcsc(A)
@@ -1021,7 +1033,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::NormalCholeskyFactorization;
10211033
else
10221034
y = ldiv!(cache.u, @get_cacheval(cache, :NormalCholeskyFactorization), A' * cache.b)
10231035
end
1024-
SciMLBase.build_linear_solution(alg, y, nothing, cache)
1036+
SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success)
10251037
end
10261038

10271039
## NormalBunchKaufmanFactorization

src/mkl.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,16 @@ function SciMLBase.solve!(cache::LinearCache, alg::MKLLUFactorization;
219219
res = getrf!(A; ipiv = cacheval[1].ipiv, info = cacheval[2])
220220
fact = LU(res[1:3]...), res[4]
221221
cache.cacheval = fact
222+
223+
if !LinearAlgebra.issuccess(fact)
224+
return SciMLBase.build_linear_solution(
225+
alg, cache.u, nothing, cache; retcode = ReturnCode.Failure)
226+
end
222227
cache.isfresh = false
223228
end
224229

225230
y = ldiv!(cache.u, @get_cacheval(cache, :MKLLUFactorization)[1], cache.b)
226-
SciMLBase.build_linear_solution(alg, y, nothing, cache)
231+
SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success)
227232

228233
#=
229234
A, info = @get_cacheval(cache, :MKLLUFactorization)

test/retcodes.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using LinearSolve, LinearAlgebra, RecursiveFactorization
1+
using LinearSolve, LinearAlgebra, RecursiveFactorization, Test
22

33
alglist = (
44
LUFactorization,
@@ -19,7 +19,7 @@ alglist = (
1919
prob = LinearProblem(A, b)
2020
linsolve = init(prob, alg())
2121
sol = solve!(linsolve)
22-
@test SciMLBase.successful_retcode(sol.retcode) || sol.retcode == ReturnCode.Default # The latter seems off...
22+
@test SciMLBase.successful_retcode(sol.retcode)
2323
end
2424
end
2525

@@ -39,7 +39,13 @@ lualgs = (
3939
prob = LinearProblem(A, b)
4040
linsolve = init(prob, alg)
4141
sol = solve!(linsolve)
42-
@test !SciMLBase.successful_retcode(sol.retcode)
42+
if alg isa NormalCholeskyFactorization
43+
# This is a known and documented incorrectness in NormalCholeskyFactorization
44+
# due to numerical instability in its method that is fundamental.
45+
@test SciMLBase.successful_retcode(sol.retcode)
46+
else
47+
@test !SciMLBase.successful_retcode(sol.retcode)
48+
end
4349
end
4450
end
4551

@@ -62,4 +68,4 @@ rankdeficientalgs = (
6268
sol = solve!(linsolve)
6369
@test SciMLBase.successful_retcode(sol.retcode)
6470
end
65-
end
71+
end

0 commit comments

Comments
 (0)