Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Aug 21, 2024
1 parent 7f07d1b commit 094057f
Showing 1 changed file with 99 additions and 173 deletions.
272 changes: 99 additions & 173 deletions ext/NLoptMathOptInterfaceExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,6 @@ MOI.get(::Optimizer, ::MOI.SolverName) = "NLopt"

MOI.get(::Optimizer, ::MOI.SolverVersion) = "$(NLopt.version())"

const _F_TYPES = Union{
MOI.ScalarAffineFunction{Float64},
MOI.ScalarQuadraticFunction{Float64},
}

function _constraints(
model,
::Type{<:MOI.ScalarAffineFunction},
Expand Down Expand Up @@ -215,7 +210,12 @@ end

function MOI.supports_constraint(
::Optimizer,
::Type{<:_F_TYPES},
::Type{
<:Union{
MOI.ScalarAffineFunction{Float64},
MOI.ScalarQuadraticFunction{Float64},
},
},
::Type{<:Union{MOI.LessThan{Float64},MOI.EqualTo{Float64}}},
)
return true
Expand All @@ -224,7 +224,13 @@ end
function MOI.get(
model::Optimizer,
::MOI.NumberOfConstraints{F,S},
) where {F<:_F_TYPES,S}
) where {
F<:Union{
MOI.ScalarAffineFunction{Float64},
MOI.ScalarQuadraticFunction{Float64},
},
S,
}
return length(_constraints(model, F, S))
end

Expand All @@ -245,23 +251,41 @@ end
function MOI.get(
model::Optimizer,
::MOI.ListOfConstraintIndices{F,S},
) where {F<:_F_TYPES,S}
) where {
F<:Union{
MOI.ScalarAffineFunction{Float64},
MOI.ScalarQuadraticFunction{Float64},
},
S,
}
return MOI.ConstraintIndex{F,S}.(eachindex(_constraints(model, F, S)))
end

function MOI.get(
model::Optimizer,
::MOI.ConstraintFunction,
c::MOI.ConstraintIndex{F,S},
) where {F<:_F_TYPES,S}
) where {
F<:Union{
MOI.ScalarAffineFunction{Float64},
MOI.ScalarQuadraticFunction{Float64},
},
S,
}
return copy(_constraints(model, F, S)[c.value].func)
end

function MOI.get(
model::Optimizer,
::MOI.ConstraintSet,
c::MOI.ConstraintIndex{F,S},
) where {F<:_F_TYPES,S}
) where {
F<:Union{
MOI.ScalarAffineFunction{Float64},
MOI.ScalarQuadraticFunction{Float64},
},
S,
}
return _constraints(model, F, S)[c.value].set
end

Expand Down Expand Up @@ -322,7 +346,7 @@ const _DEFAULT_OPTIONS = Dict{String,Any}(
"xtol_abs" => nothing,
"constrtol_abs" => 1e-7,
"maxeval" => 0,
"maxtime" => 0.0,
"maxtime" => 10.0,
"initial_step" => nothing,
"population" => 0,
"seed" => nothing,
Expand Down Expand Up @@ -474,79 +498,21 @@ function _check_inbounds(
return
end

function _fill_jacobian_terms(
jac::Matrix,
x,
row,
terms::Vector{MOI.ScalarAffineTerm{Float64}},
)
for term in terms
jac[term.variable.value, row] += term.coefficient
end
return
end

function _fill_jacobian_terms(
jac::Matrix,
x,
row,
terms::Vector{MOI.ScalarQuadraticTerm{Float64}},
)
for term in terms
jac[term.variable_1.value, row] +=
term.coefficient * x[term.variable_2.value]
if term.variable_1 != term.variable_2
jac[term.variable_2.value, row] +=
term.coefficient * x[term.variable_1.value]
end
end
return
end

function _fill_jacobian(
jac::Matrix,
x,
offset,
constraints::Vector{<:_ConstraintInfo{MOI.ScalarAffineFunction{Float64}}},
)
for (k, con) in enumerate(constraints)
_fill_jacobian_terms(jac, x, offset + k, con.func.terms)
end
return
end

function _fill_jacobian(
jac::Matrix,
x,
offset,
constraints::Vector{
<:_ConstraintInfo{MOI.ScalarQuadraticFunction{Float64}},
},
)
for (k, con) in enumerate(constraints)
_fill_jacobian_terms(jac, x, offset + k, con.func.affine_terms)
_fill_jacobian_terms(jac, x, offset + k, con.func.quadratic_terms)
end
return
end

function _fill_result(result::Vector, x, offset, constraints::Vector)
for (k, con) in enumerate(constraints)
lhs = MOI.Utilities.eval_variables(vi -> x[vi.value], con.func)
result[offset+k] = lhs - MOI.constant(con.set)
end
return
end

function MOI.add_constraint(
model::Optimizer,
func::_F_TYPES,
set::Union{MOI.LessThan{Float64},MOI.EqualTo{Float64}},
)
func::F,
set::S,
) where {
F<:Union{
MOI.ScalarAffineFunction{Float64},
MOI.ScalarQuadraticFunction{Float64},
},
S<:Union{MOI.LessThan{Float64},MOI.EqualTo{Float64}},
}
_check_inbounds(model, func)
constraints = _constraints(model, typeof(func), typeof(set))
constraints = _constraints(model, F, S)
push!(constraints, _ConstraintInfo(func, set))
return MOI.ConstraintIndex{typeof(func),typeof(set)}(length(constraints))
return MOI.ConstraintIndex{F,S}(length(constraints))
end

# MOI.VariablePrimalStart
Expand Down Expand Up @@ -623,21 +589,21 @@ function MOI.set(
return
end

function _fill_gradient!(grad, x, var::MOI.VariableIndex)
function _fill_gradient(grad, x, var::MOI.VariableIndex)
fill!(grad, 0.0)
grad[var.value] = 1.0
return
end

function _fill_gradient!(grad, x, aff::MOI.ScalarAffineFunction{Float64})
function _fill_gradient(grad, x, aff::MOI.ScalarAffineFunction{Float64})
fill!(grad, 0.0)
for term in aff.terms
grad[term.variable.value] += term.coefficient
end
return
end

function _fill_gradient!(grad, x, quad::MOI.ScalarQuadraticFunction{Float64})
function _fill_gradient(grad, x, quad::MOI.ScalarQuadraticFunction{Float64})
fill!(grad, 0.0)
for term in quad.affine_terms
grad[term.variable.value] += term.coefficient
Expand All @@ -655,20 +621,66 @@ function _fill_gradient!(grad, x, quad::MOI.ScalarQuadraticFunction{Float64})
return
end

function _fill_result(result::Vector, x, offset, constraints::Vector)
for (k, con) in enumerate(constraints)
lhs = MOI.Utilities.eval_variables(vi -> x[vi.value], con.func)
result[offset+k] = lhs - MOI.constant(con.set)
end
return
end

function _fill_jacobian(jac, x, row, term::MOI.ScalarAffineTerm)
jac[term.variable.value, row] += term.coefficient
return
end

function _fill_jacobian(jac, x, row, term::MOI.ScalarQuadraticTerm)
i, j = term.variable_1.value, term.variable_2.value
jac[i, row] += term.coefficient * x[j]
if i != j
jac[j, row] += term.coefficient * x[i]
end
return
end

function _fill_jacobian(jac, x, offset, f::MOI.ScalarAffineFunction)
for term in f.terms
_fill_jacobian(jac, x, offset, term)
end
return
end

function _fill_jacobian(jac, x, offset, f::MOI.ScalarQuadraticFunction)
for term in f.affine_terms
_fill_jacobian(jac, x, offset, term)
end
for q_term in f.quadratic_terms
_fill_jacobian(jac, x, offset, q_term)
end
return
end

function _fill_jacobian(jac, x, offset, constraints::Vector)
for (k, con) in enumerate(constraints)
_fill_jacobian(jac, x, offset + k, con.func)
end
return
end

function objective_fn(model::Optimizer, x::Vector, grad::Vector)
if length(grad) > 0
fill!(grad, 0.0)
end
# If we don't give any objective to NLopt, it throws the error:
# invalid NLopt arguments: NULL args to nlopt_optimize
if model.sense == MOI.FEASIBILITY_SENSE
fill!(grad, 0.0)
return 0.0
end
if length(grad) > 0
if model.nlp_data.has_objective
MOI.eval_objective_gradient(model.nlp_data.evaluator, grad, x)
elseif model.objective !== nothing
_fill_gradient!(grad, x, model.objective)
else
fill!(grad, 0.0)
_fill_gradient(grad, x, model.objective)
end
end
# The order of the conditions is important. NLP objectives override regular
Expand All @@ -682,92 +694,6 @@ function objective_fn(model::Optimizer, x::Vector, grad::Vector)
return 0.0
end

function _eval_constraint(model::Optimizer, g, x)
row = 1
for info in model.linear_le_constraints
g[row] = eval_function(info.func, x)
row += 1
end
for info in model.linear_eq_constraints
g[row] = eval_function(info.func, x)
row += 1
end
for info in model.quadratic_le_constraints
g[row] = eval_function(info.func, x)
row += 1
end
for info in model.quadratic_eq_constraints
g[row] = eval_function(info.func, x)
row += 1
end
nlp_g = view(g, row:length(g))
MOI.eval_constraint(model.nlp_data.evaluator, nlp_g, x)
return
end

function _fill_constraint_jacobian!(
values,
start_offset,
x,
aff::MOI.ScalarAffineFunction{Float64},
)
num_coefficients = length(aff.terms)
for i in 1:num_coefficients
values[start_offset+i] = aff.terms[i].coefficient
end
return num_coefficients
end

function _fill_constraint_jacobian!(
values,
start_offset,
x,
quad::MOI.ScalarQuadraticFunction{Float64},
)
num_affine_coefficients = length(quad.affine_terms)
for i in 1:num_affine_coefficients
values[start_offset+i] = quad.affine_terms[i].coefficient
end
num_quadratic_coefficients = 0
for term in quad.quadratic_terms
row_idx = term.variable_1
col_idx = term.variable_2
coefficient = term.coefficient
if row_idx == col_idx
values[start_offset+num_affine_coefficients+num_quadratic_coefficients+1] =
coefficient * x[col_idx.value]
num_quadratic_coefficients += 1
else
# Note that the order matches the Jacobian sparsity pattern.
values[start_offset+num_affine_coefficients+num_quadratic_coefficients+1] =
coefficient * x[col_idx.value]
values[start_offset+num_affine_coefficients+num_quadratic_coefficients+2] =
coefficient * x[row_idx.value]
num_quadratic_coefficients += 2
end
end
return num_affine_coefficients + num_quadratic_coefficients
end

function _eval_constraint_jacobian(model::Optimizer, values, x)
offset = 0
for info_1 in model.linear_le_constraints
offset += _fill_constraint_jacobian!(values, offset, x, info_1.func)
end
for info_2 in model.linear_eq_constraints
offset += _fill_constraint_jacobian!(values, offset, x, info_2.func)
end
for info_3 in model.quadratic_le_constraints
offset += _fill_constraint_jacobian!(values, offset, x, info_3.func)
end
for info_4 in model.quadratic_eq_constraints
offset += _fill_constraint_jacobian!(values, offset, x, info_4.func)
end
nlp_values = view(values, 1+offset:length(values))
MOI.eval_constraint_jacobian(model.nlp_data.evaluator, nlp_values, x)
return
end

function _initialize_options!(model::Optimizer)
local_optimizer = model.options["local_optimizer"]
if local_optimizer !== nothing
Expand Down

0 comments on commit 094057f

Please sign in to comment.