@@ -130,10 +130,10 @@ struct ForwardDiffStepSize3 <: ForwardDiffStepSize end
130130Computes the Jacobian-vector product `j(x[n]) * Δx[n]` for a Newton-Krylov
131131method without directly using the Jacobian `j(x[n])`, and instead only using
132132`x[n]`, `f(x[n])`, and other function evaluations `f(x′)`. This is done by
133- calling `jvp!(::JacobianFreeJVP, cache, jΔx, Δx, x, f!, f)`. The `jΔx` passed to
134- a Jacobian-free JVP is modified in-place. The `cache` can be obtained with
135- `allocate_cache(::JacobianFreeJVP, x_prototype)`, where `x_prototype` is
136- `similar` to `x` (and also to `Δx` and `f`).
133+ calling `jvp!(::JacobianFreeJVP, cache, jΔx, Δx, x, f!, f, post_implicit! )`.
134+ The `jΔx` passed to a Jacobian-free JVP is modified in-place. The `cache` can
135+ be obtained with `allocate_cache(::JacobianFreeJVP, x_prototype)`, where
136+ `x_prototype` is ` similar` to `x` (and also to `Δx` and `f`).
137137"""
138138abstract type JacobianFreeJVP end
139139
@@ -151,12 +151,13 @@ end
151151
152152allocate_cache (:: ForwardDiffJVP , x_prototype) = (; x2 = similar (x_prototype), f2 = similar (x_prototype))
153153
154- function jvp! (alg:: ForwardDiffJVP , cache, jΔx, Δx, x, f!, f)
154+ function jvp! (alg:: ForwardDiffJVP , cache, jΔx, Δx, x, f!, f, post_implicit! )
155155 (; default_step, step_adjustment) = alg
156156 (; x2, f2) = cache
157157 FT = eltype (x)
158158 ε = FT (step_adjustment) * default_step (Δx, x)
159159 @. x2 = x + ε * Δx
160+ isnothing (post_implicit!) || post_implicit! (x2)
160161 f! (f2, x2)
161162 @. jΔx = (f2 - f) / ε
162163end
@@ -342,10 +343,10 @@ end
342343Finds an approximation `Δx[n] ≈ j(x[n]) \\ f(x[n])` for Newton's method such
343344that `‖f(x[n]) - j(x[n]) * Δx[n]‖ ≤ rtol[n] * ‖f(x[n])‖`, where `rtol[n]` is the
344345value of the forcing term on iteration `n`. This is done by calling
345- `solve_krylov!(::KrylovMethod, cache, Δx, x, f!, f, n, j = nothing)`, where `f` is
346- `f (x[n])` and, if it is specified, `j` is either `j(x[n])` or an approximation
347- of `j(x[n])`. The `Δx` passed to a Krylov method is modified in-place. The
348- `cache` can be obtained with `allocate_cache(::KrylovMethod, x_prototype)`,
346+ `solve_krylov!(::KrylovMethod, cache, Δx, x, f!, f, n, post_implicit!, j = nothing)`,
347+ where `f` is `f (x[n])` and, if it is specified, `j` is either `j(x[n])` or an
348+ approximation of `j(x[n])`. The `Δx` passed to a Krylov method is modified in-place.
349+ The `cache` can be obtained with `allocate_cache(::KrylovMethod, x_prototype)`,
349350where `x_prototype` is `similar` to `x` (and also to `Δx` and `f`).
350351
351352This is primarily a wrapper for a `Krylov.KrylovSolver` from `Krylov.jl`. In
@@ -427,14 +428,14 @@ function allocate_cache(alg::KrylovMethod, x_prototype)
427428 )
428429end
429430
430- function solve_krylov! (alg:: KrylovMethod , cache, Δx, x, f!, f, n, j = nothing )
431+ function solve_krylov! (alg:: KrylovMethod , cache, Δx, x, f!, f, n, post_implicit!, j = nothing )
431432 (; jacobian_free_jvp, forcing_term, solve_kwargs) = alg
432433 (; disable_preconditioner, debugger) = alg
433434 type = solver_type (alg)
434435 (; jacobian_free_jvp_cache, forcing_term_cache, solver, debugger_cache) = cache
435436 jΔx! (jΔx, Δx) =
436437 isnothing (jacobian_free_jvp) ? mul! (jΔx, j, Δx) :
437- jvp! (jacobian_free_jvp, jacobian_free_jvp_cache, jΔx, Δx, x, f!, f)
438+ jvp! (jacobian_free_jvp, jacobian_free_jvp_cache, jΔx, Δx, x, f!, f, post_implicit! )
438439 opj = LinearOperator (eltype (x), length (x), length (x), false , false , jΔx!)
439440 M = disable_preconditioner || isnothing (j) || isnothing (jacobian_free_jvp) ? I : j
440441 print_debug! (debugger, debugger_cache, opj, M)
@@ -566,9 +567,25 @@ function allocate_cache(alg::NewtonsMethod, x_prototype, j_prototype = nothing)
566567 )
567568end
568569
569- solve_newton! (alg:: NewtonsMethod , cache:: Nothing , x, f!, j! = nothing , post_implicit! = nothing ) = nothing
570-
571- function solve_newton! (alg:: NewtonsMethod , cache, x, f!, j! = nothing , post_implicit! = nothing )
570+ solve_newton! (
571+ alg:: NewtonsMethod ,
572+ cache:: Nothing ,
573+ x,
574+ f!,
575+ j! = nothing ,
576+ post_implicit! = nothing ,
577+ post_implicit_last! = nothing ,
578+ ) = nothing
579+
580+ function solve_newton! (
581+ alg:: NewtonsMethod ,
582+ cache,
583+ x,
584+ f!,
585+ j! = nothing ,
586+ post_implicit! = nothing ,
587+ post_implicit_last! = nothing ,
588+ )
572589 (; max_iters, update_j, krylov_method, convergence_checker, verbose) = alg
573590 (; krylov_method_cache, convergence_checker_cache) = cache
574591 (; Δx, f, j) = cache
@@ -588,16 +605,20 @@ function solve_newton!(alg::NewtonsMethod, cache, x, f!, j! = nothing, post_impl
588605 ldiv! (Δx, j, f)
589606 end
590607 else
591- solve_krylov! (krylov_method, krylov_method_cache, Δx, x, f!, f, n, j)
608+ solve_krylov! (krylov_method, krylov_method_cache, Δx, x, f!, f, n, post_implicit!, j)
592609 end
593610 is_verbose (verbose) && @info " Newton iteration $n : ‖x‖ = $(norm (x)) , ‖Δx‖ = $(norm (Δx)) "
594611
595612 x .- = Δx
596- isnothing (post_implicit!) || post_implicit! (x)
597613 # Update x[n] with Δx[n - 1], and exit the loop if Δx[n] is not needed.
598614 # Check for convergence if necessary.
599615 if is_converged! (convergence_checker, convergence_checker_cache, x, Δx, n)
616+ isnothing (post_implicit_last!) || post_implicit_last! (x)
600617 break
618+ elseif n == max_iters
619+ isnothing (post_implicit_last!) || post_implicit_last! (x)
620+ else
621+ isnothing (post_implicit!) || post_implicit! (x)
601622 end
602623 if is_verbose (verbose) && n == max_iters
603624 @warn " Newton's method did not converge within $n iterations: ‖x‖ = $(norm (x)) , ‖Δx‖ = $(norm (Δx)) "
0 commit comments