Skip to content

Commit

Permalink
Fix tests and make Multiple Shooting Type Stable
Browse files Browse the repository at this point in the history
  • Loading branch information
avik-pal committed Oct 11, 2023
1 parent b9074b6 commit be2298f
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 218 deletions.
11 changes: 11 additions & 0 deletions src/algorithms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ Significantly more stable than Single Shooting.
grid_coarsening
end

function concretize_jacobian_algorithm(alg::MultipleShooting, prob)
jac_alg = concrete_jacobian_algorithm(alg.jac_alg, prob, alg)
return MultipleShooting(alg.ode_alg, alg.nlsolve, jac_alg, alg.nshoots,
alg.grid_coarsening)
end

function update_nshoots(alg::MultipleShooting, nshoots::Int)
return MultipleShooting(alg.ode_alg, alg.nlsolve, alg.jac_alg, nshoots,
alg.grid_coarsening)
end

function MultipleShooting(nshoots::Int, ode_alg; nlsolve = NewtonRaphson(),
grid_coarsening = true, jac_alg = BVPJacobianAlgorithm())
@assert grid_coarsening isa Bool || grid_coarsening isa Function ||
Expand Down
256 changes: 65 additions & 191 deletions src/solve/multiple_shooting.jl

Large diffs are not rendered by default.

95 changes: 94 additions & 1 deletion src/sparse_jacobians.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
function _sparse_like(I, J, x::AbstractArray, m = maximum(I), n = maximum(J))
I′ = adapt(parameterless_type(x), I)
J′ = adapt(parameterless_type(x), J)
V = similar(x, length(I))
V = __ones_like(x, length(I))
return sparse(I′, J′, V, m, n)
end

Expand All @@ -21,6 +21,11 @@ end
col_colorvec
end

Base.size(M::ColoredMatrix, args...) = size(M.M, args...)
Base.eltype(M::ColoredMatrix) = eltype(M.M)

ColoredMatrix() = ColoredMatrix(nothing, nothing, nothing)

function SparseDiffTools.PrecomputedJacobianColorvec(M::ColoredMatrix)
return PrecomputedJacobianColorvec(; jac_prototype = M.M, M.row_colorvec,
M.col_colorvec)
Expand Down Expand Up @@ -110,3 +115,91 @@ function __generate_sparse_jacobian_prototype(::MIRKCache, ::TwoPointBVProblem,
end

# For Multiple Shooting
"""
__generate_sparse_jacobian_prototype(::MultipleShooting, ::StandardBVProblem,
bcresid_prototype, u0, N::Int, nshoots::Int)
__generate_sparse_jacobian_prototype(::MultipleShooting, ::TwoPointBVProblem,
bcresid_prototype, u0, N::Int, nshoots::Int)
Returns a 3-Tuple:
* Entire Jacobian Prototype (if Two-Point Problem) else `nothing`.
* Sparse Non-BC Part Jacobian Prototype along with the column and row color vectors.
* Sparse BC Part Jacobian Prototype along with the column and row color vectors (if
Two-Point Problem) else `nothing`.
"""
function __generate_sparse_jacobian_prototype(::MultipleShooting, ::StandardBVProblem,
bcresid_prototype, u0, N::Int, nshoots::Int)
Is = Vector{Int}(undef, (N^2 + N) * nshoots)
Js = Vector{Int}(undef, (N^2 + N) * nshoots)

idx = 1
for i in 1:nshoots
for (i₁, i₂) in Iterators.product(1:N, 1:N)
Is[idx] = i₁ + ((i - 1) * N)
Js[idx] = i₂ + ((i - 1) * N)
idx += 1
end
Is[idx:(idx + N - 1)] .= (1:N) .+ ((i - 1) * N)
Js[idx:(idx + N - 1)] .= (1:N) .+ (i * N)
idx += N
end

J_c = _sparse_like(Is, Js, u0)

col_colorvec = Vector{Int}(undef, size(J_c, 2))
for i in eachindex(col_colorvec)
col_colorvec[i] = mod1(i, 2N)
end
row_colorvec = Vector{Int}(undef, size(J_c, 1))
for i in eachindex(row_colorvec)
row_colorvec[i] = mod1(i, 2N)
end

return nothing, ColoredMatrix(J_c, row_colorvec, col_colorvec), nothing
end

function __generate_sparse_jacobian_prototype(alg::MultipleShooting, ::TwoPointBVProblem,
bcresid_prototype::ArrayPartition, u0, N::Int, nshoots::Int)
resida, residb = bcresid_prototype.x
L₁, L₂ = length(resida), length(residb)

_, J_c, _ = __generate_sparse_jacobian_prototype(alg, StandardBVProblem(),
bcresid_prototype, u0, N, nshoots)

Is_bc = Vector{Int}(undef, (L₁ + L₂) * N)
Js_bc = Vector{Int}(undef, (L₁ + L₂) * N)
idx = 1
for i in 1:L₁, j in 1:N
Is_bc[idx] = i
Js_bc[idx] = j
idx += 1
end
for i in 1:L₂, j in 1:N
Is_bc[idx] = i + L₁
Js_bc[idx] = j + N
idx += 1
end

col_colorvec_bc = Vector{Int}(undef, 2N)
row_colorvec_bc = Vector{Int}(undef, L₁ + L₂)
col_colorvec_bc[1:N] .= 1:N
col_colorvec_bc[(N + 1):end] .= 1:N
for i in 1:max(L₁, L₂)
i L₁ && (row_colorvec_bc[i] = i)
i L₂ && (row_colorvec_bc[i + L₁] = i)
end

J_bc = ColoredMatrix(_sparse_like(Is_bc, Js_bc, bcresid_prototype), row_colorvec_bc,
col_colorvec_bc)

J_full = _sparse_like(Int[], Int[], u0, size(J_bc, 1) + size(J_c, 1),
size(J_c, 2))

J_full[(L₁ + L₂ + 1):end, :] .= J_c.M
J_full[1:L₁, 1:N] .= J_bc.M[1:L₁, 1:N]
J_full[(L₁ + 1):(L₁ + L₂), (end - 2N + 1):(end - N)] .= J_bc.M[(L₁ + 1):(L₁ + L₂),
(N + 1):(2N)]

return J_full, J_c, J_bc
end
11 changes: 9 additions & 2 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@ end
function BVPJacobianAlgorithm(diffmode = missing; nonbc_diffmode = missing,
bc_diffmode = missing)
if diffmode !== missing
@assert nonbc_diffmode === missing && bc_diffmode === missing
bc_diffmode = bc_diffmode === missing ? diffmode : bc_diffmode
nonbc_diffmode = nonbc_diffmode === missing ? diffmode : nonbc_diffmode
return BVPJacobianAlgorithm(diffmode, diffmode, diffmode)
else
diffmode = nothing
bc_diffmode = bc_diffmode === missing ? nothing : bc_diffmode
nonbc_diffmode = nonbc_diffmode === missing ? nothing : nonbc_diffmode
return BVPJacobianAlgorithm(bc_diffmode, nonbc_diffmode, nonbc_diffmode)
return BVPJacobianAlgorithm(bc_diffmode, nonbc_diffmode, diffmode)
end
end

Expand Down Expand Up @@ -88,6 +89,12 @@ function concrete_jacobian_algorithm(jac_alg::BVPJacobianAlgorithm, ::TwoPointBV
return BVPJacobianAlgorithm(bc_diffmode, nonbc_diffmode, diffmode)
end

# This can cause Type Instability
function concretize_jacobian_algorithm(alg, prob)
@set! alg.jac_alg = concrete_jacobian_algorithm(alg.jac_alg, prob, alg)
return alg
end

function MIRKJacobianComputationAlgorithm(diffmode = missing;
collocation_diffmode = missing, bc_diffmode = missing)
Base.depwarn("`MIRKJacobianComputationAlgorithm` has been deprecated in favor of \
Expand Down
6 changes: 3 additions & 3 deletions test/mirk/ensemble.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ bvp = BVProblem(ode!, bc!, initial_guess, tspan, p)
ensemble_prob = EnsembleProblem(bvp; prob_func)

@testset "$(solver)" for solver in (MIRK2, MIRK3, MIRK4, MIRK5, MIRK6)
jac_algs = [MIRKJacobianComputationAlgorithm(),
MIRKJacobianComputationAlgorithm(; bc_diffmode = AutoFiniteDiff(),
collocation_diffmode = AutoSparseFiniteDiff())]
jac_algs = [BVPJacobianAlgorithm(),
BVPJacobianAlgorithm(; bc_diffmode = AutoFiniteDiff(),
nonbc_diffmode = AutoSparseFiniteDiff())]
for jac_alg in jac_algs
# Not sure why it is throwing so many warnings
sol = solve(ensemble_prob, solver(; jac_alg); trajectories = 10, dt = 0.1)
Expand Down
4 changes: 2 additions & 2 deletions test/mirk/mirk_convergence_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ end
u0 = MVector{2}([pi / 2, pi / 2])
bvp1 = BVProblem(simplependulum!, bc_pendulum!, u0, tspan)

jac_alg = MIRKJacobianComputationAlgorithm(; bc_diffmode = AutoFiniteDiff(),
collocation_diffmode = AutoSparseFiniteDiff())
jac_alg = BVPJacobianAlgorithm(; bc_diffmode = AutoFiniteDiff(),
nonbc_diffmode = AutoSparseFiniteDiff())

# Using ForwardDiff might lead to Cache expansion warnings
@test_nowarn solve(bvp1, MIRK2(; jac_alg); dt = 0.005)
Expand Down
2 changes: 1 addition & 1 deletion test/misc/non_vector_inputs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,5 @@ probs = [
end
end

# TODO: Multiple Shooting
# FIXME: Add Multiple Shooting here once it supports non-vector inputs
end
36 changes: 18 additions & 18 deletions test/shooting/orbital.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,25 +64,24 @@ cur_bc_2point_b! = (resid, sol, p) -> bc!_generator_2p_b(resid, sol, init_val)
resid_f = Array{Float64}(undef, 6)
resid_f_2p = (Array{Float64, 1}(undef, 3), Array{Float64, 1}(undef, 3))

TestTol = 0.05

### Now use the BVP solver to get closer
bvp = BVProblem(orbital!, cur_bc!, y0, tspan)
for autodiff in (AutoForwardDiff(), AutoFiniteDiff(; fdtype = Val(:central)),
AutoSparseForwardDiff(), AutoFiniteDiff(; fdtype = Val(:forward)),
AutoSparseFiniteDiff())
nlsolve = NewtonRaphson(; autodiff)
@time sol = solve(bvp, Shooting(DP5(); nlsolve); force_dtmin = true,
abstol = 1e-13, reltol = 1e-13)
nlsolve = TrustRegion(; autodiff)
@time sol = solve(bvp, Shooting(DP5(); nlsolve); force_dtmin = true, abstol = 1e-13,
reltol = 1e-13, verbose = false)
cur_bc!(resid_f, sol, nothing, sol.t)
@test norm(resid_f, Inf) < TestTol
@info "Single Shooting Lambert's Problem: $(norm(resid_f, Inf))"
@test norm(resid_f, Inf) < 0.005

jac_alg = BVPJacobianAlgorithm(; nonbc_diffmode = autodiff)
@time sol = solve(bvp, MultipleShooting(10, DP5(); nlsolve, jac_alg); abstol = 1e-6,
reltol = 1e-6)
@test SciMLBase.successful_retcode(sol)
@time sol = solve(bvp, MultipleShooting(10, AutoVern7(Rodas5P()); nlsolve, jac_alg);
abstol = 1e-6, reltol = 1e-6, verbose = false)
cur_bc!(resid_f, sol, nothing, sol.t)
@test norm(resid_f, Inf) < 1e-6
@info "Multiple Shooting Lambert's Problem: $(norm(resid_f, Inf))"
@test norm(resid_f, Inf) < 0.005
end

### Using the TwoPoint BVP Structure
Expand All @@ -91,18 +90,19 @@ bvp = TwoPointBVProblem(orbital!, (cur_bc_2point_a!, cur_bc_2point_b!), y0, tspa
for autodiff in (AutoForwardDiff(), AutoFiniteDiff(; fdtype = Val(:central)),
AutoSparseForwardDiff(), AutoFiniteDiff(; fdtype = Val(:forward)),
AutoSparseFiniteDiff())
nlsolve = NewtonRaphson(; autodiff)
nlsolve = TrustRegion(; autodiff)
@time sol = solve(bvp, Shooting(DP5(); nlsolve); force_dtmin = true, abstol = 1e-13,
reltol = 1e-13)
reltol = 1e-13, verbose = false)
cur_bc_2point_a!(resid_f_2p[1], sol(t0), nothing)
cur_bc_2point_b!(resid_f_2p[2], sol(t1), nothing)
@test norm(reduce(vcat, resid_f_2p), Inf) < TestTol
@info "Single Shooting Lambert's Problem: $(norm(reduce(vcat, resid_f_2p), Inf))"
@test norm(reduce(vcat, resid_f_2p), Inf) < 0.005

jac_alg = BVPJacobianAlgorithm(; nonbc_diffmode = autodiff)
@time sol = solve(bvp, MultipleShooting(10, DP5(); nlsolve, jac_alg); abstol = 1e-6,
reltol = 1e-6)
@test SciMLBase.successful_retcode(sol)
jac_alg = BVPJacobianAlgorithm(; nonbc_diffmode = autodiff, bc_diffmode = autodiff)
@time sol = solve(bvp, MultipleShooting(10, AutoVern7(Rodas5P()); nlsolve, jac_alg);
abstol = 1e-6, reltol = 1e-6, verbose = false)
cur_bc_2point_a!(resid_f_2p[1], sol(t0), nothing)
cur_bc_2point_b!(resid_f_2p[2], sol(t1), nothing)
@test norm(reduce(vcat, resid_f_2p), Inf) < TestTol
@info "Multiple Shooting Lambert's Problem: $(norm(reduce(vcat, resid_f_2p), Inf))"
@test norm(reduce(vcat, resid_f_2p), Inf) < 0.005
end

0 comments on commit be2298f

Please sign in to comment.