plasmo-dev/Plasmo.jl

Quadratic objectives with variables on separate nodes is not supported

Closed this issue · 3 comments

Plasmo seems to only support quadratic objectives when the two terms of the quadratic objectives are on the same node. If a quadratic term is added to the objective function which has variables on separate nodes, the line here returns an error. The example below returns the error:

using Plasmo, JuMP, Ipopt

graph = OptiGraph()
set_optimizer(graph, Ipopt.Optimizer)

@optinode(graph, nodes[1:4])
for (i, node) in enumerate(nodes)
    @variable(node, x >= i)
    @objective(node, Min, 2 * x^2)
end

optimize!(graph)

obj_func = objective_function(graph)

new_term = UnorderedPair(nodes[1][:x], nodes[2][:x])
obj_func.terms[new_term] = 3.0

optimize!(graph)

On the modeling side, this can be avoided by placing dummy variables on each node and then adding a linking constraint between the dummy variable and the variable it represents, but it could be nice to someday support quadratic objectives on different nodes.

I am not sure if this is sufficient (or robust), but I currently avoid this error by changing these lines into the following code:

for (i, terms) in enumerate(quad_terms(obj))
    term1 = terms[2]
    term2 = terms[3]
    node1 = optinode(term1)
    node2 = optinode(term2)
    #@assert optinode(term1) == optinode(term2)
    moi_term1 = index(term1)
    moi_term2 = index(term2)
    node_idx_map1 = backend(node1).optimizers[graph.id].node_to_optimizer_map
    node_idx_map2 = backend(node2).optimizers[graph.id].node_to_optimizer_map
    new_moi_idx_1 = node_idx_map1[moi_term1]
    new_moi_idx_2 = node_idx_map2[moi_term2]
    moi_obj = _swap_quad_term!(moi_obj, i, new_moi_idx_1, new_moi_idx_2)
end

When I make this change, I no longer get an error. I have quadratic terms with variables from separate nodes, and the OptiGraph solves without issue.

This should work now in v0.6. The following example demonstrates what you should be able to do:

using Plasmo, JuMP, Ipopt

graph = OptiGraph()
set_optimizer(graph, Ipopt.Optimizer)

@optinode(graph, nodes[1:4])
for (i, node) in enumerate(nodes)
    @variable(node, x >= i)
    @objective(node, Min, 2 * x^2)
end

node_objectives = sum([objective_function(node) for node in nodes])

# set quadratic over multiple nodes
@objective(graph, Min, node_objectives + nodes[1][:x]^2*nodes[2][:x]^2)

optimize!(graph)

@dlcole3 please reopen if this persists.