Skip to content

Commit 3603ee0

Browse files
committed
IterativeSolvers: fix MINRES residual field and IDRS keyword forwarding
1 parent f85f66b commit 3603ee0

2 files changed

Lines changed: 47 additions & 4 deletions

File tree

ext/LinearSolveIterativeSolversExt.jl

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,18 @@ function LinearSolve.init_cacheval(
106106
history = IterativeSolvers.ConvergenceHistory(partial = true)
107107
history[:abstol] = abstol
108108
history[:reltol] = reltol
109-
filter_kwargs(; idrs_s = 0, kwargs...) = kwargs
109+
# `idrs_iterable!` takes the tolerances and the iteration cap
110+
# positionally and accepts only `smoothing`/`verbose` as keywords, so
111+
# every name passed positionally below has to be dropped here. Filtering
112+
# `idrs_s` alone meant `IterativeSolversJL_IDRS(abstol = ...)` forwarded
113+
# `abstol` as a keyword too and failed with a `MethodError`
114+
# (SciML/LinearSolve.jl#24).
115+
function filter_kwargs(;
116+
idrs_s = 0, abstol = nothing, reltol = nothing,
117+
maxiter = nothing, kwargs...
118+
)
119+
return kwargs
120+
end
110121
IterativeSolvers.idrs_iterable!(
111122
history, u, A, b, s, Pl, abstol, reltol, maxiters;
112123
filter_kwargs(; alg.kwargs...)...
@@ -167,15 +178,22 @@ function SciMLBase.solve!(cache::LinearCache, alg::IterativeSolversJL; kwargs...
167178
# TODO inject callbacks KSP into solve! cb!(cache.cacheval)
168179
end
169180

170-
resid = cache.cacheval isa IterativeSolvers.IDRSIterable ? cache.cacheval.R :
171-
cache.cacheval.residual
181+
resid = _iterable_residual(cache.cacheval)
172182
if resid isa IterativeSolvers.Residual
173183
resid = resid.current
174184
end
175185

176186
return SciMLBase.build_linear_solution(alg, cache.u, resid, nothing; iters = i)
177187
end
178188

189+
# IterativeSolvers does not name this field consistently across its iterables.
190+
# Reading `.residual` unconditionally made `IterativeSolversJL_MINRES` throw a
191+
# `FieldError` on every solve, because `MINRESIterable` calls it `resnorm`
192+
# (SciML/LinearSolve.jl#24).
193+
_iterable_residual(iterable) = iterable.residual
194+
_iterable_residual(iterable::IterativeSolvers.IDRSIterable) = iterable.R
195+
_iterable_residual(iterable::IterativeSolvers.MINRESIterable) = iterable.resnorm
196+
179197
purge_history!(iter, x, b) = nothing
180198
function purge_history!(iter::IterativeSolvers.GMRESIterable, x, b)
181199
iter.k = 1

test/Core/basictests.jl

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,15 +732,40 @@ end
732732
("GMRES", IterativeSolversJL_GMRES(; kwargs...)),
733733
("IDRS", IterativeSolversJL_IDRS(; kwargs...)),
734734
("IDRS(2)", IterativeSolversJL_IDRS(; idrs_s = 2, kwargs...)),
735+
("MINRES", IterativeSolversJL_MINRES(; kwargs...)),
736+
# BICGSTAB stays out: IterativeSolvers' own bicgstabl breaks
737+
# down on the identity `prob1` here and throws
738+
# "matrix contains Infs or NaNs" out of LAPACK, which is an
739+
# upstream numerical issue rather than a wiring problem on
740+
# this side.
735741
# ("BICGSTAB",IterativeSolversJL_BICGSTAB(; kwargs...)),
736-
# ("MINRES",IterativeSolversJL_MINRES(; kwargs...)),
737742
)
738743
@testset "$(alg[1])" begin
739744
test_interface(alg[2], prob1, prob2)
740745
test_interface(alg[2], prob3, prob4)
741746
test_tolerance_update(alg[2], prob5, u5)
742747
end
743748
end
749+
750+
@testset "tolerances as algorithm kwargs (#24)" begin
751+
# `idrs_iterable!` takes abstol/reltol/maxiter positionally and
752+
# accepts only `smoothing`/`verbose` as keywords, so forwarding
753+
# them from `alg.kwargs` used to raise a MethodError. MINRES read
754+
# `.residual`, which its iterable calls `resnorm`, and threw a
755+
# FieldError on every solve.
756+
# Note these solves report `ReturnCode.Default` rather than
757+
# `Success`: this extension never passes a retcode to
758+
# `build_linear_solution`. That is pre-existing and separate from
759+
# what is tested here, so assert on the solution itself.
760+
for alg in (
761+
IterativeSolversJL_IDRS(abstol = 1.0e-10, reltol = 1.0e-10),
762+
IterativeSolversJL_MINRES(abstol = 1.0e-10, reltol = 1.0e-10),
763+
IterativeSolversJL_CG(abstol = 1.0e-10, reltol = 1.0e-10),
764+
)
765+
sol = solve(LinearProblem(A5, b5), alg)
766+
@test sol.u u5 rtol = 1.0e-6
767+
end
768+
end
744769
end
745770
end
746771

0 commit comments

Comments
 (0)