diff --git a/examples/01_create_optigraph.jl b/examples/01_create_optigraph.jl new file mode 100644 index 00000000..193e0355 --- /dev/null +++ b/examples/01_create_optigraph.jl @@ -0,0 +1,58 @@ +using Plasmo +using Ipopt + +graph = OptiGraph(;name=:graph) + +#Add nodes to a OptiGraph +n1 = add_node(graph) +@variable(n1, 0 <= x <= 2) +@variable(n1, 0 <= y <= 3) +@variable(n1, 0 <= z <= 2) +@constraint(n1, x + y + z >= 4) + +n2 = add_node(graph) +@variable(n2, x >= 0) +@constraint(n2, ref, exp(x) >= 2) +@variable(n2, 0 <= z <= 2) +@constraint(n2, z + x >= 4) + +n3 = add_node(graph) +@variable(n3, x[1:3] >= 0) +@constraint(n3, nlcon, exp(x[3]) >= 5) +@constraint(n3, conref, sum(x[i] for i in 1:3) == 10) + +n4 = add_node(graph) +@variable(n4, x[1:2] >= 0) +@constraint(n4, sum(x[i] for i in 1:2) >= 10) +@constraint(n4, ref, exp(x[2]) >= 4) + +# Add link constsraints +@linkconstraint(graph, link1, n1[:x] == n2[:x]) +@linkconstraint(graph, link2, n2[:x] == n3[:x][3]) +@linkconstraint(graph, link3, n3[:x][1] == n4[:x][1]) + +# set an objective for the graph +@objective(graph, Min, n1[:y] + n2[:x] - (n3[:x][1] + n3[:x][2] + n3[:x][3]) + n4[:x][2]^3) + +# optimize the graph +optimizer = Ipopt.Optimizer +set_optimizer(graph, optimizer) +optimize!(graph) + +println() +println("objective value = ", objective_value(graph)) +println() + +println("variable values:") +for var in all_variables(graph) + println(var, " = ", value(var)) +end +println() + +println("constraint dual values:") +for constraint_type in list_of_constraint_types(graph) + cons = all_constraints(graph, constraint_type[1], constraint_type[2]) + for con in cons + println("($con) = $(dual(con))") + end +end \ No newline at end of file diff --git a/examples/02_create_nested_optigraph.jl b/examples/02_create_nested_optigraph.jl new file mode 100644 index 00000000..b9214f55 --- /dev/null +++ b/examples/02_create_nested_optigraph.jl @@ -0,0 +1,47 @@ +using Plasmo +using Ipopt + +function add_model(graph::OptiGraph) + node = add_node(graph) + @variable(node, x >= 0) + @variable(node, y >= 1) + @constraint(node, x + y <= 5) + @constraint(node, exp(x) >= 2) + return node +end + +# the top-level graph +graph = OptiGraph(;name=:graph) + +# subgraph 1 +subgraph1 = OptiGraph(;name=:sg1) +n1 = add_model(subgraph1) +n2 = add_model(subgraph1) +@linkconstraint(subgraph1, n1[:x] == n2[:x]) + +# subgraph 2 +subgraph2 = OptiGraph(;name=:sg2) +n3 = add_model(subgraph2) +n4 = add_model(subgraph2) +@linkconstraint(subgraph2, n3[:x] == n4[:x]) + +# add subgraphs to top-level graph +add_subgraph(graph, subgraph1) +add_subgraph(graph, subgraph2) + +# add links between subgraphs +@linkconstraint(graph, n1[:x] == n3[:x]) +@linkconstraint(graph, n2[:x] == n4[:x]) + +# objective function +@objective(graph, Min, sum(node[:x] + node[:y] for node in all_nodes(graph))) + +set_optimizer(graph, Ipopt.Optimizer) +optimize!(graph) + +println("n1[:x]= ", value(graph, n1[:x])) +println("n2[:x]= ", value(graph, n2[:x])) +println("n3[:x]= ", value(graph, n3[:x])) +println("n4[:x]= ", value(graph, n4[:x])) + +println("objective = ", objective_value(graph)) \ No newline at end of file diff --git a/examples/optigraph_aggregate.jl b/examples/03_aggregate_optigraphs.jl similarity index 53% rename from examples/optigraph_aggregate.jl rename to examples/03_aggregate_optigraphs.jl index ac9cda65..0edd302f 100644 --- a/examples/optigraph_aggregate.jl +++ b/examples/03_aggregate_optigraphs.jl @@ -1,38 +1,40 @@ #Example using Plasmo -using GLPK +using HiGHS -#Create a modelgraph +# create optigraph; set optimizer graph = OptiGraph() -optimizer = GLPK.Optimizer +optimizer = HiGHS.Optimizer -#Add nodes to optigraph +# add nodes using macro n1 = @optinode(graph) n2 = @optinode(graph) -#Node 1 Model -@variable(n1, 0 <= x <= 2) +# node 1 model +@variable(n1, 1 <= x <= 2) @variable(n1, 0 <= y <= 3) @variable(n1, z >= 0) @constraint(n1, x + y + z >= 4) -#Node 2 Model +# node 2 model @variable(n2, x) @variable(n2, z >= 0) @constraint(n2, z + x >= 4) -#Link constraints take the same expressions as the JuMP @constraint macro +# add link constraints @linkconstraint(graph, n1[:x] == n2[:x]) @linkconstraint(graph, n1[:z] == n2[:z]) -#Objective function +# add objective function @objective(graph, Min, n1[:y] + n2[:x] + n1[:z]) -#Aggregate optinodes into a single optinode to solve using JuMP's interface +# aggregate optinodes into a single optinode aggregate_node, reference_map = aggregate(graph) -set_optimizer(aggregate_node, optimizer) -optimize!(aggregate_node) +agg_graph = source_graph(aggregate_node) +set_optimizer(agg_graph, optimizer) +optimize!(agg_graph) -#Use the reference map to look up values on the aggregate node +# use the reference map to look up values on the aggregate node println("n1[:x] = ", value(reference_map[n1[:x]])) println("n2[:x] = ", value(reference_map[n2[:x]])) +println("n1[:z] = ", value(reference_map[n1[:z]])) \ No newline at end of file diff --git a/examples/optigraph_graph_functions.jl b/examples/04_graph_functions.jl similarity index 85% rename from examples/optigraph_graph_functions.jl rename to examples/04_graph_functions.jl index 3a98aa33..4a377eb0 100644 --- a/examples/optigraph_graph_functions.jl +++ b/examples/04_graph_functions.jl @@ -1,29 +1,29 @@ using Plasmo -using LightGraphs +using Graphs -function create_optigraph() - graph = OptiGraph() +function create_optigraph(name) + graph = OptiGraph(;name=name) @optinode(graph, nodes[1:3]) - #node 1 + # node 1 @variable(nodes[1], 0 <= x <= 2) @variable(nodes[1], 0 <= y <= 3) @constraint(nodes[1], x + y <= 4) @objective(nodes[1], Min, x) - #node 2 + # node 2 @variable(nodes[2], x >= 1) @variable(nodes[2], 0 <= y <= 5) @constraint(nodes[2], x + y <= 7) @objective(nodes[2], Min, x) - #node 3 + # node 3 @variable(nodes[3], x >= 0) @variable(nodes[3], y >= 0) @constraint(nodes[3], x + y == 2) @objective(nodes[3], Max, x) - #Link constraints take the same expressions as the JuMP @constraint macro + # link constraints @linkconstraint(graph, nodes[1][:x] == nodes[2][:x]) @linkconstraint(graph, nodes[2][:y] == nodes[3][:x]) @linkconstraint(graph, nodes[3][:x] == nodes[1][:x]) @@ -32,11 +32,11 @@ function create_optigraph() return graph end -graph = OptiGraph() +graph = OptiGraph(;name=:graph) -graph1 = create_optigraph() -graph2 = create_optigraph() -graph3 = create_optigraph() +graph1 = create_optigraph(:sg1) +graph2 = create_optigraph(:sg2) +graph3 = create_optigraph(:sg3) add_subgraph!(graph, graph1) add_subgraph!(graph, graph2) diff --git a/examples/optigraph_partition.jl b/examples/05_partition_optigraph.jl similarity index 100% rename from examples/optigraph_partition.jl rename to examples/05_partition_optigraph.jl diff --git a/examples/optigraph_plotting.jl b/examples/06_plotting_optigraphs.jl similarity index 100% rename from examples/optigraph_plotting.jl rename to examples/06_plotting_optigraphs.jl diff --git a/examples/dev_optigraph.jl b/examples/dev_optigraph.jl deleted file mode 100644 index 4cee1df6..00000000 --- a/examples/dev_optigraph.jl +++ /dev/null @@ -1,7 +0,0 @@ -using Plasmo - -graph = OptiGraph() - -n1 = Plasmo.add_node(graph) - -@variable(n1, x) \ No newline at end of file diff --git a/examples/notebooks/optigraph_example.ipynb b/examples/notebooks/modeling_with_optigraphs.ipynb similarity index 100% rename from examples/notebooks/optigraph_example.ipynb rename to examples/notebooks/modeling_with_optigraphs.ipynb diff --git a/examples/notebooks/partition_optigraphs.ipynb b/examples/notebooks/partitioning_optigraphs.ipynb similarity index 100% rename from examples/notebooks/partition_optigraphs.ipynb rename to examples/notebooks/partitioning_optigraphs.ipynb diff --git a/examples/optigraph_2_nodes.jl b/examples/optigraph_2_nodes.jl deleted file mode 100644 index a8311a9d..00000000 --- a/examples/optigraph_2_nodes.jl +++ /dev/null @@ -1,58 +0,0 @@ -using Plasmo -using GLPK - -graph = OptiGraph() - -#Add nodes to optigraph -n1 = @optinode(graph) -n2 = @optinode(graph) - -#Node 1 Model -@variable(n1, 0 <= x <= 2) -@variable(n1, 0 <= y <= 3) -@variable(n1, z >= 0) -@constraint(n1, x + y + z >= 4) - -#Node 2 Model -@variable(n2, x) -@variable(n2, z >= 0) -@constraint(n2, z + x >= 4) - -#Link constraints take the same expressions as the JuMP @constraint macro -@linkconstraint(graph, n1[:x] == n2[:x]) -@linkconstraint(graph, n1[:z] == n2[:z]) - -#Objective function -@objective(graph, Min, n1[:y] + n2[:x] + n1[:z]) - -#Optimize with glpk. -optimizer = GLPK.Optimizer -set_optimizer(graph, optimizer) -optimize!(graph) - -#Get results -println() -println("objective value = ", objective_value(graph)) - -println() -println("variable values:") -println("n1[:z] = ", value(n1[:z])) -println("n2[:z] = ", value(n2[:z])) -println("n1[:x] = ", value(n1[:x])) -println("n1[:y] = ", value(n1[:y])) -println("n2[:x] = ", value(n2[:x])) - -println() -println("dual values on nodes:") -for constraint_type in list_of_constraint_types(graph) - cons = all_constraints(graph, constraint_type[1], constraint_type[2]) - for con in cons - println("($con) = $(dual(con))") - end -end - -println() -println("dual values on link constraints") -for link in all_linkconstraints(graph) - println("($link) = $(dual(link))") -end diff --git a/examples/optigraph_4_nodes.jl b/examples/optigraph_4_nodes.jl deleted file mode 100644 index 783e3692..00000000 --- a/examples/optigraph_4_nodes.jl +++ /dev/null @@ -1,64 +0,0 @@ -using Plasmo -using Ipopt - -graph = OptiGraph() -optimizer = Ipopt.Optimizer -set_optimizer(graph, optimizer) - -#Add nodes to a OptiGraph -@optinode(graph, nodes[1:4]) - -@variable(nodes[1], 0 <= x <= 2) -@variable(nodes[1], 0 <= y <= 3) -@variable(nodes[1], 0 <= z <= 2) -@constraint(nodes[1], x + y + z >= 4) -@objective(nodes[1], Min, y) - -@variable(nodes[2], x >= 0) -@NLconstraint(nodes[2], ref, exp(x) >= 2) -@variable(nodes[2], 0 <= z <= 2) -@constraint(nodes[2], z + x >= 4) -@objective(nodes[2], Min, x) - -@variable(nodes[3], x[1:3] >= 0) -@NLconstraint(nodes[3], nlcon, exp(x[3]) >= 5) -@constraint(nodes[3], conref, sum(x[i] for i in 1:3) == 10) -@objective(nodes[3], Max, x[1] + x[2] + x[3]) - -@variable(nodes[4], x[1:2] >= 0) -@constraint(nodes[4], sum(x[i] for i in 1:2) >= 10) -@NLconstraint(nodes[4], ref, exp(x[2]) >= 4) -@NLobjective(nodes[4], Min, x[2]^3) - -#Link constraints take the same expressions as the JuMP @constraint macro -@linkconstraint(graph, link1, nodes[1][:x] == nodes[2][:x]) -@linkconstraint(graph, link2, nodes[2][:x] == nodes[3][:x][3]) -@linkconstraint(graph, link3, nodes[3][:x][1] == nodes[4][:x][1]) - -optimize!(graph) - -println("objective value = ", objective_value(graph)) -println() -println("variable values:") -for var in all_variables(graph) - println(var, " = ", value(var)) -end -println() - -println("dual values on nodes:") -for constraint_type in list_of_constraint_types(graph) - cons = all_constraints(graph, constraint_type[1], constraint_type[2]) - for con in cons - println("($con) = $(dual(con))") - end -end -println() -println("($conref) = $(dual(conref))") -println("nonlinear dual values on nodes:") -println("($nlcon) = $(dual(nlcon))") - -println() -println("dual values on link constraints") -for link in all_linkconstraints(graph) - println("($link) = $(dual(link))") -end diff --git a/examples/optigraph_subgraphs.jl b/examples/optigraph_subgraphs.jl deleted file mode 100644 index 3d89ed57..00000000 --- a/examples/optigraph_subgraphs.jl +++ /dev/null @@ -1,46 +0,0 @@ -using Plasmo -using Ipopt - -function set_nl_model(node::OptiNode) - @variable(node, x >= 0) - @variable(node, y >= 1) - @constraint(node, x + y <= 5) - @NLconstraint(node, exp(x) >= 2) - @objective(node, Min, x + y) - return nothing -end - -#the top level graph -graph = OptiGraph() - -#System 1 -subgraph1 = OptiGraph() -@optinode(subgraph1, n1) -@optinode(subgraph1, n2) -set_nl_model(n1) -set_nl_model(n2) -@linkconstraint(subgraph1, n1[:x] == n2[:x]) #linkconstraint is local to graph1 - -#System 2 -subgraph2 = OptiGraph() -@optinode(subgraph2, n3) -@optinode(subgraph2, n4) -set_nl_model(n3) -set_nl_model(n4) -@linkconstraint(subgraph2, n3[:x] == n4[:x]) - -#Top level links -add_subgraph!(graph, subgraph1) -add_subgraph!(graph, subgraph2) -@linkconstraint(graph, n1[:x] == n3[:x]) -@linkconstraint(graph, n2[:x] == n4[:x]) - -set_optimizer(graph, Ipopt.Optimizer) -optimize!(graph) - -println("n1[:x]= ", value(n1[:x])) -println("n2[:x]= ", value(n2[:x])) -println("n3[:x]= ", value(n3[:x])) -println("n4[:x]= ", value(n4[:x])) - -println("objective = ", objective_value(graph)) diff --git a/src/backends/moi_backend.jl b/src/backends/moi_backend.jl index 9f90daf0..ddf1671f 100644 --- a/src/backends/moi_backend.jl +++ b/src/backends/moi_backend.jl @@ -287,6 +287,8 @@ end # optimize! function MOI.optimize!(gb::GraphMOIBackend) + # If there are subgraphs, we need to copy their backend data to this graph + _copy_subgraph_backends!(gb.optigraph) MOI.optimize!(gb.moi_backend) return end diff --git a/src/jump_methods.jl b/src/jump_methods.jl deleted file mode 100644 index 8b137891..00000000 --- a/src/jump_methods.jl +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/optigraph.jl b/src/optigraph.jl index 823d17c9..c45247d1 100644 --- a/src/optigraph.jl +++ b/src/optigraph.jl @@ -135,6 +135,8 @@ function add_subgraph(graph::OptiGraph, subgraph::OptiGraph) return subgraph end +@deprecate add_subgraph! add_subgraph + function traverse_parents(graph::OptiGraph) parents = OptiGraph[] if graph.parent_graph != nothing @@ -182,6 +184,18 @@ function get_nodes(graph::OptiGraph) return graph.optinodes end +function num_nodes(graph::OptiGraph) + return length(graph.optinodes) +end + +function num_all_nodes(graph::OptiGraph) + n_nodes = num_nodes(graph) + for subgraph in graph.subgraphs + n_nodes += num_all_nodes(subgraph) + end + return n_nodes +end + """ all_nodes(graph::OptiGraph)::Vector{OptiNode} diff --git a/src/optimizer_interface.jl b/src/optimizer_interface.jl index 435f1d01..498461f5 100644 --- a/src/optimizer_interface.jl +++ b/src/optimizer_interface.jl @@ -5,10 +5,6 @@ function JuMP.get_attribute(graph::OptiGraph, attr::AT) where return MOI.get(graph, attr) end -# function JuMP.get_attribute(graph::OptiGraph, attr::MOI.AbstractOptimizerAttribute) -# return MOI.get(graph, attr) -# end - function JuMP.get_attribute( nvref::NodeVariableRef, attr::AT, @@ -127,8 +123,7 @@ end # mostly copied from: https://github.com/jump-dev/JuMP.jl/blob/597ef39c97d713929e8a6819908c341b31cbd8aa/src/optimizer_interface.jl#L409 function JuMP.optimize!( graph::OptiGraph; - #ignore_optimize_hook = (graph.optimize_hook === nothing), TODO - _differentiation_backend::MOI.Nonlinear.AbstractAutomaticDifferentiation = MOI.Nonlinear.SparseReverseMode(), + #ignore_optimize_hook = (graph.optimize_hook === nothing), kwargs..., ) @@ -147,12 +142,9 @@ function JuMP.optimize!( if JuMP.mode(graph) != DIRECT && MOIU.state(JuMP.backend(graph)) == MOIU.NO_OPTIMIZER throw(JuMP.NoOptimizer()) end - - # If there are subgraphs, we need to copy their backend data to this graph - _copy_subgraph_backends!(graph) - + try - MOI.optimize!(JuMP.backend(graph)) + MOI.optimize!(graph_backend(graph)) catch err if err isa MOI.UnsupportedAttribute{MOI.NLPBlock} error( @@ -165,4 +157,10 @@ function JuMP.optimize!( end graph.is_model_dirty = false return +end + +function JuMP.set_optimizer(node::OptiNode; kwargs...) + error("Optinodes currently no support setting an optimizer. Optimization is now + handled through the optigraph. If you wish to optimize a single optinode, + you can create a new optigraph using `assemble_optigraph(node)`.") end \ No newline at end of file diff --git a/src/optinode.jl b/src/optinode.jl index 22332b38..740cdc71 100644 --- a/src/optinode.jl +++ b/src/optinode.jl @@ -240,4 +240,12 @@ function MOI.delete(node::OptiNode, cref::ConstraintRef) MOI.delete(graph_backend(graph), cref) end return +end + +### JuMP Interop + +# TODO +function set_model(node::OptiNode, model::JuMP.Model) + # assert node is empty + # copy JuMP model to optinode end \ No newline at end of file