Skip to content

Re-use initialised AD if bounds change #515

@odow

Description

@odow

See this discussion: JuliaControl/ModelPredictiveControl.jl#275

Currently we recreate the problem if things are modified:

function _setup_model(model::Optimizer)
vars = MOI.get(model.variables, MOI.ListOfVariableIndices())
if isempty(vars)
# Don't attempt to create a problem because Ipopt will error.
model.invalid_model = true
return
end
if model.nlp_model !== nothing
model.nlp_data = MOI.NLPBlockData(
MOI.Nonlinear.Evaluator(model.nlp_model, model.ad_backend, vars),
)
end
has_quadratic_constraints =
any(isequal(_kFunctionTypeScalarQuadratic), model.qp_data.function_type)
has_nlp_constraints =
!isempty(model.nlp_data.constraint_bounds) ||
!isempty(model.vector_nonlinear_oracle_constraints)
has_hessian = :Hess in MOI.features_available(model.nlp_data.evaluator)
for (_, s) in model.vector_nonlinear_oracle_constraints
if s.set.eval_hessian_lagrangian === nothing
has_hessian = false
break
end
end
init_feat = [:Grad]
if has_hessian
push!(init_feat, :Hess)
end
if has_nlp_constraints
push!(init_feat, :Jac)
end
MOI.initialize(model.nlp_data.evaluator, init_feat)
jacobian_sparsity = MOI.jacobian_structure(model)
hessian_sparsity = if has_hessian
MOI.hessian_lagrangian_structure(model)
else
Tuple{Int,Int}[]
end
eval_f_cb(x) = MOI.eval_objective(model, x)
eval_grad_f_cb(x, grad_f) = MOI.eval_objective_gradient(model, grad_f, x)
eval_g_cb(x, g) = MOI.eval_constraint(model, g, x)
function eval_jac_g_cb(x, rows, cols, values)
if values === nothing
for i in 1:length(jacobian_sparsity)
rows[i], cols[i] = jacobian_sparsity[i]
end
else
MOI.eval_constraint_jacobian(model, values, x)
end
return
end
function eval_h_cb(x, rows, cols, obj_factor, lambda, values)
if values === nothing
for i in 1:length(hessian_sparsity)
rows[i], cols[i] = hessian_sparsity[i]
end
else
MOI.eval_hessian_lagrangian(model, values, x, obj_factor, lambda)
end
return
end
g_L, g_U = copy(model.qp_data.g_L), copy(model.qp_data.g_U)
for (_, s) in model.vector_nonlinear_oracle_constraints
append!(g_L, s.set.l)
append!(g_U, s.set.u)
end
for bound in model.nlp_data.constraint_bounds
push!(g_L, bound.lower)
push!(g_U, bound.upper)
end
model.inner = Ipopt.CreateIpoptProblem(
length(vars),
model.variables.lower,
model.variables.upper,
length(g_L),
g_L,
g_U,
length(jacobian_sparsity),
length(hessian_sparsity),
eval_f_cb,
eval_g_cb,
eval_grad_f_cb,
eval_jac_g_cb,
has_hessian ? eval_h_cb : nothing,
)

but we need to re-initialize the AD engine only if the scalar nonlinear stuff has changed. We could skip most of _setup_model and just call a new CreateIpoptProblem with the new bounds.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions