Skip to content

Commit

Permalink
getNLP
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreMartinon committed Mar 21, 2024
1 parent 1441006 commit b4d2563
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 118 deletions.
3 changes: 1 addition & 2 deletions src/CTDirect.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ include("solution.jl")
include("solve.jl")

# export functions only for user
export solve
export is_solvable
export OptimalControlInit
export DirectTranscription
export getNLP
export solveDOCP
#export setDOCPInitialGuess()
export initial_guess
export ipopt_objective
export ipopt_constraint
Expand Down
6 changes: 3 additions & 3 deletions src/problem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ mutable struct DOCP
dim_NLP_steps::Int64

# initialization
NLP_init
#NLP_init

# NLP solution
NLP_solution
Expand All @@ -72,7 +72,7 @@ mutable struct DOCP
nlp

# constructor
function DOCP(ocp::OptimalControlModel, N::Integer, init::OptimalControlInit)
function DOCP(ocp::OptimalControlModel, N::Integer)

# +++ try to put here more const members (indicators etc)
docp = new(ocp)
Expand Down Expand Up @@ -114,7 +114,7 @@ mutable struct DOCP

## Non Linear Programming NLP
docp.dim_NLP_steps = N
docp.NLP_init = init
#docp.NLP_init = init

# Mayer to Lagrange reformulation:
# additional state with Lagrange cost as dynamics and null initial condition
Expand Down
111 changes: 10 additions & 101 deletions src/solve.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# +++ TODO
# add transcription function ocp -> docp
# add function to set the intial guess for a docp
# introduce new struct docp to replace most of CTData
# replace solve with a solvedocp that would go to CTBase later

# TODO
# add function to set the intial guess for a docp: need to rebuild the nlp model completely ?

# availble methods by order of preference: from top to bottom
algorithmes = ()
Expand Down Expand Up @@ -31,21 +27,24 @@ function DirectTranscription(ocp::OptimalControlModel,
grid_size::Integer=__grid_size_direct())

# initialization is optional
docp = DOCP(ocp, grid_size, init)
xu0 = initial_guess(docp)
docp = DOCP(ocp, grid_size)
x0 = initial_guess(docp, init)
l_var, u_var = variables_bounds(docp)
lb, ub = constraints_bounds(docp)
docp.nlp = ADNLPModel!(xu -> ipopt_objective(xu, docp),
xu0,
docp.nlp = ADNLPModel!(x -> ipopt_objective(x, docp),
x0,
l_var, u_var,
(c, xu) -> ipopt_constraint!(c, xu, docp),
(c, x) -> ipopt_constraint!(c, x, docp),
lb, ub,
backend = :optimized)

return docp

end

function getNLP(docp::DOCP)
return docp.nlp

Check warning on line 46 in src/solve.jl

View check run for this annotation

Codecov / codecov/patch

src/solve.jl#L45-L46

Added lines #L45 - L46 were not covered by tests
end

"""
$(TYPEDSIGNATURES)
Expand All @@ -72,93 +71,3 @@ function solveDOCP(docp::DOCP;

return sol
end



# +++ to be deprecated
"""
$(TYPEDSIGNATURES)
Solve the the optimal control problem `ocp` by the method given by the (optional) description.
Return an
[`OptimalControlSolution`](https://control-toolbox.org/docs/ctbase/stable/api-types.html#CTBase.OptimalControlSolution)
from [`CTBase`](https://github.com/control-toolbox/CTBase.jl) package, that is an approximation of
the optimal solution if the method has converged correctly.
# The (optional) description
You can pass a partial description.
If you give a partial description, then, if several complete descriptions contains the partial one,
then, the method with the highest priority is chosen. The higher in the list, the higher is the priority.
Keyword arguments:
- `display`: print or not information during the resolution
- `init`: an initial condition for the solver
- `grid_size`: number of time steps for the discretization
- `print_level`: print level for the `Ipopt` solver
- `mu_strategy`: mu strategy for the `Ipopt` solver
!!! warning
There is only one available method for the moment: the direct method transforms
the optimal control problem into a nonlinear programming problem (NLP) solved
by [`Ipopt`](https://coin-or.github.io/Ipopt/), thanks to the package
[`ADNLPModels`](https://github.com/JuliaSmoothOptimizers/ADNLPModels.jl).
!!! tip
- To see the list of available methods, simply call `available_methods()`.
- You can pass any other option by a pair `keyword=value` according to the chosen method. See for instance, [`Ipopt` options](https://coin-or.github.io/Ipopt/OPTIONS.html).
- The default values for the keyword arguments are given [here](https://control-toolbox.org/CTDocs.jl/ctbase/stable/api-default.html).
```@examples
julia> solve(ocp)
julia> solve(ocp, :adnlp)
julia> solve(ocp, :adnlp, :ipopt)
julia> solve(ocp, display=false, init=OptimalControlInit(), grid_size=100, print_level=0, mu_strategy="adaptive")
```
"""
function solve(ocp::OptimalControlModel,
description...;
display::Bool=__display(),
init::OptimalControlInit=OptimalControlInit(),
grid_size::Integer=__grid_size_direct(),
print_level::Integer=__print_level_ipopt(),
mu_strategy::String=__mu_strategy_ipopt(),
kwargs...)

# get full description from partial
# throw error if description is not valid
# thus, no else is needed below
method = getFullDescription(description, available_methods())

# Model: from ocp to nlp
if :adnlp in method
ctd = CTDirect_data(ocp, grid_size, init)
xu0 = initial_guess(ctd)
l_var, u_var = variables_bounds(ctd)
lb, ub = constraints_bounds(ctd)
nlp = ADNLPModel!(xu -> ipopt_objective(xu, ctd),
xu0,
l_var, u_var,
(c, xu) -> ipopt_constraint!(c, xu, ctd),
lb, ub,
backend = :optimized)
end

# solve
if :ipopt in method
# https://github.com/JuliaSmoothOptimizers/NLPModelsIpopt.jl/blob/main/src/NLPModelsIpopt.jl#L119
# callback: https://github.com/jump-dev/Ipopt.jl#solver-specific-callback
# sb="yes": remove ipopt header +++ make that default
print_level = display ? print_level : 0
ipopt_solution = ipopt(nlp, print_level=print_level, mu_strategy=mu_strategy, sb="yes"; kwargs...)
end

# from NLP to OCP: call OptimaControlSolution constructor
sol = _OptimalControlSolution(ocp, ipopt_solution, ctd)

return sol

end
18 changes: 9 additions & 9 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -121,33 +121,33 @@ function set_variable!(xu, v_init, docp)
end
end

function initial_guess(docp)
function initial_guess(docp, init::OptimalControlInit=OptimalControlInit())

# default initialization
# note: internal variables (lagrange cost, k_i for RK schemes) will keep these default values
xu0 = 0.1 * ones(docp.dim_NLP_variables)
xuv = 0.1 * ones(docp.dim_NLP_variables)

init = docp.NLP_init
#init = docp.NLP_init
N = docp.dim_NLP_steps
t0 = get_initial_time(xu0, docp)
tf = get_final_time(xu0, docp)
t0 = get_initial_time(xuv, docp)
tf = get_final_time(xuv, docp)
h = (tf - t0) / N

# set state / control variables if provided
for i in 0:N
ti = t0 + i * h
if !isnothing(init.state_init(ti))
set_state_at_time_step!(xu0, init.state_init(ti), docp, i)
set_state_at_time_step!(xuv, init.state_init(ti), docp, i)
end
if !isnothing(init.control_init(ti))
set_control_at_time_step!(xu0, init.control_init(ti), docp, i)
set_control_at_time_step!(xuv, init.control_init(ti), docp, i)
end

# set variables if provided
if !isnothing(init.variable_init)
set_variable!(xu0, init.variable_init, docp)
set_variable!(xuv, init.variable_init, docp)
end
end

return xu0
return xuv
end
17 changes: 14 additions & 3 deletions test/test_export_docp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,22 @@ constraint!(ocp1, :final, 0, :final_constraint)
dynamics!(ocp1, (x, u) -> -x + u)
objective!(ocp1, :lagrange, (x, u) -> u^2)

println("Test simple integrator: new formulation with export")
println("Test simple integrator: basic init")
docp = DirectTranscription(ocp1, grid_size=100)
sol = solveDOCP(docp, print_level=0, tol=1e-12)
nlp = getNLP(docp)
#print(nlp.meta.x0)
sol = solveDOCP(docp, print_level=5, tol=1e-12)
println("Expected Objective 0.313, found ", sol.objective)

# different starting guess
println("with constant init x=0.5 and u=0")
init_constant = OptimalControlInit(x_init=[-0.5], u_init=0)
docp = DirectTranscription(ocp1, grid_size=100, init=init_constant)
#print(docp.nlp.meta.x0)
sol = solveDOCP(docp, print_level=5, tol=1e-12)
println("Expected Objective 0.313, found ", sol.objective)



# check types on objective and constraints functions
#xu = initial_guess(docp)
#@code_warntype ipopt_objective(xu, docp)

0 comments on commit b4d2563

Please sign in to comment.