Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inefficiency in set_to_node_objectives when working with large models #126

Open
yhz0 opened this issue Sep 27, 2024 · 2 comments
Open

Inefficiency in set_to_node_objectives when working with large models #126

yhz0 opened this issue Sep 27, 2024 · 2 comments

Comments

@yhz0
Copy link

yhz0 commented Sep 27, 2024

I am working with a large optimization model using Plasmo.jl, but I encountered a performance issue when calling set_to_node_objectives(graph) on a graph with many nodes.

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

# Initial call to build the tree
println("Building Graph")
build_layer1_tree(graph, SA[], 1.0, 0)
display(graph)

# An OptiGraph
#          g##284 #local elements  #total elements
#--------------------------------------------------
#          Nodes:    161617           161617
#          Edges:    182364           182364
#      Subgraphs:         0                0
#      Variables:    714177           714177
#    Constraints:   1306045          1306045

using JuMP


println("Setting Objective")
# set_to_node_objectives(graph) # hangs indefinitely

println("Optimizing")
optimize!(graph)

Upon investigating the source code of this function, I found two inefficiencies:

  1. The obj variable is initialized as an integer, but it accumulates more complex types (e.g., JuMP expressions).
  2. obj += sense * JuMP.objective_function(node) creates a copy of the expression each time it’s executed.
function set_to_node_objectives(graph::OptiGraph)
    obj = 0
    for node in all_nodes(graph)
        if has_objective(node)
            sense = JuMP.objective_sense(node) == MOI.MAX_SENSE ? -1 : 1
            obj += sense * JuMP.objective_function(node)  # Copies obj each time
        end
    end
    if obj != 0
        @objective(graph, Min, obj)
    end
    return nothing
end

For my specific case (which involves linear objectives), I was able to use the following workaround.

function set_to_node_objectives_linear(graph::OptiGraph)
    obj::GenericAffExpr{Float64, NodeVariableRef} = 0.0
    for node in all_nodes(graph)
        if Plasmo.has_objective(node)
            sense = JuMP.objective_sense(node) == MOI.MAX_SENSE ? -1 : 1
            JuMP.add_to_expression!(obj, JuMP.objective_function(node), sense)
        end
    end
    @objective(graph, Min, obj)
    return
end

Is there a general way to fix this performance issue?

@jalving
Copy link
Member

jalving commented Oct 2, 2024

Hey @yhz0! Thanks for reporting this. It should be possible to extend what you did for the general case if check the objective types on nodes. I'll work on a fix soon.

@jalving
Copy link
Member

jalving commented Nov 2, 2024

@yhz0 This should be fixed with PR #128. Linear and quadratic should work fine, but the performance issue remains for nonlinear node objectives since nonlinear expressions do not support in-place updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants