Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Multi-Step Methods #356

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name = "NonlinearSolve"
uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
authors = ["SciML"]
version = "3.5.4"
version = "3.6.0"

[deps]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
ConcreteStructs = "2569d6c7-a4a2-43d3-a901-331e8e4be471"
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
Expand Down Expand Up @@ -55,12 +56,13 @@ NonlinearSolveZygoteExt = "Zygote"

[compat]
ADTypes = "0.2.6"
Accessors = "0.1.32"
Aqua = "0.8"
ArrayInterface = "7.7"
BandedMatrices = "1.4"
BenchmarkTools = "1.4"
ConcreteStructs = "0.2.3"
CUDA = "5.1"
ConcreteStructs = "0.2.3"
DiffEqBase = "6.146.0"
Enzyme = "0.11.11"
FastBroadcast = "0.2.8"
Expand Down
1 change: 1 addition & 0 deletions docs/src/devdocs/internal_interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ NonlinearSolve.AbstractNonlinearSolveCache
```@docs
NonlinearSolve.AbstractDescentAlgorithm
NonlinearSolve.AbstractDescentCache
NonlinearSolve.DescentResult
```

## Approximate Jacobian
Expand Down
4 changes: 2 additions & 2 deletions docs/src/tutorials/large_systems.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

This tutorial is for getting into the extra features of using NonlinearSolve.jl. Solving
ill-conditioned nonlinear systems requires specializing the linear solver on properties of
the Jacobian in order to cut down on the ``\mathcal{O}(n^3)`` linear solve and the
``\mathcal{O}(n^2)`` back-solves. This tutorial is designed to explain the advanced usage of
the Jacobian in order to cut down on the `\mathcal{O}(n^3)` linear solve and the
`\mathcal{O}(n^2)` back-solves. This tutorial is designed to explain the advanced usage of
NonlinearSolve.jl by solving the steady state stiff Brusselator partial differential
equation (BRUSS) using NonlinearSolve.jl.

Expand Down
16 changes: 11 additions & 5 deletions src/NonlinearSolve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import Reexport: @reexport
import PrecompileTools: @recompile_invalidations, @compile_workload, @setup_workload

@recompile_invalidations begin
using ADTypes, ConcreteStructs, DiffEqBase, FastBroadcast, FastClosures, LazyArrays,
LineSearches, LinearAlgebra, LinearSolve, MaybeInplace, Preferences, Printf,
SciMLBase, SimpleNonlinearSolve, SparseArrays, SparseDiffTools
using Accessors, ADTypes, ConcreteStructs, DiffEqBase, FastBroadcast, FastClosures,
LazyArrays, LineSearches, LinearAlgebra, LinearSolve, MaybeInplace, Preferences,
Printf, SciMLBase, SimpleNonlinearSolve, SparseArrays, SparseDiffTools

import ArrayInterface: undefmatrix, can_setindex, restructure, fast_scalar_indexing
import DiffEqBase: AbstractNonlinearTerminationMode,
Expand Down Expand Up @@ -45,11 +45,13 @@ include("adtypes.jl")
include("timer_outputs.jl")
include("internal/helpers.jl")

include("descent/common.jl")
include("descent/newton.jl")
include("descent/steepest.jl")
include("descent/dogleg.jl")
include("descent/damped_newton.jl")
include("descent/geodesic_acceleration.jl")
include("descent/multistep.jl")

include("internal/operators.jl")
include("internal/jacobian.jl")
Expand All @@ -69,6 +71,7 @@ include("core/spectral_methods.jl")

include("algorithms/raphson.jl")
include("algorithms/pseudo_transient.jl")
include("algorithms/multistep.jl")
include("algorithms/broyden.jl")
include("algorithms/klement.jl")
include("algorithms/lbroyden.jl")
Expand Down Expand Up @@ -138,7 +141,8 @@ include("default.jl")
end

# Core Algorithms
export NewtonRaphson, PseudoTransient, Klement, Broyden, LimitedMemoryBroyden, DFSane
export NewtonRaphson, PseudoTransient, Klement, Broyden, LimitedMemoryBroyden, DFSane,
MultiStepNonlinearSolver
export GaussNewton, LevenbergMarquardt, TrustRegion
export NonlinearSolvePolyAlgorithm,
RobustMultiNewton, FastShortcutNonlinearPolyalg, FastShortcutNLLSPolyalg
Expand All @@ -152,7 +156,9 @@ export GeneralizedFirstOrderAlgorithm, ApproximateJacobianSolveAlgorithm, Genera

# Descent Algorithms
export NewtonDescent, SteepestDescent, Dogleg, DampedNewtonDescent,
GeodesicAcceleration
GeodesicAcceleration, GenericMultiStepDescent
## Multistep Algorithms
export MultiStepSchemes

# Globalization
## Line Search Algorithms
Expand Down
41 changes: 31 additions & 10 deletions src/abstract_types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@
### `__internal_solve!` specification

```julia
δu, success, intermediates = __internal_solve!(cache::AbstractDescentCache, J, fu, u,
idx::Val; skip_solve::Bool = false, kwargs...)
descent_result = __internal_solve!(cache::AbstractDescentCache, J, fu, u, idx::Val;
skip_solve::Bool = false, kwargs...)
```

- `J`: Jacobian or Inverse Jacobian (if `pre_inverted = Val(true)`).
Expand All @@ -79,21 +79,19 @@
direction was rejected and we want to try with a modified trust region.
- `kwargs`: keyword arguments to pass to the linear solver if there is one.

#### Returned values

- `δu`: the descent direction.
- `success`: Certain Descent Algorithms can reject a descent direction for example
`GeodesicAcceleration`.
- `intermediates`: A named tuple containing intermediates computed during the solve.
For example, `GeodesicAcceleration` returns `NamedTuple{(:v, :a)}` containing the
"velocity" and "acceleration" terms.
Returns a result of type [`DescentResult`](@ref).

### Interface Functions

- `get_du(cache)`: get the descent direction.
- `get_du(cache, ::Val{N})`: get the `N`th descent direction.
- `set_du!(cache, δu)`: set the descent direction.
- `set_du!(cache, δu, ::Val{N})`: set the `N`th descent direction.
- `get_internal_cache(cache, ::Val{field})`: get the internal cache field.
- `get_internal_cache(cache, field::Val, ::Val{N})`: get the `N`th internal cache field.
- `set_internal_cache!(cache, value, ::Val{field})`: set the internal cache field.
- `set_internal_cache!(cache, value, field::Val, ::Val{N})`: set the `N`th internal cache
field.
- `last_step_accepted(cache)`: whether or not the last step was accepted. Checks if the
cache has a `last_step_accepted` field and returns it if it does, else returns `true`.
"""
Expand All @@ -105,6 +103,29 @@
set_du!(cache::AbstractDescentCache, δu) = (cache.δu = δu)
set_du!(cache::AbstractDescentCache, δu, ::Val{1}) = set_du!(cache, δu)
set_du!(cache::AbstractDescentCache, δu, ::Val{N}) where {N} = (cache.δus[N - 1] = δu)
function get_internal_cache(cache::AbstractDescentCache, ::Val{field}) where {field}
return getproperty(cache, field)

Check warning on line 107 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L106-L107

Added lines #L106 - L107 were not covered by tests
end
function get_internal_cache(cache::AbstractDescentCache, field::Val, ::Val{1})
return get_internal_cache(cache, field)

Check warning on line 110 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L109-L110

Added lines #L109 - L110 were not covered by tests
end
function get_internal_cache(

Check warning on line 112 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L112

Added line #L112 was not covered by tests
cache::AbstractDescentCache, ::Val{field}, ::Val{N}) where {field, N}
true_field = Symbol(string(field), "s") # Julia 1.10 compiles this away
return getproperty(cache, true_field)[N]

Check warning on line 115 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L114-L115

Added lines #L114 - L115 were not covered by tests
end
function set_internal_cache!(cache::AbstractDescentCache, value, ::Val{field}) where {field}
return setproperty!(cache, field, value)

Check warning on line 118 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L117-L118

Added lines #L117 - L118 were not covered by tests
end
function set_internal_cache!(

Check warning on line 120 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L120

Added line #L120 was not covered by tests
cache::AbstractDescentCache, value, field::Val, ::Val{1})
return set_internal_cache!(cache, value, field)

Check warning on line 122 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L122

Added line #L122 was not covered by tests
end
function set_internal_cache!(

Check warning on line 124 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L124

Added line #L124 was not covered by tests
cache::AbstractDescentCache, value, ::Val{field}, ::Val{N}) where {field, N}
true_field = Symbol(string(field), "s") # Julia 1.10 compiles this away
return setproperty!(cache, true_field, value, N)

Check warning on line 127 in src/abstract_types.jl

View check run for this annotation

Codecov / codecov/patch

src/abstract_types.jl#L126-L127

Added lines #L126 - L127 were not covered by tests
end

function last_step_accepted(cache::AbstractDescentCache)
hasfield(typeof(cache), :last_step_accepted) && return cache.last_step_accepted
Expand Down
10 changes: 10 additions & 0 deletions src/algorithms/multistep.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function MultiStepNonlinearSolver(; concrete_jac = nothing, linsolve = nothing,

Check warning on line 1 in src/algorithms/multistep.jl

View check run for this annotation

Codecov / codecov/patch

src/algorithms/multistep.jl#L1

Added line #L1 was not covered by tests
scheme = MSS.PotraPtak3, precs = DEFAULT_PRECS, autodiff = nothing,
vjp_autodiff = nothing, linesearch = NoLineSearch())
forward_ad = ifelse(autodiff isa ADTypes.AbstractForwardMode, autodiff, nothing)
scheme_concrete = apply_patch(

Check warning on line 5 in src/algorithms/multistep.jl

View check run for this annotation

Codecov / codecov/patch

src/algorithms/multistep.jl#L4-L5

Added lines #L4 - L5 were not covered by tests
scheme, (; autodiff, vjp_autodiff, jvp_autodiff = forward_ad))
descent = GenericMultiStepDescent(; scheme = scheme_concrete, linsolve, precs)
return GeneralizedFirstOrderAlgorithm(; concrete_jac, name = MSS.display_name(scheme),

Check warning on line 8 in src/algorithms/multistep.jl

View check run for this annotation

Codecov / codecov/patch

src/algorithms/multistep.jl#L7-L8

Added lines #L7 - L8 were not covered by tests
descent, jacobian_ad = autodiff, linesearch, reverse_ad = vjp_autodiff, forward_ad)
end
96 changes: 52 additions & 44 deletions src/core/approximate_jacobian.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,7 @@

linsolve = get_linear_solver(alg.descent)
initialization_cache = __internal_init(prob, alg.initialization, alg, f, fu, u, p;
linsolve,
maxiters, internalnorm)
linsolve, maxiters, internalnorm)

abstol, reltol, termination_cache = init_termination_cache(abstol, reltol, fu, u,
termination_condition)
Expand Down Expand Up @@ -222,9 +221,7 @@
new_jacobian = true
@static_timeit cache.timer "jacobian init/reinit" begin
if get_nsteps(cache) == 0 # First Step is special ignore kwargs
J_init = __internal_solve!(cache.initialization_cache,
cache.fu,
cache.u,
J_init = __internal_solve!(cache.initialization_cache, cache.fu, cache.u,
Val(false))
if INV
if jacobian_initialized_preinverted(cache.initialization_cache.alg)
Expand Down Expand Up @@ -283,54 +280,65 @@
@static_timeit cache.timer "descent" begin
if cache.trustregion_cache !== nothing &&
hasfield(typeof(cache.trustregion_cache), :trust_region)
δu, descent_success, descent_intermediates = __internal_solve!(
cache.descent_cache,
J, cache.fu, cache.u; new_jacobian,
trust_region = cache.trustregion_cache.trust_region)
descent_result = __internal_solve!(cache.descent_cache, J, cache.fu, cache.u;

Check warning on line 283 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L283

Added line #L283 was not covered by tests
new_jacobian, trust_region = cache.trustregion_cache.trust_region)
else
δu, descent_success, descent_intermediates = __internal_solve!(
cache.descent_cache,
J, cache.fu, cache.u; new_jacobian)
descent_result = __internal_solve!(cache.descent_cache, J, cache.fu, cache.u;
new_jacobian)
end
end

if descent_success
if GB === :LineSearch
@static_timeit cache.timer "linesearch" begin
needs_reset, α = __internal_solve!(cache.linesearch_cache, cache.u, δu)
end
if needs_reset && cache.steps_since_last_reset > 5 # Reset after a burn-in period
cache.force_reinit = true
else
@static_timeit cache.timer "step" begin
@bb axpy!(α, δu, cache.u)
evaluate_f!(cache, cache.u, cache.p)
end
end
elseif GB === :TrustRegion
@static_timeit cache.timer "trustregion" begin
tr_accepted, u_new, fu_new = __internal_solve!(cache.trustregion_cache, J,
cache.fu, cache.u, δu, descent_intermediates)
if tr_accepted
@bb copyto!(cache.u, u_new)
@bb copyto!(cache.fu, fu_new)
end
if hasfield(typeof(cache.trustregion_cache), :shrink_counter) &&
cache.trustregion_cache.shrink_counter > cache.max_shrink_times
cache.retcode = ReturnCode.ShrinkThresholdExceeded
cache.force_stop = true
end
end
α = true
elseif GB === :None
if descent_result.success
if GB === :None
@static_timeit cache.timer "step" begin
@bb axpy!(1, δu, cache.u)
if descent_result.u !== missing
@bb copyto!(cache.u, descent_result.u)
elseif descent_result.δu !== missing
@bb axpy!(1, descent_result.δu, cache.u)

Check warning on line 297 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L294-L297

Added lines #L294 - L297 were not covered by tests
else
error("This shouldn't occur. `$(cache.alg.descent)` is incorrectly \

Check warning on line 299 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L299

Added line #L299 was not covered by tests
specified.")
end
evaluate_f!(cache, cache.u, cache.p)
end
α = true
else
error("Unknown Globalization Strategy: $(GB). Allowed values are (:LineSearch, \
:TrustRegion, :None)")
δu = descent_result.δu
@assert δu!==missing "Descent Supporting LineSearch or TrustRegion must return a `δu`."

if GB === :LineSearch
@static_timeit cache.timer "linesearch" begin
needs_reset, α = __internal_solve!(cache.linesearch_cache, cache.u, δu)
end
if needs_reset && cache.steps_since_last_reset > 5 # Reset after a burn-in period
cache.force_reinit = true
else
@static_timeit cache.timer "step" begin
@bb axpy!(α, δu, cache.u)
evaluate_f!(cache, cache.u, cache.p)
end
end
elseif GB === :TrustRegion
@static_timeit cache.timer "trustregion" begin
tr_accepted, u_new, fu_new = __internal_solve!(cache.trustregion_cache,

Check warning on line 323 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L321-L323

Added lines #L321 - L323 were not covered by tests
J, cache.fu, cache.u, δu, descent_result.extras)
if tr_accepted
@bb copyto!(cache.u, u_new)
@bb copyto!(cache.fu, fu_new)
α = true

Check warning on line 328 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L325-L328

Added lines #L325 - L328 were not covered by tests
else
α = false

Check warning on line 330 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L330

Added line #L330 was not covered by tests
end
if hasfield(typeof(cache.trustregion_cache), :shrink_counter) &&

Check warning on line 332 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L332

Added line #L332 was not covered by tests
cache.trustregion_cache.shrink_counter > cache.max_shrink_times
cache.retcode = ReturnCode.ShrinkThresholdExceeded
cache.force_stop = true

Check warning on line 335 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L334-L335

Added lines #L334 - L335 were not covered by tests
end
end
else
error("Unknown Globalization Strategy: $(GB). Allowed values are \

Check warning on line 339 in src/core/approximate_jacobian.jl

View check run for this annotation

Codecov / codecov/patch

src/core/approximate_jacobian.jl#L339

Added line #L339 was not covered by tests
(:LineSearch, :TrustRegion, :None)")
end
end
check_and_update!(cache, cache.fu, cache.u, cache.u_cache)
else
Expand Down
Loading
Loading