Skip to content

Commit

Permalink
Fix copy_to(::Model, src) when src is nonlinear (#3101)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Oct 8, 2022
1 parent 8b774fd commit 1a5b487
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 1 deletion.
56 changes: 55 additions & 1 deletion src/copy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -296,5 +296,59 @@ function MOI.copy_to(dest::MOI.ModelLike, src::Model)
end

function MOI.copy_to(dest::Model, src::MOI.ModelLike)
return MOI.copy_to(backend(dest), src)
index_map = MOI.copy_to(backend(dest), src)
if MOI.NLPBlock() in MOI.get(src, MOI.ListOfModelAttributesSet())
block = MOI.get(src, MOI.NLPBlock())
dest.nlp_model = _nlp_model_from_nlpblock(block, block.evaluator)
end
return index_map
end

_lift_variable_from_expression(expr) = expr

function _lift_variable_from_expression(expr::Expr)
if Meta.isexpr(expr, :ref, 2) && expr.args[1] == :x
return expr.args[2]
end
for i in 1:length(expr.args)
expr.args[i] = _lift_variable_from_expression(expr.args[i])
end
return expr
end

function _set_from_nlpboundspair(bound::MOI.NLPBoundsPair)
if bound.lower == bound.upper
return MOI.EqualTo(bound.lower)
elseif isfinite(bound.lower) && !isfinite(bound.upper)
return MOI.GreaterThan(bound.lower)
elseif isfinite(bound.upper) && !isfinite(bound.lower)
return MOI.LessThan(bound.upper)
else
return MOI.Interval(bound.lower, bound.upper)
end
end

function _nlp_model_from_nlpblock(block::MOI.NLPBlockData, evaluator)
if !(:ExprGraph in MOI.features_available(evaluator))
error(
"Unable to copy model because the nonlinear evaluator doesn't " *
"support :ExprGraph.",
)
end
MOI.initialize(evaluator, [:ExprGraph])
model = MOI.Nonlinear.Model()
for (i, bound) in enumerate(block.constraint_bounds)
expr = MOI.constraint_expr(evaluator, i)
f = Meta.isexpr(expr, :comparison) ? expr.args[3] : expr.args[2]
MOI.Nonlinear.add_constraint(
model,
_lift_variable_from_expression(f),
_set_from_nlpboundspair(bound),
)
end
if block.has_objective
expr = _lift_variable_from_expression(MOI.objective_expr(evaluator))
MOI.Nonlinear.set_objective(model, expr)
end
return model
end
31 changes: 31 additions & 0 deletions test/file_formats.jl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,37 @@ function test_unsupported_attribute()
return
end

function test_nl_round_trip()
model = Model()
@variable(model, 0 <= x <= 1)
@constraint(model, x^2 <= 2)
@objective(model, Max, x^2)
@NLconstraint(model, 2 * x >= -1)
@NLconstraint(model, 1 * x == 0.2)
@NLconstraint(model, 0 <= sin(x) <= 1)
write_to_file(model, "model.nl")
model_2 = read_from_file("model.nl")
nlp = nonlinear_model(model_2)
xi = index(x)
@test nlp.objective == MOI.Nonlinear.parse_expression(nlp, :($xi * $xi))
@test length(nlp.constraints) == 4
constraints = Dict(
MOI.LessThan(2.0) =>
MOI.Nonlinear.parse_expression(nlp, :($xi * $xi)),
MOI.GreaterThan(0.0) =>
MOI.Nonlinear.parse_expression(nlp, :(2 * $xi - -1)),
MOI.EqualTo(0.0) =>
MOI.Nonlinear.parse_expression(nlp, :(1 * $xi - 0.2)),
MOI.Interval(0.0, 1.0) =>
MOI.Nonlinear.parse_expression(nlp, :(sin($xi))),
)
for (_, c) in nlp.constraints
@test c.expression == constraints[c.set]
end
rm("model.nl")
return
end

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

0 comments on commit 1a5b487

Please sign in to comment.