Skip to content

Commit 172511a

Browse files
Merge pull request #350 from SciML/qqy/bvp_opt
Optimization based solvers
2 parents 25e9002 + 8a6fc74 commit 172511a

38 files changed

Lines changed: 531 additions & 330 deletions

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ makedocs(; sitename = "BoundaryValueDiffEq.jl",
2727
clean = true,
2828
doctest = false,
2929
checkdocs = :exports,
30-
warnonly = [:missing_docs],
30+
warnonly = [:missing_docs, :cross_references],
3131
plugins = [bib, interlinks],
3232
format = Documenter.HTML(assets = ["assets/favicon.ico"],
3333
canonical = "https://docs.sciml.ai/BoundaryValueDiffEq/stable/"),

docs/src/basics/solve.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- `controller`: Error controller for collocation methods, default as `DefectControl()`, more controller options in [Error Control Adaptivity](@ref error_control).
66
- `defect_threshold`: Monitor of the size of defect norm. Defaults to `0.1`.
77
- `odesolve_kwargs`: OrdinaryDiffEq.jl solvers kwargs for passing to ODE solving in shooting methods. For more information, see the documentation for OrdinaryDiffEq: [Common Solver Options](https://docs.sciml.ai/DiffEqDocs/latest/basics/common_solver_opts/).
8-
- `nlsolve_kwargs`: NonlinearSolve.jl solvers kwargs for passing to nonlinear solving in collocation methods and shooting methods. For more information, see the documentation for NonlinearSolve: [Common Solver Options](https://docs.sciml.ai/NonlinearSolve/stable/basics/solve/). The default absolute tolerance of nonlinear solving in collocaio
8+
- `nlsolve_kwargs`: NonlinearSolve.jl solvers kwargs for passing to nonlinear solving in collocation methods and shooting methods. For more information, see the documentation for NonlinearSolve: [Common Solver Options](https://docs.sciml.ai/NonlinearSolve/stable/basics/solve/). The default internal nonlinear solver is [customized polyalgorithm](https://github.com/SciML/BoundaryValueDiffEq.jl/blob/master/lib/BoundaryValueDiffEqCore/src/default_nlsolve.jl) and the default absolute tolerance of nonlinear solving in collocation and shooting methods is `1e-6`.
9+
- `optimize_kwargs`: Optimization.jl solvers kwargs for passing to optimization problem solving in collocation methods and shooting methods. For more information, see the documentation for Optimization: [Common Solver Options](https://docs.sciml.ai/Optimization/stable/API/solve/). The internal optimization solver should be specified and the default absolute tolerance of optimization problem solving in collocation and shooting methods is `1e-6`.
910
- `verbose`: Toggles whether warnings are thrown when the solver exits early. Defaults to `true`.
1011
- `ensemblealg`: Whether `MultipleShooting` uses multithreading, default as `EnsembleThreads()`. For more information, see the documentation for OrdinaryDiffEq: [EnsembleAlgorithms](https://docs.sciml.ai/DiffEqDocs/latest/features/ensemble/#EnsembleAlgorithms).

docs/src/tutorials/unknown_parameters.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ sol = solve(bvp, MIRK4(), dt = 0.05)
5454
plot(sol)
5555
```
5656

57-
after solving the boundary value problem, the estimated unknown parameters can be accessed with
57+
after solving the boundary value problem, the estimated unknown parameters can be accessed in the solution
5858

5959
```@example unknown
6060
sol.prob.p

lib/BoundaryValueDiffEqAscher/src/BoundaryValueDiffEqAscher.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ using AlmostBlockDiagonals: AlmostBlockDiagonals, IntermediateAlmostBlockDiagona
66
using BoundaryValueDiffEqCore: AbstractBoundaryValueDiffEqAlgorithm,
77
AbstractBoundaryValueDiffEqCache, BVPJacobianAlgorithm,
88
__extract_problem_details, concrete_jacobian_algorithm,
9-
__Fix3, __concrete_nonlinearsolve_algorithm,
9+
__Fix3, __concrete_solve_algorithm,
1010
__internal_nlsolve_problem, __vec, __vec_f, __vec_f!,
1111
__vec_bc, __vec_bc!, __extract_mesh, get_dense_ad,
12-
__get_bcresid_prototype, __split_kwargs,
13-
__default_nonsparse_ad
12+
__get_bcresid_prototype, __split_kwargs, __concrete_kwargs,
13+
__default_nonsparse_ad, __construct_internal_problem
1414

1515
using ConcreteStructs: @concrete
1616
using DiffEqBase: DiffEqBase

lib/BoundaryValueDiffEqAscher/src/algorithms.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ for stage in (1, 2, 3, 4, 5, 6, 7)
1414
- `nlsolve`: Internal Nonlinear solver. Any solver which conforms to the SciML
1515
`NonlinearProblem` interface can be used. Note that any autodiff argument for
1616
the solver will be ignored and a custom jacobian algorithm will be used.
17+
- `optimize`: Internal Optimization solver. Any solver which conforms to the SciML
18+
`OptimizationProblem` interface can be used. Note that any autodiff argument for
19+
the solver will be ignored and a custom jacobian algorithm will be used.
1720
- `max_num_subintervals`: Number of maximal subintervals, default as 3000.
1821
- `zeta`: side condition points, should always be provided.
1922
@@ -46,8 +49,9 @@ for stage in (1, 2, 3, 4, 5, 6, 7)
4649
}
4750
```
4851
"""
49-
@kwdef struct $(alg){N, J <: BVPJacobianAlgorithm} <: AbstractAscher
52+
@kwdef struct $(alg){N, O, J <: BVPJacobianAlgorithm} <: AbstractAscher
5053
nlsolve::N = nothing
54+
optimize::O = nothing
5155
zeta::Vector{Float64} = nothing
5256
jac_alg::J = BVPJacobianAlgorithm()
5357
max_num_subintervals::Int = 3000

lib/BoundaryValueDiffEqAscher/src/ascher.jl

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
TU
4040
valstr
4141
nlsolve_kwargs
42+
optimize_kwargs
4243
kwargs
4344
end
4445

@@ -60,7 +61,8 @@ end
6061

6162
function SciMLBase.__init(
6263
prob::BVProblem, alg::AbstractAscher; dt = 0.0, controller = GlobalErrorControl(),
63-
adaptive = true, abstol = 1e-4, nlsolve_kwargs = (; abstol = abstol), kwargs...)
64+
adaptive = true, abstol = 1e-4, nlsolve_kwargs = (; abstol = abstol),
65+
optimize_kwargs = (; abstol = abstol), kwargs...)
6466
(; tspan, p) = prob
6567
_, T, ncy, n, u0 = __extract_problem_details(prob; dt, check_positive_dt = true)
6668
t₀, t₁ = tspan
@@ -147,9 +149,9 @@ function SciMLBase.__init(
147149
g = build_almost_block_diagonals(zeta, ncomp, mesh, T)
148150
cache = AscherCache{iip, T}(
149151
prob, f, jac, bc, bcjac, k, copy(mesh), mesh, mesh_dt, ncomp, ny, p, zeta,
150-
fixpnt, alg, prob.problem_type, bcresid_prototype, residual, zval, yval,
151-
gval, err, g, w, v, lz, ly, dmz, delz, deldmz, dqdmz, dmv, pvtg, pvtw, TU,
152-
valst, nlsolve_kwargs, (; abstol, dt, adaptive, controller, kwargs...))
152+
fixpnt, alg, prob.problem_type, bcresid_prototype, residual, zval, yval, gval,
153+
err, g, w, v, lz, ly, dmz, delz, deldmz, dqdmz, dmv, pvtg, pvtw, TU, valst,
154+
nlsolve_kwargs, optimize_kwargs, (; abstol, dt, adaptive, controller, kwargs...))
153155
return cache
154156
end
155157

@@ -176,8 +178,10 @@ function __perform_ascher_iteration(cache::AscherCache{iip, T}, abstol, adaptive
176178
iip, T}
177179
info::ReturnCode.T = ReturnCode.Success
178180
nlprob = __construct_nlproblem(cache)
179-
nlsolve_alg = __concrete_nonlinearsolve_algorithm(nlprob, cache.alg.nlsolve)
180-
nlsol = __solve(nlprob, nlsolve_alg; cache.nlsolve_kwargs...)
181+
solve_alg = __concrete_solve_algorithm(nlprob, cache.alg.nlsolve, cache.alg.optimize)
182+
kwargs = __concrete_kwargs(
183+
cache.alg.nlsolve, cache.alg.optimize, cache.nlsolve_kwargs, cache.optimize_kwargs)
184+
nlsol = __solve(nlprob, solve_alg; kwargs...)
181185
error_norm = 2 * abstol
182186
info = nlsol.retcode
183187

@@ -203,7 +207,7 @@ function __perform_ascher_iteration(cache::AscherCache{iip, T}, abstol, adaptive
203207
__expand_cache_for_error!(cache)
204208

205209
_nlprob = __construct_nlproblem(cache)
206-
nlsol = __solve(_nlprob, nlsolve_alg; cache.nlsolve_kwargs...)
210+
nlsol = solve(_nlprob, solve_alg; kwargs...)
207211

208212
error_norm = error_estimate!(cache)
209213
if norm(error_norm) > abstol
@@ -346,9 +350,9 @@ function __construct_nlproblem(cache::AscherCache{iip, T}) where {iip, T}
346350
jac_prototype, u, diffmode, jac_cache, loss, cache.p)
347351
end
348352

349-
nlf = NonlinearFunction{iip}(
350-
loss; jac = jac, resid_prototype = resid_prototype, jac_prototype = jac_prototype)
351-
return __internal_nlsolve_problem(cache.prob, similar(lz), lz, nlf, lz, cache.p)
353+
return __construct_internal_problem(
354+
cache.prob, alg, loss, jac, jac_prototype, resid_prototype,
355+
lz, cache.p, cache.ncomp, length(cache.mesh))
352356
end
353357

354358
function __ascher_mpoint_jacobian!(J, x, diffmode, diffcache, loss, resid, p)

lib/BoundaryValueDiffEqAscher/src/utils.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ end
112112
return nothing
113113
end
114114

115-
@views function recursive_flatten!(y::Vector, x::Vector{Vector{T}}) where {T}
115+
@views function recursive_flatten!(y::AbstractArray, x::Vector{Vector{T}}) where {T}
116116
i = 0
117117
for xᵢ in x
118118
copyto!(y[(i + 1):(i + length(xᵢ))], xᵢ)

lib/BoundaryValueDiffEqCore/src/BoundaryValueDiffEqCore.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ include("utils.jl")
2929
include("algorithms.jl")
3030
include("abstract_types.jl")
3131
include("alg_utils.jl")
32-
include("default_nlsolve.jl")
32+
include("default_internal_solve.jl")
3333
include("calc_errors.jl")
3434

3535
function SciMLBase.__solve(prob::AbstractBVProblem,

lib/BoundaryValueDiffEqCore/src/algorithms.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,12 @@ function Base.show(io::IO, alg::AbstractBoundaryValueDiffEqAlgorithm)
2626
print(io, join(modifiers, ", "))
2727
print(io, ")")
2828
end
29+
30+
# Check what's the internal solver, nonlinear or optimization?
31+
function __internal_solver(alg::AbstractBoundaryValueDiffEqAlgorithm)
32+
# We don't allow both `nlsolve` and `optimize` to be specified at the same time
33+
(isnothing(alg.nlsolve) && isnothing(alg.optimize)) &&
34+
error("Either `nlsolve` or `optimize` must be specified in the algorithm, but not both.")
35+
isnothing(alg.nlsolve) && return alg.optimize
36+
isnothing(alg.optimize) && return alg.nlsolve
37+
end

lib/BoundaryValueDiffEqCore/src/default_nlsolve.jl renamed to lib/BoundaryValueDiffEqCore/src/default_internal_solve.jl

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,37 @@ function __FastShortcutNonlinearPolyalg(::Type{T} = Float64; concrete_jac = noth
4646
return NonlinearSolvePolyAlgorithm(algs)
4747
end
4848

49-
@inline __concrete_nonlinearsolve_algorithm(prob, alg) = alg
50-
@inline function __concrete_nonlinearsolve_algorithm(prob, ::Nothing)
49+
"""
50+
__concrete_solve_algorithm(prob, nlsolve_alg, optimize_alg)
51+
52+
Automatic solver choosing according to the input solver.
53+
If none of the solvers are specified, we use nonlinear solvers from NonlinearSolve.jl.
54+
If both of the nonlinear solver and optimization solver are specified, we throw an error.
55+
If only one of the nonlinear solver and optimization solver is specified, we use that solver.
56+
"""
57+
@inline __concrete_solve_algorithm(prob, alg) = alg
58+
@inline __concrete_solve_algorithm(prob, alg, ::Nothing) = alg
59+
@inline __concrete_solve_algorithm(prob, ::Nothing, alg) = alg
60+
@inline __concrete_solve_algorithm(prob,
61+
alg1,
62+
alg2) = error("Both `nlsolve` and `optimize` are specified in the algorithm, but only one of them is allowed. Please specify only one of them.")
63+
@inline function __concrete_solve_algorithm(prob, ::Nothing)
64+
if prob isa NonlinearLeastSquaresProblem
65+
return __FastShortcutBVPCompatibleNLLSPolyalg(eltype(prob.u0))
66+
else
67+
return __FastShortcutBVPCompatibleNonlinearPolyalg(eltype(prob.u0))
68+
end
69+
end
70+
@inline function __concrete_solve_algorithm(prob, ::Nothing, ::Nothing)
5171
if prob isa NonlinearLeastSquaresProblem
5272
return __FastShortcutBVPCompatibleNLLSPolyalg(eltype(prob.u0))
5373
else
5474
return __FastShortcutBVPCompatibleNonlinearPolyalg(eltype(prob.u0))
5575
end
5676
end
77+
78+
# Some optimization algorithms (solvers from interfacing packages) don't support the __solve(prob) interface
79+
@inline __internal_solve(
80+
prob::Union{SciMLBase.NonlinearProblem, SciMLBase.NonlinearLeastSquaresProblem},
81+
alg; kwargs...) = __solve(prob, alg; kwargs...)
82+
@inline __internal_solve(prob::SciMLBase.OptimizationProblem, alg; kwargs...) = solve(prob, alg; kwargs...)

0 commit comments

Comments
 (0)