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

Added implicit midpoint discretization #228

Merged
merged 42 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
2de90c5
first draft for midpoint
PierreMartinon Aug 27, 2024
4827b2a
build
PierreMartinon Aug 27, 2024
a439056
first test ok
PierreMartinon Aug 27, 2024
26d15db
updated tests (improved checks for initial guess)
PierreMartinon Aug 27, 2024
8840b52
tests ok for midpoint
PierreMartinon Aug 27, 2024
73eb608
fix prof
PierreMartinon Aug 27, 2024
424a990
types seeem fine
PierreMartinon Aug 28, 2024
ae15f01
new file midpoint.jl
PierreMartinon Aug 28, 2024
b1d392a
tests ok
PierreMartinon Aug 28, 2024
ccf9ae5
ok
PierreMartinon Aug 28, 2024
2d9f538
sync from main
PierreMartinon Aug 28, 2024
47b2e46
sync from trapeze
PierreMartinon Aug 28, 2024
e14aa28
file
PierreMartinon Aug 28, 2024
650847c
trapz
PierreMartinon Aug 28, 2024
9a04a8b
tests ok
PierreMartinon Aug 28, 2024
9f7dcf9
sync
PierreMartinon Aug 28, 2024
7ff59fb
pseudo args
PierreMartinon Aug 28, 2024
999fb40
final sync
PierreMartinon Aug 28, 2024
a5e51f4
midpoint ready
PierreMartinon Aug 29, 2024
b6cd209
retrigger checks
PierreMartinon Aug 29, 2024
47f537d
starting gauss legendre 2
PierreMartinon Aug 29, 2024
3945769
more tests
PierreMartinon Aug 29, 2024
56fd72f
prof
PierreMartinon Aug 29, 2024
de9c474
prof
PierreMartinon Aug 29, 2024
5143fe3
use args for pathconstraints
PierreMartinon Aug 29, 2024
0ddda2c
updated midpoint with args for pathconstraints
PierreMartinon Aug 29, 2024
3968497
prof
PierreMartinon Aug 30, 2024
c30b347
prof
PierreMartinon Aug 30, 2024
3bd97f2
NB multiple dispatch DOES seem to increase allocations...
PierreMartinon Aug 30, 2024
fc3f7c7
getter for full time grid
PierreMartinon Aug 30, 2024
1bb57e1
cleanup
PierreMartinon Aug 30, 2024
8e40554
use disc-specific Args structs
PierreMartinon Aug 30, 2024
23fcac9
midpoint ok; trapeze perf closer to main branch
PierreMartinon Aug 30, 2024
ac5b300
try to parametrize docp with the discretization ?
PierreMartinon Aug 30, 2024
2fa5b3c
perf for trapeze is back to main branch level; cause apparently was …
PierreMartinon Aug 30, 2024
6db3f69
bench
PierreMartinon Aug 30, 2024
abb6b37
Merge branch 'main' into midpoint
PierreMartinon Sep 2, 2024
b863ccd
prof
PierreMartinon Sep 2, 2024
b8ee2fa
better call to get_optim_variable
PierreMartinon Sep 2, 2024
ecaaf78
@. is not better than individual dot operators for state equation
PierreMartinon Sep 2, 2024
9df36a0
added todo for AD in constraints
PierreMartinon Sep 2, 2024
33125ff
fix
PierreMartinon Sep 2, 2024
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "CTDirect"
uuid = "790bbbee-bee9-49ee-8912-a9de031322d5"
authors = ["Olivier Cots <olivier.cots@enseeiht.fr>"]
version = "0.12.0"
version = "0.12.1"

[deps]
ADNLPModels = "54578032-b7ea-4c30-94aa-7cbd1cce6c9a"
Expand Down
15 changes: 9 additions & 6 deletions benchmark/benchmark.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function bench(;
precompile = true,
display = false,
verbose = true,
discretization = :trapeze
)

#######################################################
Expand Down Expand Up @@ -66,6 +67,7 @@ function bench(;
linear_solver = linear_solver,
max_iter = 0,
display = display,
discretization = discretization
)
t_precomp += t
end
Expand All @@ -84,6 +86,7 @@ function bench(;
linear_solver = linear_solver,
grid_size = grid_size,
tol = tol,
discretization = discretization
)
if !isnothing(problem[:obj]) && !isapprox(sol.objective, problem[:obj], rtol = 5e-2)
error(
Expand Down Expand Up @@ -115,29 +118,29 @@ function bench(;
end

# +++ put repeat directly in bench()
function bench_average(; repeat = 2, verbose = false, kwargs...)
function bench_average(; repeat = 4, verbose = false, kwargs...)

# execute series of benchmark runs
t_list = []
for i = 1:repeat
t = bench(; verbose = verbose, kwargs...)
append!(t_list, t)
@printf("Run %d / %d: time (s) = %6.2f\n", i, repeat, t)
verbose && @printf("Run %d / %d: time (s) = %6.2f\n", i, repeat, t)
end

# print / return average total time
avg_time = sum(t_list) / length(t_list)
@printf("Average time (s): %6.2f\n", avg_time)
verbose && @printf("Average time (s): %6.2f\n", avg_time)
return avg_time
end

function bench_series(; grid_size_list = [250, 500, 1000, 2500, 5000, 10000], kwargs...)
function bench_series(; grid_size_list = [250, 500, 1000, 2500, 5000], kwargs...)
println(grid_size_list)
t_list = []
for grid_size in grid_size_list
t = bench_average(; grid_size = grid_size, kwargs...)
append!(t_list, t)
@printf("Grid size %d: time (s) = %6.2f\n\n", grid_size, t)
@printf("Grid size %d: time (s) = %6.1f\n", grid_size, t)
end
return t_list
#return t_list
end
46 changes: 26 additions & 20 deletions benchmark/prof.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# +++ TODO: make function with bools as args ?
# Profiling
include("../test/deps.jl")
using CTDirect
using CTBase

using LinearAlgebra
using NLPModelsIpopt
using BenchmarkTools
#using Traceur
#using Profile
Expand All @@ -25,20 +29,31 @@ println("Load problem ", prob[:name])

if precompile
println("Precompilation")
direct_solve(ocp, grid_size = grid_size, display = false, max_iter = 2)
CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
CTDirect.DOCP_constraints!(
zeros(docp.dim_NLP_constraints),
CTDirect.DOCP_initial_guess(docp),
docp,
)
if test_solve
direct_solve(ocp, grid_size = grid_size, display = false, max_iter = 2)
end
if test == :objective
CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
else
CTDirect.DOCP_constraints!(zeros(docp.dim_NLP_constraints), CTDirect.DOCP_initial_guess(docp), docp)
end
end

if test == :objective
println("Timed objective")
#@timev CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
@btime CTDirect.DOCP_objective(CTDirect.DOCP_initial_guess(docp), docp)
else
println("Timed constraints")
#@timev CTDirect.DOCP_constraints!(zeros(docp.dim_NLP_constraints), CTDirect.DOCP_initial_guess(docp), docp)
@btime CTDirect.DOCP_constraints!(zeros(docp.dim_NLP_constraints), CTDirect.DOCP_initial_guess(docp), docp)
end

# full solve
if test_solve
println("Timed solve")
@timev sol = direct_solve(ocp, grid_size = grid_size, display = false)
@btime sol = direct_solve(ocp, grid_size = grid_size, display = false)
println("Timed full solve")
#@timev sol = direct_solve(ocp, grid_size = grid_size, display=false)
@btime sol = direct_solve(ocp, grid_size = grid_size, display=false)
end

if test_code_warntype
Expand Down Expand Up @@ -71,12 +86,3 @@ if test_jet
end
end

#=
# ipopt statistics
if test_ipopt
docp = directTranscription(ocp, grid_size=200, init=(state=[1,0.2,0.5], control=0.5))
dsol = solve(docp, print_level=5, tol=1e-12, print_timing_statistics="yes")
println("\n Discrete solution")
println(dsol)
end
=#
4 changes: 3 additions & 1 deletion src/CTDirect.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ const matrix2vec = CTBase.matrix2vec
# includes
include("utils.jl")
include("default.jl")
include("problem.jl")
include("problem.jl") # rename as docp.jl ?
include("midpoint.jl")
include("trapeze.jl")
include("solution.jl")
include("solve.jl")

Expand Down
8 changes: 8 additions & 0 deletions src/default.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
"""
$(TYPEDSIGNATURES)
Used to set the default discretization method.
The default value is `trapeze`.
"""
__discretization() = "trapeze"

"""
$(TYPEDSIGNATURES)
Used to set the default grid size.
The default value is `250`.
"""
Expand Down
105 changes: 105 additions & 0 deletions src/gauss.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#= Functions for implicit gauss-legendre 2 discretization scheme
Internal layout for NLP variables:
[X_0,U_0,K_0, X_1,U_1,K_1 .., X_N-1,U_N-1,K_N-1, XN, V]
with the conventions U_i constant per step, K_i := [K_i1 K_i2]

NB. try both control_stage and control_step versions !
Q. if taking controls at stages, how to define the control at 'step' (for path constraints or tf). Unless we evaluate path constraints at stages, but then we have to determine the state at stages (a possibility is to take the state argument as for the stage dynamics)
=#

# Later adjust this one for generic IRK scheme ! (use stage value)
# define several tags for different methods
# use intermediate abstract type ImplicitRungeKuttaTag

struct GaussLegendre2Tag <: DiscretizationTag
stage::Int
additional_controls::Int
butcher_a
butcher_b
butcher_c
GaussLegendre2Tag() = new(2, 0, [0.25 (0.25 - sqrt(3)/6); (0.25 + sqrt(3)/6) 0.25], [0.5, 0.5], [(0.5 - sqrt(3)/6), (0.5 + sqrt(3)/6)])
end


"""
$(TYPEDSIGNATURES)

Retrieve state and control variables at given time step from the NLP variables.
"""
function get_variables_at_time_step(xu, docp, i, tag::GuassLegendre2Tag)

# block: [X_i U_i1 U_i2 K_i1 K_i2]
nx = docp.dim_NLP_x
n = docp.dim_OCP_x
m = docp.dim_NLP_u
N = docp.dim_NLP_steps
offset = (nx*3 + m) * i

# retrieve scalar/vector OCP state (w/o lagrange state)
if n == 1
xi = xu[offset + 1]
else
xi = xu[(offset + 1):(offset + n)]
end
if docp.has_lagrange
xli = xu[offset + nx]
else
xli = nothing # dummy. use xu type ?
end

# retrieve scalar/vector controls
if i < N
offset_u = offset
else
offset_u = (nx*3 + m) * (i-1)
end
if m == 1
ui = xu[offset_u + nx + 1]
else
ui = xu[(offset_u + nx + 1):(offset_u + nx + m)]
end

# retrieve vector stage variable (except at final time)
if i < N
ki = (xu[(offset + nx + m + 1):(offset + nx + m + nx)],
xu[(offset + nx*2 + m + 1):(offset + nx*2 + m + nx)])
else
ki = nothing
end

return xi, ui, xli, ki
end


# internal NLP version for solution parsing
# could be fused with one above if
# - using extended dynamics that include lagrange cost
# - scalar case is handled at OCP level
function get_NLP_variables_at_time_step(xu, docp, i, tag::GaussLegendre2Tag)

+++
nx = docp.dim_NLP_x
m = docp.dim_NLP_u
N = docp.dim_NLP_steps
offset = (nx*2 + m) * i

# state
xi = xu[(offset + 1):(offset + nx)]
# control
if i < N
offset_u = offset
else
offset_u = (nx*2 + m) * (i-1)
end
ui = xu[(offset_u + nx + 1):(offset_u + nx + m)]
# stage
if i < N
ki = xu[(offset + nx + m + 1):(offset + nx + m + nx) ]
else
ki = nothing
end

return xi, ui, ki
end


Loading
Loading