|
function (discretizer::Collocation)(ocp::AbstractOptimalControlProblem) |
|
|
|
# common parts for builders |
|
docp = get_docp(discretizer, ocp) |
|
exa_getter = nothing # will be set in build_exa_model |
|
|
|
# ========================================================================================== |
|
# The needed builders for the construction of the final DiscretizedOptimalControlProblem |
|
# ========================================================================================== |
|
# +++ recheck kwargs passing / default with Olivier |
|
function build_adnlp_model( |
|
initial_guess::CTModels.AbstractOptimalControlInitialGuess; |
|
adnlp_backend=__adnlp_backend(), |
|
show_time=false, |
|
kwargs... |
|
)::ADNLPModels.ADNLPModel |
|
|
|
# functions for objective and constraints |
|
f = x -> CTDirect.DOCP_objective(x, docp) |
|
c! = (c, x) -> CTDirect.DOCP_constraints!(c, x, docp) |
|
|
|
# build initial guess |
|
init = get_docp_initial_guess(:adnlp, docp, initial_guess) |
|
|
|
# unused backends (option excluded_backend = [:jprod_backend, :jtprod_backend, :hprod_backend, :ghjvprod_backend] does not seem to work) |
|
unused_backends = ( |
|
hprod_backend=ADNLPModels.EmptyADbackend, |
|
jtprod_backend=ADNLPModels.EmptyADbackend, |
|
jprod_backend=ADNLPModels.EmptyADbackend, |
|
ghjvprod_backend=ADNLPModels.EmptyADbackend, |
|
) |
|
|
|
# set adnlp backends |
|
if adnlp_backend == :manual |
|
|
|
# build sparsity patterns for Jacobian and Hessian |
|
J_backend = ADNLPModels.SparseADJacobian( |
|
docp.dim_NLP_variables, f, |
|
docp.dim_NLP_constraints, c!, |
|
CTDirect.DOCP_Jacobian_pattern(docp), |
|
) |
|
H_backend = ADNLPModels.SparseReverseADHessian( |
|
docp.dim_NLP_variables, f, |
|
docp.dim_NLP_constraints, c!, |
|
CTDirect.DOCP_Hessian_pattern(docp), |
|
) |
|
backend_options = ( |
|
gradient_backend=ADNLPModels.ReverseDiffADGradient, |
|
jacobian_backend=J_backend, |
|
hessian_backend=H_backend, |
|
) |
|
else |
|
# use backend preset |
|
backend_options = (backend=adnlp_backend,) |
|
end |
|
|
|
# build NLP |
|
nlp = ADNLPModel!( |
|
f, |
|
init, |
|
docp.bounds.var_l, |
|
docp.bounds.var_u, |
|
c!, |
|
docp.bounds.con_l, |
|
docp.bounds.con_u; |
|
minimize=(!docp.flags.max), |
|
backend_options..., |
|
unused_backends..., |
|
show_time=show_time, |
|
) |
|
|
|
return nlp |
|
end |
|
|
|
# Solution builder for ADNLPModels |
|
function build_adnlp_solution(nlp_solution::SolverCore.AbstractExecutionStats) |
|
|
|
# retrieve data from NLP solver |
|
minimize = !docp.flags.max |
|
objective, iterations, constraints_violation, message, status, successful = CTModels.extract_solver_infos(nlp_solution, minimize) |
|
|
|
# retrieve time grid |
|
T = get_time_grid(nlp_solution.solution, docp) |
|
|
|
# build OCP solution from NLP solution |
|
sol = CTDirect.build_OCP_solution(docp, nlp_solution, T, |
|
objective, iterations, constraints_violation, message, status, successful) |
|
|
|
return sol |
|
end |
|
|
|
# NLP builder for ExaModels |
|
# +++ recheck kwargs passing / default with Olivier |
|
function build_exa_model( |
|
::Type{BaseType}, |
|
initial_guess::CTModels.AbstractOptimalControlInitialGuess; |
|
exa_backend=CTDirect.__exa_backend(), |
|
kwargs... |
|
)::ExaModels.ExaModel where {BaseType<:AbstractFloat} |
|
|
|
# recover discretization scheme and options |
|
# since exa part does not reuse the docp struct |
|
scheme = get_scheme(discretizer) |
|
grid_size, time_grid = grid_options(discretizer) |
|
|
|
# build initial guess |
|
init = get_docp_initial_guess(:exa, docp, initial_guess) |
|
|
|
# build Exa model and getters |
|
# +++ later try to call Exa constructor here if possible, reusing existing functions... |
|
build_exa = CTModels.get_build_examodel(ocp) |
|
nlp, exa_getter = build_exa(; |
|
grid_size=grid_size, |
|
backend=exa_backend, |
|
scheme=scheme, |
|
init=init, |
|
) |
|
|
|
return nlp |
|
end |
|
|
|
# Solution builder for ExaModels |
|
function build_exa_solution(nlp_solution::SolverCore.AbstractExecutionStats) |
|
|
|
# NB exa_getter is set during build_exa_model call ! |
|
if isnothing(exa_getter) |
|
error("build_exa_solution: exa_getter is nothing") |
|
end |
|
|
|
# retrieve data from NLP solver |
|
minimize = !docp.flags.max |
|
objective, iterations, constraints_violation, message, status, successful = CTModels.extract_solver_infos(nlp_solution, minimize) |
|
|
|
# retrieve time grid |
|
T = get_time_grid_exa(nlp_solution, docp, exa_getter) |
|
|
|
# build OCP solution from NLP solution |
|
sol = CTDirect.build_OCP_solution(docp, nlp_solution, T, |
|
objective, iterations, constraints_violation, message, status, successful; |
|
exa_getter=exa_getter) |
|
|
|
return sol |
|
end |
|
|
|
#NB. it would be better to return builders as model/solution pairs since they are linked |
|
return CTModels.DiscretizedOptimalControlProblem( |
|
ocp, |
|
CTModels.ADNLPModelBuilder(build_adnlp_model), |
|
CTModels.ExaModelBuilder(build_exa_model), |
|
CTModels.ADNLPSolutionBuilder(build_adnlp_solution), |
|
CTModels.ExaSolutionBuilder(build_exa_solution), |
|
) |
|
end |
Question: Should we use
letblocks inbuild_adnlp_model?Context
In
[CTModels.jl/src/ocp/model.jl#L165-L188](https://github.com/control-toolbox/CTModels.jl/blob/ac5a9f37bd84aa7b82f5887c4ba6b51bf758fe2f/src/ocp/model.jl#L165-L188), the code usesletblocks when creating closures:Why
letblocks?The
letblock ensures that captured variables are:Boxwrapperslet-captured variablesQuestion
In
[CTDirect.jl/src/collocation.jl#L204-L359](https://github.com/control-toolbox/CTDirect.jl/blob/da9c4e66303dc649556bea66a628b57f5d953c49/src/collocation.jl#L204-L359),build_adnlp_modelcreates closures that capturedocp:Should we apply the same pattern here for consistency and performance?
This would ensure optimal performance in the NLP solver loop and maintain consistency with the rest of the codebase.
CTDirect.jl/src/collocation.jl
Lines 207 to 359 in da9c4e6