Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 44 additions & 37 deletions src/FileFormats/NL/NL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,13 @@ function MOI.supports(
return true
end

function MOI.set(model::Model, ::MOI.VariablePrimalStart, x, v::Real)
model.x[x].start = Float64(v)
return
end

function MOI.set(model::Model, ::MOI.VariablePrimalStart, x, ::Nothing)
model.x[x].start = nothing
function MOI.set(
model::Model,
::MOI.VariablePrimalStart,
x::MOI.VariableIndex,
v::Union{Nothing,Real},
)
model.x[x].start = v === nothing ? nothing : convert(Float64, v)::Float64
return
end

Expand All @@ -237,60 +237,61 @@ end
# ==============================================================================

struct _LinearNLPEvaluator <: MOI.AbstractNLPEvaluator end
MOI.features_available(::_LinearNLPEvaluator) = [:ExprGraph]
MOI.initialize(::_LinearNLPEvaluator, ::Vector{Symbol}) = nothing

function MOI.copy_to(dest::Model, model::MOI.ModelLike)
if !MOI.is_empty(dest)
MOI.empty!(dest)
end
mapping = MOI.Utilities.IndexMap()
# Initialize the NLP block.
has_nlp = MOI.NLPBlock() in MOI.get(model, MOI.ListOfModelAttributesSet())
nlp_block = if has_nlp
MOI.get(model, MOI.NLPBlock())
else
nlp_block =
MOI.NLPBlockData(MOI.NLPBoundsPair[], _LinearNLPEvaluator(), false)
end
if !(:ExprGraph in MOI.features_available(nlp_block.evaluator))
error(
"Unable to use AmplNLWriter because the nonlinear evaluator " *
"does not supply expression graphs.",
)
for attr in MOI.get(model, MOI.ListOfModelAttributesSet())
if attr == MOI.NLPBlock()
nlp_block = MOI.get(model, MOI.NLPBlock())
if !(:ExprGraph in MOI.features_available(nlp_block.evaluator))
error(
"Unable to use AmplNLWriter because the nonlinear " *
"evaluator does not supply expression graphs.",
)
end
elseif attr == MOI.ObjectiveSense()
dest.sense = MOI.get(model, MOI.ObjectiveSense())
elseif attr isa MOI.ObjectiveFunction
dest.f = _NLExpr(MOI.get(model, attr))
else
throw(MOI.UnsupportedAttribute(attr))
end
end
MOI.initialize(nlp_block.evaluator, [:ExprGraph])
# Objective function.
if nlp_block.has_objective
if nlp_block.has_objective # Nonlinear objective takes precedence.
dest.f = _NLExpr(MOI.objective_expr(nlp_block.evaluator))
else
F = MOI.get(model, MOI.ObjectiveFunctionType())
obj = MOI.get(model, MOI.ObjectiveFunction{F}())
dest.f = _NLExpr(obj)
end
# Nonlinear constraints
for (i, bound) in enumerate(nlp_block.constraint_bounds)
push!(
dest.g,
_NLConstraint(MOI.constraint_expr(nlp_block.evaluator, i), bound),
)
end
dest.nlpblock_dim = length(dest.g)
starts = MOI.supports(model, MOI.VariablePrimalStart(), MOI.VariableIndex)
for x in MOI.get(model, MOI.ListOfVariableIndices())
x_src = MOI.get(model, MOI.ListOfVariableIndices())
for x in x_src
dest.x[x] = _VariableInfo()
if starts
start = MOI.get(model, MOI.VariablePrimalStart(), x)
MOI.set(dest, MOI.VariablePrimalStart(), x, start)
end
mapping[x] = x
end
dest.sense = MOI.get(model, MOI.ObjectiveSense())
MOI.Utilities.pass_attributes(dest, model, mapping, x_src)
resize!(dest.order, length(dest.x))
# Now deal with the normal MOI constraints.
for (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
if !MOI.supports_constraint(dest, F, S)
throw(MOI.UnsupportedConstraint{F,S}())
end
_process_constraint(dest, model, F, S, mapping)
end
# Correct bounds of binary variables. Mainly because AMPL doesn't have the
# concept of binary nonlinear variables, but it does have binary linear
# variables! How annoying.
for (x, v) in dest.x
for (_, v) in dest.x
if v.type == _BINARY
v.lower = max(0.0, v.lower)
v.upper = min(1.0, v.upper)
Expand Down Expand Up @@ -438,7 +439,8 @@ function _process_constraint(
::Type{S},
mapping,
) where {F,S}
for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
ci_src = MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
for ci in ci_src
f = MOI.get(model, MOI.ConstraintFunction(), ci)
s = MOI.get(model, MOI.ConstraintSet(), ci)
op, l, u = _set_to_bounds(s)
Expand All @@ -461,6 +463,7 @@ function _process_constraint(
mapping[ci] = MOI.ConstraintIndex{F,S}(length(dest.g))
end
end
MOI.Utilities.pass_attributes(dest, model, mapping, ci_src)
return
end

Expand All @@ -471,7 +474,8 @@ function _process_constraint(
S::Type{<:_SCALAR_SETS},
mapping,
)
for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
ci_src = MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
for ci in ci_src
mapping[ci] = ci
f = MOI.get(model, MOI.ConstraintFunction(), ci)
s = MOI.get(model, MOI.ConstraintSet(), ci)
Expand All @@ -483,6 +487,7 @@ function _process_constraint(
dest.x[f].upper = u
end
end
MOI.Utilities.pass_attributes(dest, model, mapping, ci_src)
return
end

Expand All @@ -493,11 +498,13 @@ function _process_constraint(
S::Type{<:Union{MOI.ZeroOne,MOI.Integer}},
mapping,
)
for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
ci_src = MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
for ci in ci_src
mapping[ci] = ci
f = MOI.get(model, MOI.ConstraintFunction(), ci)
dest.x[f].type = S == MOI.ZeroOne ? _BINARY : _INTEGER
end
MOI.Utilities.pass_attributes(dest, model, mapping, ci_src)
return
end

Expand Down
14 changes: 13 additions & 1 deletion test/FileFormats/NL/NL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,10 @@ function test_nlmodel_hs071_linear_obj()
n = NL.Model()
@test MOI.supports(n, MOI.VariablePrimalStart(), MOI.VariableIndex)
@test MOI.supports(n, MOI.ObjectiveFunction{typeof(f)}())
MOI.copy_to(n, model)
index_map = MOI.copy_to(n, model)
for (vi, starti) in zip(v, start)
@test MOI.get(n, MOI.VariablePrimalStart(), index_map[vi]) == starti
end
@test n.sense == MOI.MAX_SENSE
@test n.f == NL._NLExpr(f)
_test_nlexpr(
Expand Down Expand Up @@ -1002,6 +1005,15 @@ function test_empty()
@test MOI.is_empty(n)
end

function test_moi()
MOI.Test.runtests(
NL.Model(),
MOI.Test.Config(exclude = Any[MOI.optimize!]),
include = ["test_model_copy_to_Unsupported"],
)
return
end

function runtests()
for name in names(@__MODULE__; all = true)
if startswith("$(name)", "test_")
Expand Down