Skip to content

Commit

Permalink
tests ok with OCPInit moved to CTBase (#87)
Browse files Browse the repository at this point in the history
* tests ok with OCPInit moved to CTBase

* todo: add dictionary for init

* compact syntax for init=... seems ok

* v0.5.0 with CTBase 0.8
  • Loading branch information
PierreMartinon authored May 3, 2024
1 parent e756855 commit 8f63f7a
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 113 deletions.
4 changes: 2 additions & 2 deletions 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 <[email protected]>"]
version = "0.4.6"
version = "0.5.0"

[deps]
ADNLPModels = "54578032-b7ea-4c30-94aa-7cbd1cce6c9a"
Expand All @@ -14,6 +14,6 @@ Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"

[compat]
ADNLPModels = "0.7"
CTBase = "0.7"
CTBase = "0.8"
DocStringExtensions = "0.9"
julia = "1.9"
8 changes: 4 additions & 4 deletions docs/src/continuation.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ nothing # hide
Then we perform the continuation with a simple *for* loop, using each solution to initialize the next problem.

```@example main
init1 = OptimalControlInit()
init1 = OCPInit()
iter_list = []
for T=1:5
ocp1 = ocp_T(T)
sol1 = solve(ocp1, print_level=0, init=init1)
global init1 = OptimalControlInit(sol1)
global init1 = OCPInit(sol1)
@printf("T %.2f objective %.6f iterations %d\n", T, sol1.objective, sol1.iterations)
push!(iter_list, sol1.iterations)
end
Expand Down Expand Up @@ -101,7 +101,7 @@ Tmax_list = []
obj_list = []
for Tmax_local=3.5:-0.5:1
global Tmax = Tmax_local
global sol = solve(ocp, print_level=0, init=OptimalControlInit(sol))
global sol = solve(ocp, print_level=0, init=OCPInit(sol))
@printf("Tmax %.2f objective %.6f iterations %d\n", Tmax, sol.objective, sol.iterations)
push!(Tmax_list, Tmax)
push!(obj_list, sol.objective)
Expand Down Expand Up @@ -134,7 +134,7 @@ for vmax=0.15:-0.01:0.05
print(vmax," ")
remove_constraint!(ocp, :speed_limit)
constraint!(ocp, :state, Index(2), 0, vmax, :speed_limit)
global sol = solve(ocp, print_level=0, init=OptimalControlInit(sol))
global sol = solve(ocp, print_level=0, init=OCPInit(sol))
push!(vmax_list, vmax)
push!(obj_list, sol.objective)
push!(iter_list, sol.iterations)
Expand Down
14 changes: 7 additions & 7 deletions docs/src/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,29 +78,29 @@ Let us start with the simplest case, constant initialisation.
x_const = [1.05, 0.2, 0.8]
u_const = 0.5
v_const = 0.15
init1 = OptimalControlInit(x_init=x_const, u_init=u_const, v_init=v_const)
init1 = OCPInit(state=x_const, control=u_const, variable=v_const)
sol2 = solve(ocp, print_level=0, init=init1)
println("Objective ", sol2.objective, " after ", sol2.iterations, " iterations")
```

Now we illustrate the functional initialisation, with some random functions. Note that we only consider the state and control variables, since the optimization variables are scalar and therefore a functional initialisation is not relevant. In the example notice that the call to **OptimalControlInit** does not provide an argument for the optimization variables, therefore the default initial guess will be used.
Now we illustrate the functional initialisation, with some random functions. Note that we only consider the state and control variables, since the optimization variables are scalar and therefore a functional initialisation is not relevant. In the example notice that the call to **OCPInit** does not provide an argument for the optimization variables, therefore the default initial guess will be used.
```@example main
x_func = t->[1+t^2, sqrt(t), 1-t]
u_func = t->(cos(t)+1)*0.5
init2 = OptimalControlInit(x_init=x_func, u_init=u_func)
init2 = OCPInit(state=x_func, control=u_func)
sol3 = solve(ocp, print_level=0, init=init2)
println("Objective ", sol3.objective, " after ", sol3.iterations, " iterations")
```
More generally, the default, constant and functional initialisations can be mixed, as shown in the example below that uses a functional initial guess for the state, a constant initial guess for the control, and the default initial guess for the optimization variables.
```@example main
init3 = OptimalControlInit(x_init=x_func, u_init=u_const)
init3 = OCPInit(state=x_func, control=u_const)
sol4 = solve(ocp, print_level=0, init=init3)
println("Objective ", sol4.objective, " after ", sol4.iterations, " iterations")
```

Finally, we can also use a so-called *warmstart* strategy and use an existing solution as initial guess (note that the OCP solution returned by the **solve** call is functional, thus it is not necessary to use the same time grid). Notice that the objective and constraint violation values start much closer to the solution than with the previous initialisations.
```@example main
init4 = OptimalControlInit(sol1)
init4 = OCPInit(sol1)
sol4 = solve(ocp, grid_size=200, print_level=5, init=init4)
nothing # hide
```
Expand All @@ -118,12 +118,12 @@ nothing # hide
```
The initial guess can be passed to **solve** same as before.
```@example main
sol6 = solve(docp, print_level=0, init=OptimalControlInit(sol1))
sol6 = solve(docp, print_level=0, init=OCPInit(sol1))
println("Objective ", sol6.objective, " after ", sol6.iterations, " iterations")
```
Another possibility is to set the initial guess associated to the DOCP, using the function **setDOCPInit**.
```@example main
setDOCPInit(docp, OptimalControlInit(sol1))
setDOCPInit(docp, OCPInit(sol1))
sol7 = solve(docp, print_level=5)
nothing # hide
```
11 changes: 5 additions & 6 deletions src/CTDirect.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ using NLPModelsIpopt # NLP solver
using LinearAlgebra # norm

# Other declarations
const nlp_constraints = CTBase.nlp_constraints
const __grid_size_direct() = 100
const __print_level_ipopt = CTBase.__print_level_ipopt
const __mu_strategy_ipopt = CTBase.__mu_strategy_ipopt
const __display = CTBase.__display
const nlp_constraints = CTBase.nlp_constraints
const matrix2vec = CTBase.matrix2vec

# includes
include("init.jl")
#include("init.jl")
include("utils.jl")
include("problem.jl")
include("solution.jl")
Expand All @@ -26,14 +26,11 @@ include("solve.jl")
export available_methods
export is_solvable
export solve
export OptimalControlInit
#export OptimalControlInit
export directTranscription
export getNLP
export setDOCPInit
export OCPSolutionFromDOCP
#export initial_guess #remove ?
#export DOCP_objective #remove ?
#export DOCP_constraints! #remove ?

# CTBase reexports
export @def
Expand All @@ -46,5 +43,7 @@ export time!
export constraint!
export dynamics!
export objective!
export remove_constraint!
export OCPInit

end
File renamed without changes.
44 changes: 33 additions & 11 deletions src/solve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,22 @@ Discretize an optimal control problem into a nonlinear optimization problem (ie
"""
function directTranscription(ocp::OptimalControlModel,
description...;
init::OptimalControlInit=OptimalControlInit(),
init=OCPInit(),
grid_size::Integer=__grid_size_direct())

# initialization is optional
docp = DOCP(ocp, grid_size)
x0 = initial_guess(docp, init)

# build init data if needed
if !(init isa OCPInit)
init = OCPInit(init)
end

# set initial guess and bounds
x0 = DOCP_initial_guess(docp, init)
docp.var_l, docp.var_u = variables_bounds(docp)
docp.con_l, docp.con_u = constraints_bounds(docp)

# call NLP problem constructor
docp.nlp = ADNLPModel!(x -> DOCP_objective(x, docp),
x0,
docp.var_l, docp.var_u,
Expand Down Expand Up @@ -56,9 +64,17 @@ $(TYPEDSIGNATURES)
Extract the NLP problem from the DOCP
"""
function setDOCPInit(docp::DOCP, init::OptimalControlInit)
function setDOCPInit(docp::DOCP, init)

nlp = getNLP(docp)
nlp.meta.x0 .= initial_guess(docp, init)

# build init data if needed
if !(init isa OCPInit)
init = OCPInit(init)
end

nlp.meta.x0 .= DOCP_initial_guess(docp, init)

end


Expand All @@ -79,7 +95,11 @@ function solve(docp::DOCP;
if init == nothing
docp_solution = ipopt(getNLP(docp), print_level=print_level, mu_strategy=mu_strategy, sb="yes"; kwargs...)
else
docp_solution = ipopt(getNLP(docp),x0=initial_guess(docp, init), print_level=print_level, mu_strategy=mu_strategy, sb="yes"; kwargs...)
# build init data if needed
if !(init isa OCPInit)
init = OCPInit(init)
end
docp_solution = ipopt(getNLP(docp),x0=DOCP_initial_guess(docp, init), print_level=print_level, mu_strategy=mu_strategy, sb="yes"; kwargs...)
end

# return solution for original OCP
Expand All @@ -94,19 +114,21 @@ Solve an optimal control problem OCP by direct method
"""
function solve(ocp::OptimalControlModel,
description...;
init::Union{OptimalControlInit, OptimalControlSolution}=OptimalControlInit(),
init=OCPInit(),
grid_size::Integer=__grid_size_direct(),
display::Bool=__display(),
print_level::Integer=__print_level_ipopt(),
mu_strategy::String=__mu_strategy_ipopt(),
kwargs...)

# build init if needed
if init isa OptimalControlSolution
init = OptimalControlInit(init)
end
# build init data if needed
if !(init isa OCPInit)
init = OCPInit(init)
end

# build discretized OCP
docp = directTranscription(ocp, description, init=init, grid_size=grid_size)

# solve DOCP and retrieve OCP solution
ocp_solution = solve(docp; display=display, print_level=print_level, mu_strategy=mu_strategy, kwargs...)

Expand Down
2 changes: 1 addition & 1 deletion src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ $(TYPEDSIGNATURES)
Build initial guess for discretized problem
"""
function initial_guess(docp, init::OptimalControlInit=OptimalControlInit())
function DOCP_initial_guess(docp, init::OCPInit=OCPInit())

# default initialization
# note: internal variables (lagrange cost, k_i for RK schemes) will keep these default values
Expand Down
8 changes: 4 additions & 4 deletions test/suite/continuation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ function ocp_T(T)
return ocp
end
@testset verbose = true showtiming = true ":parametric_ocp :warm_start" begin
init = OptimalControlInit()
init = OCPInit()
obj_list = []
iter_list = []
for T=1:5
ocp = ocp_T(T)
sol = solve(ocp, print_level=0, init=init)
init = OptimalControlInit(sol)
init = OCPInit(sol)
push!(obj_list, sol.objective)
push!(iter_list, sol.iterations)
end
Expand Down Expand Up @@ -61,13 +61,13 @@ function myocp(ρ)
return ocp
end
@testset verbose = true showtiming = true ":parametric_ocp :warm_start" begin
init = OptimalControlInit()
init = OCPInit()
obj_list = []
iter_list = []
for ρ in [0.1, 5, 10, 30, 100]
ocp = myocp(ρ)
sol = solve(ocp, print_level=0, init=init)
init = OptimalControlInit(sol)
init = OCPInit(sol)
push!(obj_list, sol.objective)
push!(iter_list, sol.iterations)
end
Expand Down
Loading

0 comments on commit 8f63f7a

Please sign in to comment.