diff --git a/docs/plasmo3.svg b/docs/plasmo.svg similarity index 100% rename from docs/plasmo3.svg rename to docs/plasmo.svg diff --git a/docs/plasmo2.svg b/docs/plasmo2.svg deleted file mode 100644 index 9ca96fd5..00000000 --- a/docs/plasmo2.svg +++ /dev/null @@ -1,255 +0,0 @@ - -image/svg+xml - - - - - -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Plasmo.jl b/src/Plasmo.jl index bf95fdc7..75abe8d4 100644 --- a/src/Plasmo.jl +++ b/src/Plasmo.jl @@ -20,9 +20,7 @@ export OptiGraph, NodeVariableRef, graph_backend, graph_index, -add_node, add_edge, add_subgraph, - -has_edge, get_edge, +add_node, add_edge, add_subgraph, has_edge, get_edge, collect_nodes, local_nodes, all_nodes, local_edges, all_edges, @@ -40,16 +38,28 @@ set_to_node_objectives, containing_optigraphs, source_graph, assemble_optigraph, -Partition, apply_partition!, aggregate, aggregate!, aggregate_to_depth, +# partition + +Partition, apply_partition, apply_partition!, n_subpartitions, all_subpartitions, + +# aggregate + +aggregate, aggregate!, aggregate_to_depth, + +# projections hyper_projection, edge_hyper_projection, clique_projection, edge_clique_projection, bipartite_projection, +# topoology + incident_edges, induced_edges, identify_nodes, identify_edges, neighborhood, expand, +# macros + @optinode, @nodevariables, @linkconstraint include("core_types.jl") diff --git a/src/graph_functions/partition.jl b/src/graph_functions/partition.jl index 3b43336b..b71abd2e 100644 --- a/src/graph_functions/partition.jl +++ b/src/graph_functions/partition.jl @@ -25,7 +25,9 @@ end @deprecate getpartitionlist get_partition_list @deprecate partition_list get_partition_list -### Partition constructors +# +# Partition constructors +# """ Partition(graph::OptiGraph, node_membership_vector::Vector{Int64}) @@ -122,16 +124,9 @@ function Partition(projection::GraphProjection, membership_vector::Vector{Int64} return partition end -# function Partition(membership_vector::Vector{Int64}, ref_map::ProjectionMap; kwargs...) -# optigraph = ref_map.optigraph -# partition_vectors = Plasmo._partition_list(membership_vector) -# induced = Plasmo.induced_elements(ref_map.projected_graph, partition_vectors; kwargs...) -# partition_elements = Plasmo._identify_partitions(induced, ref_map) #could be optinode_vectors, optiedge_vectors, or subgraphs -# partition = Partition(optigraph, partition_elements) -# return partition -# end - -# Partition utilities +# +# partition utilities +# function _check_valid_partition( graph::OptiGraph, optinode_vectors::Vector{Vector{OptiNode}} @@ -282,7 +277,9 @@ function _build_partition_list(membership_vector::Vector) return partitions end -### Partition methods +# +# partition methods +# function all_subpartitions(partition::Partition) subparts = partition.subpartitions @@ -309,8 +306,6 @@ function all_nodes(partition::Partition) return nodes end -### Create and modify optigraphs with partitions - """ Assemble a new optigraph from a given `Partition`. """ diff --git a/src/optiedge.jl b/src/optiedge.jl index 174d52fb..69078cb7 100644 --- a/src/optiedge.jl +++ b/src/optiedge.jl @@ -136,45 +136,4 @@ end function JuMP.is_valid(edge::OptiEdge, cref::ConstraintRef) return edge === JuMP.owner_model(cref) && MOI.is_valid(graph_backend(edge), cref) -end - -# -# MOI Methods -# - -# function MOI.get(edge::OptiEdge, attr::MOI.AbstractConstraintAttribute, ref::ConstraintRef) -# return MOI.get(graph_backend(edge), attr, ref) -# end - -# function MOI.get(edge::OptiEdge, attr::MOI.ListOfConstraintTypesPresent) -# cons = graph_backend(edge).element_constraints[edge] -# con_types = unique(typeof.(cons)) -# type_tuple = [(type.parameters[1],type.parameters[2]) for type in con_types] -# return type_tuple -# end - -# function MOI.get( -# edge::OptiEdge, -# attr::MOI.NumberOfConstraints{F,S} -# ) where {F <: MOI.AbstractFunction, S <: MOI.AbstractSet} -# num_cons = 0 -# for con in graph_backend(edge).element_constraints[edge] -# if (typeof(con).parameters[1] == F && typeof(con).parameters[2] == S) -# num_cons += 1 -# end -# end -# return num_cons -# end - -# function MOI.get( -# edge::OptiEdge, -# attr::MOI.ListOfConstraintIndices{F,S} -# ) where {F <: MOI.AbstractFunction, S <: MOI.AbstractSet} -# con_inds = MOI.ConstraintIndex{F,S}[] -# for con in graph_backend(edge).element_constraints[edge] -# if (typeof(con).parameters[1] == F && typeof(con).parameters[2] == S) -# push!(con_inds, con) -# end -# end -# return con_inds -# end \ No newline at end of file +end \ No newline at end of file diff --git a/src/optielement.jl b/src/optielement.jl index 73dd44c6..657799e8 100644 --- a/src/optielement.jl +++ b/src/optielement.jl @@ -1,6 +1,8 @@ # JuMP & MOI methods that dispatch on both optinodes and optiedges -### MOI Methods +# +# MOI Methods +# function MOI.get(element::OptiElement, attr::MOI.AnyAttribute) return MOI.get(graph_backend(element), attr, element) @@ -39,7 +41,9 @@ function MOI.delete(element::OptiElement, cref::ConstraintRef) return end -### JuMP Methods +# +# JuMP Methods +# """ JuMP.constraint_ref_with_index( diff --git a/src/optinode.jl b/src/optinode.jl index 4e7fc4aa..ca97b351 100644 --- a/src/optinode.jl +++ b/src/optinode.jl @@ -239,7 +239,6 @@ function set_model(node::OptiNode, model::JuMP.Model) ) #_copy_model_to!(node, model) end -# @deprecate set_model copy_model_to # TODO # function _copy_model_to!(node::OptiNode, model::JuMP.Model) diff --git a/test/test_optigraph.jl b/test/test_optigraph.jl index 4ee98378..1bd87358 100644 --- a/test/test_optigraph.jl +++ b/test/test_optigraph.jl @@ -61,7 +61,7 @@ function _create_test_nonlinear_optigraph() return graph end -function test_optigraph() +function test_optigraph_build() graph = _create_test_nonlinear_optigraph() # basic queries @@ -97,19 +97,19 @@ function test_optigraph() @test num_constraints(graph, F, S) == 1 @test num_link_constraints(graph) == 29 @test num_link_constraints(graph, F, S) == 0 + + @test length(collect_nodes(objective_function(graph))) == 4 + @test JuMP.objective_function_type(graph) == JuMP.GenericAffExpr{Float64, Plasmo.NodeVariableRef} end function test_objective_functions() graph = _create_test_nonlinear_optigraph() n1,n2,n3,n4 = all_nodes(graph) - @test length(collect_nodes(objective_function(graph))) == 4 - @test JuMP.objective_function_type(graph) == JuMP.GenericAffExpr{Float64, Plasmo.NodeVariableRef} - # linear objective JuMP.set_objective_coefficient(graph, n1[:x], 2.0) @test JuMP.objective_function(graph) == 2*n1[:x] + n2[:x] + n3[:x][1] + n4[:x] - + JuMP.set_objective_coefficient(graph, [n1[:x],n2[:x]], [2.0,2.0]) @test JuMP.objective_function(graph) == 2*n1[:x] + 2*n2[:x] + n3[:x][1] + n4[:x] @@ -150,10 +150,12 @@ function test_objective_functions() set_to_node_objectives(graph) @test objective_function(graph) == n1[:x] - n2[:x] + n3[:x][1]^2 + n3[:x][2]^2 + n4[:x]^2 + @objective(n4, Min, n4[:x]^3) set_to_node_objectives(graph) @test typeof(objective_function(graph)) == GenericNonlinearExpr{NodeVariableRef} + # test solve set_optimizer(graph, optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0)) JuMP.optimize!(graph) @test graph.is_model_dirty == false @@ -235,7 +237,7 @@ function test_assemble_optigraph() @test num_constraints(new_graph) == num_constraints(graph) @test num_link_constraints(new_graph) == num_link_constraints(graph) - # test they produce the same solution + # test graphs produce the same solution set_optimizer(graph, optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0)) optimize!(graph) set_optimizer(new_graph, optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0)) @@ -334,6 +336,7 @@ function test_multiple_solves() @linkconstraint(graph, sum(all_variables(graph)) <= 100) optimize!(graph) + @test value(sum(all_variables(graph))) <= 100 @test termination_status(graph) == MOI.LOCALLY_SOLVED end diff --git a/test/test_partition.jl b/test/test_partition.jl index 63577866..37dcd9d4 100644 --- a/test/test_partition.jl +++ b/test/test_partition.jl @@ -39,7 +39,7 @@ function _create_simple_optigraph() @linkconstraint(graph, nodes[1][:x] == nodes[2][:x]) @linkconstraint(graph, nodes[2][:y] == nodes[3][:x]) @linkconstraint(graph, nodes[3][:x] == nodes[4][:x]) - + set_to_node_objectives(graph) return graph end @@ -57,19 +57,26 @@ end function test_partition_manual() graph = _create_chain_optigraph() + nodes = all_nodes(graph) @objective(graph, Min, sum(all_variables(graph))) set_optimizer(graph, optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0)) optimize!(graph) - obj_val = objective_value(graph) - nodes = all_nodes(graph) A = reshape(nodes, 20, 5) node_vectors = [A[c, :] for c in 1:size(A, 1)] partition = Partition(graph, node_vectors) - @test length(partition.subpartitions) == 20 - @test num_nodes(graph) == 100 + @test n_subpartitions(partition) == 20 + @test length(all_subpartitions(partition)) == 20 + @test length(all_nodes(partition)) == 100 + + # test `assemble_optigraph` + new_graph = assemble_optigraph(partition) + @test num_nodes(new_graph) == 100 + @test num_local_nodes(new_graph) == 0 + + # test `apply_partition!` and modify original graph apply_partition!(graph, partition) @test num_local_nodes(graph) == 0 @test num_nodes(graph) == 100 @@ -81,25 +88,24 @@ function test_partition_manual() @test objective_value(graph) == obj_val end -# function test_node_vector_partition() -# graph = _create_optigraph() -# node_membership_vector = [0, 0, 1, 1] -# part1 = Partition(graph, node_membership_vector) - -# hgraph, hmap = hyper_graph(graph) -# return part2 = Partition(node_membership_vector, hmap) -# end - -# #Hypergraph -# function test_partition_hypergraph() -# optigraph = _create_optigraph() -# hg, hyper_map = Plasmo.hyper_graph(optigraph) -# partition_vector = @suppress KaHyPar.partition(hg, 2; configuration=kahypar_config) -# partition_hyper = Partition(partition_vector, hyper_map) -# return apply_partition!(optigraph, partition_hyper) +function test_node_vector_partition() + graph = _create_simple_optigraph() + node_membership_vector = [0, 0, 1, 1] + partition = Partition(graph, node_membership_vector) + new_graph = assemble_optigraph(partition) + @test num_nodes(new_graph) == 4 +end -# #@test graph_structure(optigraph) == Plasmo.RECURSIVE_GRAPH -# end +# hypergraph +function test_partition_hypergraph() + graph = _create_simple_optigraph() + projection = hyper_projection(graph) + partition_vector = @suppress KaHyPar.partition(projection, 2; configuration=kahypar_config) + partition = Partition(projection, partition_vector) + new_graph = assemble_optigraph(partition) + @test num_nodes(new_graph) == 4 + @test num_subgraphs(new_graph) == 2 +end # #Edge-HyperGraph # function test_partition_edge_hypergraph() diff --git a/test/test_projections.jl b/test/test_projections.jl index f2e1e24e..c1f4d8a7 100644 --- a/test/test_projections.jl +++ b/test/test_projections.jl @@ -1,129 +1,60 @@ module TestGraphRepresentations using Plasmo -using Graphs using SparseArrays using Test -# function test_hypergraph() -# hyper = Plasmo.HyperGraph() -# add_node!(hyper) -# add_node!(hyper) -# add_node!(hyper) -# add_node!(hyper) -# add_node!(hyper) -# add_node!(hyper) - -# @test getnode(hyper, 1) == hyper.vertices[1] -# @test Base.getindex(hyper, 1) == 1 -# @test getnodes(hyper) == [1, 2, 3, 4, 5, 6] -# @test length(hyper.vertices) == 6 -# @test LightGraphs.vertices(hyper) == [1, 2, 3, 4, 5, 6] - -# Plasmo.add_hyperedge!(hyper, 1, 2, 3) -# Plasmo.add_hyperedge!(hyper, 1, 2) -# LightGraphs.add_edge!(hyper, 4, 1, 3) -# LightGraphs.add_edge!(hyper, 4, 5, 6) -# @test length(hyper.hyperedge_map) == 4 -# @test hyper.hyperedge_map[1] == Plasmo.HyperEdge(1, 2, 3) -# @test hyper.hyperedge_map[2] == Plasmo.HyperEdge(1, 2) -# @test hyper.hyperedge_map[3] == Plasmo.HyperEdge(1, 3, 4) -# @test collect(Plasmo.gethyperedges(hyper)) == [1, 2, 3, 4] -# @test collect(Plasmo.getedges(hyper)) == [1, 2, 3, 4] - -# e1 = Plasmo.gethyperedge(hyper, 1) -# @test_throws Exception Base.reverse(e1) -# @test sort(Plasmo.gethypernodes(e1)) == [1, 2, 3] -# @test sort(LightGraphs.vertices(e1)) == [1, 2, 3] -# @test Set([1, 2, 3]) == hyper.hyperedge_map[1].vertices -# @test Set([1, 2]) == hyper.hyperedge_map[2].vertices -# @test Set([1, 3, 4]) == hyper.hyperedge_map[3].vertices -# @test Base.getindex(hyper, e1) == 1 - -# @test LightGraphs.edgetype(hyper) == Plasmo.HyperEdge -# @test LightGraphs.has_edge(hyper, e1) == true -# @test LightGraphs.has_edge(hyper, Set([1, 2, 3])) == true -# @test LightGraphs.has_vertex(hyper, 1) == true -# @test LightGraphs.has_vertex(hyper, 10) == false -# @test LightGraphs.is_directed(hyper) == false -# @test LightGraphs.ne(hyper) == 4 -# @test LightGraphs.nv(hyper) == 6 -# @test LightGraphs.degree(hyper, 1) == 3 -# @test LightGraphs.all_neighbors(hyper, 1) == [2, 3, 4] - -# #4 vertices are connected by 3 hyperedges -# A = LightGraphs.incidence_matrix(hyper) -# @test size(A) == (6, 4) -# @test SparseArrays.nnz(A) == 11 - -# B = LightGraphs.adjacency_matrix(hyper) -# @test SparseArrays.nnz(B) == 16 - -# @test SparseArrays.sparse(hyper) == A - -# @test Plasmo.incident_edges(hyper, 1) == -# [Plasmo.HyperEdge(1, 2, 3), Plasmo.HyperEdge(1, 2), Plasmo.HyperEdge(1, 3, 4)] - -# @test Plasmo.induced_edges(hyper, [1, 2, 3]) == -# [Plasmo.HyperEdge(1, 2, 3), Plasmo.HyperEdge(1, 2)] - -# @test Plasmo.incident_edges(hyper, [1, 2]) == -# [Plasmo.HyperEdge(1, 2, 3), Plasmo.HyperEdge(1, 3, 4)] - -# partition_vector = [[1, 2, 3], [4, 5, 6]] -# p, cross = Plasmo.identify_edges(hyper, partition_vector) -# @test p == -# [[Plasmo.HyperEdge(1, 2, 3), Plasmo.HyperEdge(1, 2)], [Plasmo.HyperEdge(4, 5, 6)]] -# @test cross == Plasmo.HyperEdge[Plasmo.HyperEdge(1, 3, 4)] -# @test Plasmo.induced_elements(hyper, partition_vector) == partition_vector - -# hedges = collect(values(hyper.hyperedge_map)) -# partition_vector = [hedges[1:2], hedges[3:4]] -# p, cross = Plasmo.identify_nodes(hyper, partition_vector) -# @test p == [[2], [4, 5, 6]] -# @test cross == [1, 3] - -# @test Plasmo.neighborhood(hyper, [1, 2], 1) == [1, 2, 3, 4] -# @test Plasmo.neighborhood(hyper, [1, 2], 2) == [1, 2, 3, 4, 5, 6] - -# new_nodes, new_edges = Plasmo.expand(hyper, [1, 2], 1) -# @test new_nodes == [1, 2, 3, 4] -# @test new_edges == hedges[1:3] - -# @test_throws Exception LightGraphs.rem_edge!(hyper, e1) -# @test_throws Exception LightGraphs.rem_vertex!(hyper, 1) -# end - -# function test_clique_graph() -# graph = Plasmo.CliqueGraph() - -# add_vertex!(graph) -# add_vertex!(graph) -# add_vertex!(graph) -# @test nv(graph) == 3 +function _create_simple_optigraph() + graph = OptiGraph() + @optinode(graph, nodes[1:4]) + + #node 1 + @variable(nodes[1], 0 <= x <= 2) + @variable(nodes[1], 0 <= y <= 3) + @constraint(nodes[1], 0 <= x + y <= 4) + @objective(nodes[1], Min, x) + + #node 2 + @variable(nodes[2], x >= 1) + @variable(nodes[2], 0 <= y <= 5) + @constraint(nodes[2], exp(x) + y <= 7) + @objective(nodes[2], Min, x) + + #node 3 + @variable(nodes[3], x >= 0) + @variable(nodes[3], y >= 0) + @constraint(nodes[3], x + y == 2) + @objective(nodes[3], Max, x) + + #node 4 + @variable(nodes[4], 0 <= x <= 1) + @variable(nodes[4], y >= 0) + @constraint(nodes[4], x + y <= 3) + @objective(nodes[4], Max, y) + + @linkconstraint(graph, nodes[1][:x] == nodes[2][:x]) + @linkconstraint(graph, nodes[2][:y] == nodes[3][:x]) + @linkconstraint(graph, nodes[3][:x] == nodes[4][:x]) + set_to_node_objectives(graph) + return graph +end -# add_edge!(graph, 1, 2) -# add_edge!(graph, 2, 3) -# add_edge!(graph, 1, 3) -# @test ne(graph) == 3 +function test_projection_hypergraph() + graph = _create_simple_optigraph() + projection = hyper_projection(graph) -# @test collect(LightGraphs.edges(graph)) == [Edge(1, 2), Edge(1, 3), Edge(2, 3)] -# @test LightGraphs.edgetype(graph) == LightGraphs.SimpleGraphs.SimpleEdge{Int64} -# @test LightGraphs.has_edge(graph, 1, 2) == true -# @test LightGraphs.has_edge(graph, 1, 5) == false -# @test LightGraphs.has_vertex(graph, 1) == true -# @test LightGraphs.has_vertex(graph, 5) == false -# @test LightGraphs.is_directed(graph) == false -# @test LightGraphs.ne(graph) == 3 -# @test LightGraphs.nv(graph) == 3 -# @test LightGraphs.vertices(graph) == [1, 2, 3] + # TODO: get mapped elements +end -# @test LightGraphs.all_neighbors(graph, 1) == [2, 3] +function test_projection_clique() + graph = _create_simple_optigraph() + projection = clique_projection(graph) +end -# A = LightGraphs.incidence_matrix(graph) -# @test length(A) == 9 -# @test SparseArrays.nnz(A) == 6 -# end +function test_projection_bipartite() + graph = _create_simple_optigraph() + projection = bipartite_projection(graph) +end # function test_bipartite_graph() # graph = Plasmo.BipartiteGraph() diff --git a/test/test_topology_functions.jl b/test/test_topology_functions.jl index 3e892edf..5acaf21a 100644 --- a/test/test_topology_functions.jl +++ b/test/test_topology_functions.jl @@ -4,18 +4,17 @@ using Plasmo using Graphs using Test -# function _create_test_optigraph() -# graph = OptiGraph() -# @optinode(graph, nodes[1:100]) -# for node in nodes -# @variable(node, 0 <= x <= 2) -# @variable(node, 0 <= y <= 3) -# @NLconstraint(node, x^3 + y <= 4) -# end -# @linkconstraint(graph, links[i=1:99], nodes[i][:x] == nodes[i + 1][:x]) -# @objective(graph, Min, sum(node[:y] for node in nodes)) -# return graph -# end +function _create_chain_optigraph() + graph = OptiGraph() + @optinode(graph, nodes[1:100]) + for node in nodes + @variable(node, x >= 0) + end + for j in 1:99 + @linkconstraint(graph, nodes[j][:x] == nodes[j + 1][:x]) + end + return graph +end # function _create_test_optigraph_w_subgraphs() # graph = _create_test_optigraph() @@ -31,24 +30,6 @@ using Test # return graph # end -# function test_hypergraph_backend() -# graph = _create_test_optigraph() - -# Plasmo.set_graph_backend(graph) -# @test isa(Plasmo.graph_backend(graph), Plasmo.HyperGraphBackend) - -# hgraph, proj_map = Plasmo.graph_backend_data(graph) -# @test LightGraphs.nv(hgraph) == 100 - -# #this should not reset the graph backend -# @test Plasmo._init_graph_backend(graph) == false - -# graph = _create_test_optigraph() -# #this will reset because it is empty -# @test Plasmo.graph_backend(graph) == nothing -# @test Plasmo._init_graph_backend(graph) == true -# @test isa(Plasmo.graph_backend(graph), Plasmo.HyperGraphBackend) -# end # function test_hypergraph_functions() # graph = _create_test_optigraph()