plasmo-dev / Plasmo.jl

A Platform for Scalable Modeling and Optimization
Other
143 stars 20 forks source link

`unset_binary` function does not work after `optimize!` is called #87

Open dlcole3 opened 1 year ago

dlcole3 commented 1 year ago

I found that calling unset_binary does not work after optimize! is called when using Juniper as the solver. Below is a MWE that returns the same error.

using Ipopt, Juniper, Plasmo

graph = OptiGraph()

@optinode(graph, nodes[1:2])
@variable(nodes[1], x, Bin)
@variable(nodes[1], 1 <= y)
@variable(nodes[2], 1 <= y)
@linkconstraint(graph, nodes[1][:y] + nodes[2][:y] >= 3)
@constraint(nodes[1], nodes[1][:x] + nodes[1][:y] == 1.5)
@objective(graph, Min, nodes[1][:y] + nodes[2][:y] - nodes[1][:x])

ipopt = optimizer_with_attributes(Ipopt.Optimizer, "print_level"=>0)
optimizer = optimizer_with_attributes(Juniper.Optimizer, "nl_solver"=>ipopt)

set_optimizer(graph, optimizer)
optimize!(graph)
unset_binary(graph[:nodes][1][:x])

I initially thought it might be a problem on Juniper's side, but I can run the same problem in JuMP using Juniper and it does not return an error (see below).

using JuMP
m = Model()

@variable(m, x, Bin)
@variable(m, y1 >= 1)
@variable(m, y2 >= 1)
@constraint(m, y1 + y2 >= 3)
@constraint(m, y1 + x == 1.5)
@objective(m, Min, y1 + y2 - x)

set_optimizer(m, optimizer)

optimize!(m)
unset_binary(m[:x])

I followed the stacktrace, and it seems like it might be because this line calls delete on a MOI.Bridges.LazyBridgeOptimizer object. In contrast, this line works, but it is calling delete on a MOI.Utilities.CachingOptimizer object. It looks like the JuMP version also only calls delete on the CachingOptimizer object.

jalving commented 10 months ago

With v0.5.4, you can use a work-around with reset_optimizer like following:

using Ipopt, Juniper, Plasmo

graph = OptiGraph()

@optinode(graph, nodes[1:2])
@variable(nodes[1], x, Bin)
@variable(nodes[1], 1 <= y)
@variable(nodes[2], 1 <= y)
@linkconstraint(graph, nodes[1][:y] + nodes[2][:y] >= 3)
@constraint(nodes[1], nodes[1][:x] + nodes[1][:y] == 1.5)
@objective(graph, Min, nodes[1][:y] + nodes[2][:y] - nodes[1][:x])

ipopt = optimizer_with_attributes(Ipopt.Optimizer, "print_level"=>0)
optimizer = optimizer_with_attributes(Juniper.Optimizer, "nl_solver"=>ipopt)

set_optimizer(graph, optimizer)
optimize!(graph)
reset_optimizer(graph)
unset_binary(graph[:nodes][1][:x])

Juniper does not seem to support deleting constraints, so it falls back to the caching optimizer. We will need to change how we manage optimizers to fix this correctly. Mainly, we should call reset_optimizer when we detect unsupported changes.

Your first snippet does work with HiGHS which supports deleting constraints.