From 8884ce19800d6c014871f97f0f75c221059f7444 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Thu, 15 Jun 2023 11:09:42 -0600 Subject: [PATCH 01/35] Initial testing --- Project.toml | 3 ++- examples/run_examples.jl | 3 ++- src/heuristics.jl | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 3b6d181..c55cc4b 100644 --- a/Project.toml +++ b/Project.toml @@ -11,9 +11,10 @@ JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" Memento = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" TikzGraphs = "b4f28e30-c73f-5eaf-a395-8a9db949a742" TikzPictures = "37f6aa50-8035-52d0-81c2-5a1d08754b2d" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" [compat] DataStructures = "~0.17, ~0.18" diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 48b8fa1..14399ee 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -59,7 +59,8 @@ params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_ model_options = Dict{Symbol,Any}( :eigen_cuts_sizes => [2, num_nodes], :topology_flow_cuts => true, - :solution_type => "optimal", + :solution_type => "heuristic", + :kopt_parameter => 3, ) result = LOpt.run_LOpt( diff --git a/src/heuristics.jl b/src/heuristics.jl index fa4a397..06ec658 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -2,5 +2,7 @@ # Heuristics to maximize algebraic connectivity of weighted graphs function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int) + @show kopt_parameter + @show data return end From c33d0c463c12a488be43559f3c7b4c643582e03a Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Thu, 15 Jun 2023 13:03:00 -0600 Subject: [PATCH 02/35] Removed revise pkg. Added it by mistake. --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index c55cc4b..55218cb 100644 --- a/Project.toml +++ b/Project.toml @@ -12,7 +12,6 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" Memento = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" TikzGraphs = "b4f28e30-c73f-5eaf-a395-8a9db949a742" TikzPictures = "37f6aa50-8035-52d0-81c2-5a1d08754b2d" From 4beb611c2b998360b5c637ae57739e6b0f4b7ad9 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Tue, 20 Jun 2023 10:38:47 -0600 Subject: [PATCH 03/35] Added a new parameter --- src/types.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types.jl b/src/types.jl index f42f5f1..366e084 100644 --- a/src/types.jl +++ b/src/types.jl @@ -21,7 +21,8 @@ mutable struct LaplacianOptModelOptions sdp_relaxation::Bool - kopt_parameter::Int + kopt_parameter::Int64 + num_of_central_nodes_verifier::Int64 best_lower_bound::Float64 best_incumbent::Union{Vector{Tuple{Int64,Int64}},Nothing} @@ -52,6 +53,7 @@ function get_default_options() sdp_relaxation = false # true, false kopt_parameter = 2 # Integer value >= 1 + num_of_central_nodes_verifier = 5 # Integer value >= 1 best_lower_bound = 0 # Best known feasible solution's objective best_incumbent = nothing @@ -74,6 +76,7 @@ function get_default_options() cheeger_cuts_factor, sdp_relaxation, kopt_parameter, + num_of_central_nodes_verifier, best_lower_bound, best_incumbent, tol_zero, From e67ce54c7ea67cd0ded89174e6bc0d376cc60908 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Tue, 20 Jun 2023 10:59:47 -0600 Subject: [PATCH 04/35] Added new parameter in heuristic_kopt function --- src/lopt_model.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lopt_model.jl b/src/lopt_model.jl index 28e568c..4441f50 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -24,7 +24,7 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.objective_LOModel(lom) elseif lom.options.solution_type == "heuristic" - LOpt.heuristic_kopt(lom.data, lom.options.kopt_parameter) + LOpt.heuristic_kopt(lom.data, lom.options.kopt_parameter, lom.options.num_of_central_nodes_verifier) end elseif lom.options.formulation_type == "max_span_tree" if lom.options.solution_type in ["optimal", "heuristic"] From ddda36b13707288065e4adeb53d17f70fdd91b1c Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Thu, 29 Jun 2023 14:26:24 -0600 Subject: [PATCH 05/35] Committing heuristics for spanning tree --- examples/run_examples.jl | 9 +- src/heuristics.jl | 319 ++++++++++++++++++++++++++++++++++++++- src/utility.jl | 102 +++++++++++++ 3 files changed, 423 insertions(+), 7 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 14399ee..4e61d8f 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -1,7 +1,7 @@ import LaplacianOpt as LOpt using JuMP using CPLEX -# using Gurobi +using Gurobi # using GLPK include("optimizer.jl") @@ -10,7 +10,7 @@ include("optimizer.jl") # MIP solver # # (Cplex 22.1 performs the best) # #----------------------------------# -lopt_optimizer = get_cplex() +lopt_optimizer = get_gurobi() #-------------------------------------# # User-defined input graphs # @@ -45,7 +45,7 @@ end # User-defined params # #-------------------------------# num_nodes = 8 -instance = 1 +instance = 5 data_dict, augment_budget = data_I(num_nodes, instance) params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -57,10 +57,11 @@ params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_ # For more model options, check https://github.com/harshangrjn/LaplacianOpt.jl/blob/master/src/types.jl model_options = Dict{Symbol,Any}( - :eigen_cuts_sizes => [2, num_nodes], + :eigen_cuts_sizes => [num_nodes], :topology_flow_cuts => true, :solution_type => "heuristic", :kopt_parameter => 3, + :num_of_central_nodes_verifier => 5, ) result = LOpt.run_LOpt( diff --git a/src/heuristics.jl b/src/heuristics.jl index 06ec658..93283e2 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,8 +1,321 @@ # -- UNDER CONSTRUCTION -- # # Heuristics to maximize algebraic connectivity of weighted graphs +using Graphs +using LinearAlgebra + + +function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_of_central_nodes_verifier::Int) + num_nodes = data["num_nodes"] + adjacency_augment_graph = data["adjacency_augment_graph"] + adjacency_base_graph = data["adjacency_base_graph"] + num_edges_existing = data["num_edges_existing"] + num_edges_to_augment = data["num_edges_to_augment"] + augment_budget = data["augment_budget"] + is_base_graph_connected = data["is_base_graph_connected"] + + # Making list of all possible edges to augment + edge_augment_list = collect(edges(SimpleGraph(adjacency_augment_graph))) + + # Making list of tuples of edges and edge weights + edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list),) + + for (index, edge) in enumerate(edge_augment_list) + edge_edgeweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)]) + end + + # Sorting the list of tuples in descending order of edge weights + sorted_edge_edgeweight_list = sort(edge_edgeweight_list, by = x -> x[2], rev = true) + #@show sorted_edge_edgeweight_list + + if num_edges_existing == 0 + if augment_budget == num_nodes - 1 #spanning tree + + # Priority central nodes based on sum of edge weights + priority_central_nodes_list = priority_central_nodes(adjacency_augment_graph, num_nodes) + #@show priority_central_nodes_list + + # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity + adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_of_central_nodes_verifier) + + Threads.@threads for index in 1:num_of_central_nodes_verifier + # Builds a spanning tree + adjacency_graph_list[index] = build_span_tree( + num_nodes, + adjacency_augment_graph, + sorted_edge_edgeweight_list, + priority_central_nodes_list[index], + ) + + adjacency_graph_list[index] = refinement_span_tree( + adjacency_augment_graph, + sorted_edge_edgeweight_list, + adjacency_graph_list[index], + kopt_parameter, + ) + end + + adjacency_graph_ac_tuple_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] + @show adjacency_graph_ac_tuple_star + + elseif augment_budget >= num_nodes + print("Under construction") + end + else + print("Under construction") + end -function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int) - @show kopt_parameter - @show data return end + + +function build_span_tree( + num_nodes, + adjacency_augment_graph, + sorted_edge_edgeweight_list, + central_node, + ) + + G = SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph + uncon_nodes_set = Int64[] # Set contains all unconnected nodes + uncon_nodes_set = collect(1:num_nodes) + dir_con_set = Int64[] # Set contains nodes which are directly connected to central node + sec_con_set = Int64[] # Set contains nodes which are connected to nodes in dir_con_set + + for j in eachindex(sorted_edge_edgeweight_list) + if (src(sorted_edge_edgeweight_list[j][1]) == central_node || dst(sorted_edge_edgeweight_list[j][1]) == central_node) + + # First edge of the graph + add_edge!(G, src(sorted_edge_edgeweight_list[j][1]), dst(sorted_edge_edgeweight_list[j][1])) + + # Adding to directly connected set + src(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, dst(sorted_edge_edgeweight_list[j][1])) + dst(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, src(sorted_edge_edgeweight_list[j][1])) + + # Removing from unconnected set + deleteat!(uncon_nodes_set, uncon_nodes_set .== src(sorted_edge_edgeweight_list[j][1])) + deleteat!(uncon_nodes_set, uncon_nodes_set .== dst(sorted_edge_edgeweight_list[j][1])) + break + end + end + #@show connected_components(G) + + while !is_connected(G) # Connecting nodes until a spanning tree is formed + algebraic_connectivity_list = Vector{Float64}(undef, size(dir_con_set)[1] + 1) + algebraic_connectivity_ranker = Int64[] + + for j in 1:(size(dir_con_set)[1]+1) + for k in eachindex(sorted_edge_edgeweight_list) + if j == 1 + if (src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) || + (dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + + # Adding edge to central node + add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + + # Storing current algebraic connectivity + algebraic_connectivity_list[j] = algebraic_connectivity((weighted_adj_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_nodes_set)[1] + 1))) + + + # Removing the added edge + rem_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + break + end + else + if (src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) || + (dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + + # Adding edge to directly connected node + add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + + # Storing current algebraic connectivity + algebraic_connectivity_list[j] = algebraic_connectivity((weighted_adj_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_nodes_set)[1] + 1))) + + # Removing the added edge + rem_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + break + end + end + end + end + + #Sorting algebraic connectivities to choose the best incumbent edge + algebraic_connectivity_ranker = sortperm(algebraic_connectivity_list, rev = true)[1] + + for k in eachindex(sorted_edge_edgeweight_list) + if algebraic_connectivity_ranker == 1 + if (src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + + # Adding edge to central node + add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + + # Adding node to directly connected set + push!(dir_con_set, dst(sorted_edge_edgeweight_list[k][1])) + + # Removing node from unconnected nodes set + deleteat!(uncon_nodes_set, uncon_nodes_set .== dst(sorted_edge_edgeweight_list[k][1])) + break + + elseif (dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + + # Adding edge to central node + add_edge!(G, dst(sorted_edge_edgeweight_list[k][1]), src(sorted_edge_edgeweight_list[k][1])) + + # Adding node to directly connected set + push!(dir_con_set, src(sorted_edge_edgeweight_list[k][1])) + + # Removing node from unconnected nodes set + deleteat!(uncon_nodes_set, uncon_nodes_set .== src(sorted_edge_edgeweight_list[k][1])) + break + end + else + if (src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[algebraic_connectivity_ranker - 1]) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + + # Adding edge to directly connected node + add_edge!(G, dir_con_set[algebraic_connectivity_ranker - 1], dst(sorted_edge_edgeweight_list[k][1])) + + # Adding node to secondary connected set + push!(sec_con_set, dst(sorted_edge_edgeweight_list[k][1])) + + # Removing node from unconnected nodes set + deleteat!(uncon_nodes_set, uncon_nodes_set .== dst(sorted_edge_edgeweight_list[k][1])) + break + + elseif (dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[algebraic_connectivity_ranker - 1]) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + + # Adding edge to directly connected node + add_edge!(G, dir_con_set[algebraic_connectivity_ranker - 1], src(sorted_edge_edgeweight_list[k][1])) + + # Adding node to secondary connected set + push!(sec_con_set, src(sorted_edge_edgeweight_list[k][1])) + + # Removing node from unconnected nodes set + deleteat!(uncon_nodes_set, uncon_nodes_set .== src(sorted_edge_edgeweight_list[k][1])) + break + end + end + end + #@show connected_components(G) + end + #@show G + return (Matrix(adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) +end + +function refinement_span_tree( + adjacency_augment_graph, + sorted_edge_edgeweight_list, + adjacency_graph_ac_tuple, + kopt_parameter, + ) + + G = SimpleGraph(adjacency_graph_ac_tuple[1]) + # Refinement of Algebraic connectivity + if kopt_parameter == 1 + cycle_basis_list = Vector{Int64} + for i in eachindex(sorted_edge_edgeweight_list) + if add_edge!(G, src(sorted_edge_edgeweight_list[i][1]), dst(sorted_edge_edgeweight_list[i][1])) + if is_cyclic(G) + cycle_basis_list = cycle_basis(G)[1] + algebraic_connectivity_list = Vector{Float64}(undef, 0) + vertices_list = Vector{Any}(undef, 0) + for j = 1:(length(cycle_basis_list)-1) + for k = (j+1):length(cycle_basis_list) + if rem_edge!(G, cycle_basis_list[j], cycle_basis_list[k]) + push!(algebraic_connectivity_list, algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) + push!(vertices_list, (cycle_basis_list[j], cycle_basis_list[k])) + add_edge!(G, cycle_basis_list[j], cycle_basis_list[k]) + end + end + end + rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2]) + end + end + end + updated_adjacency_graph_ac_tuple = update(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + + elseif kopt_parameter == 2 + combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + cycle_basis_list = Vector{Int64} + for i in eachindex(combinations) + if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && + adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 + add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])) + add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])) + if is_cyclic(G) + cycle_basis_list = cycle_basis(G) + algebraic_connectivity_list = Vector{Float64}(undef, 0) + vertices_list = Vector{Any}(undef, 0) + for j in 1:(length(cycle_basis_list[1])-1) + for k in (j+1):length(cycle_basis_list[1]) + for l in 1:(length(cycle_basis_list[2])-1) + for m in (l+1):length(cycle_basis_list[2]) + if (cycle_basis_list[1][j], cycle_basis_list[1][k]) !== (cycle_basis_list[2][l], cycle_basis_list[2][m]) + if adjacency_matrix(G)[cycle_basis_list[1][j], cycle_basis_list[1][k]] == 1 && adjacency_matrix(G)[cycle_basis_list[2][l], cycle_basis_list[2][m]] == 1 + rem_edge!(G, cycle_basis_list[1][j], cycle_basis_list[1][k]) + rem_edge!(G, cycle_basis_list[2][l], cycle_basis_list[2][m]) + push!(algebraic_connectivity_list, algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) + push!(vertices_list, ((cycle_basis_list[1][j], cycle_basis_list[1][k]), (cycle_basis_list[2][l], cycle_basis_list[2][m]))) + add_edge!(G, cycle_basis_list[1][j], cycle_basis_list[1][k]) + add_edge!(G, cycle_basis_list[2][l], cycle_basis_list[2][m]) + end + end + end + end + end + end + rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1][2]) + rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2][2]) + end + end + end + updated_adjacency_graph_ac_tuple = update(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + + elseif kopt_parameter == 3 + combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + cycle_basis_list = Vector{Int64} + for i in eachindex(combinations) + if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && + adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 && + adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1])] == 0 + add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])) + add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])) + add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1])) + if is_cyclic(G) + cycle_basis_list = cycle_basis(G) + algebraic_connectivity_list = Vector{Float64}(undef, 0) + vertices_list = Vector{Any}(undef, 0) + for j in 1:(length(cycle_basis_list[1])-1) + for k in (j+1):length(cycle_basis_list[1]) + for l in 1:(length(cycle_basis_list[2])-1) + for m in (l+1):length(cycle_basis_list[2]) + for p in 1:(length(cycle_basis_list[3])-1) + for q in (p+1):length(cycle_basis_list[3]) + if (cycle_basis_list[1][j], cycle_basis_list[1][k]) !== (cycle_basis_list[2][l], cycle_basis_list[2][m]) && + (cycle_basis_list[1][j], cycle_basis_list[1][k]) !== (cycle_basis_list[3][p], cycle_basis_list[3][q]) && + (cycle_basis_list[3][p], cycle_basis_list[3][q]) !== (cycle_basis_list[2][l], cycle_basis_list[2][m]) + if adjacency_matrix(G)[cycle_basis_list[1][j], cycle_basis_list[1][k]] == 1 && adjacency_matrix(G)[cycle_basis_list[2][l], cycle_basis_list[2][m]] == 1 && adjacency_matrix(G)[cycle_basis_list[3][p], cycle_basis_list[3][q]] == 1 + rem_edge!(G, cycle_basis_list[1][j], cycle_basis_list[1][k]) + rem_edge!(G, cycle_basis_list[2][l], cycle_basis_list[2][m]) + rem_edge!(G, cycle_basis_list[3][p], cycle_basis_list[3][q]) + push!(algebraic_connectivity_list, algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) + push!(vertices_list, ((cycle_basis_list[1][j], cycle_basis_list[1][k]), (cycle_basis_list[2][l], cycle_basis_list[2][m]), (cycle_basis_list[3][p], cycle_basis_list[3][q]))) + add_edge!(G, cycle_basis_list[1][j], cycle_basis_list[1][k]) + add_edge!(G, cycle_basis_list[2][l], cycle_basis_list[2][m]) + add_edge!(G, cycle_basis_list[3][p], cycle_basis_list[3][q]) + end + end + end + end + end + end + end + end + rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1][2]) + rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2][2]) + rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][3][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][3][2]) + end + end + end + updated_adjacency_graph_ac_tuple = update(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + end + return updated_adjacency_graph_ac_tuple +end \ No newline at end of file diff --git a/src/utility.jl b/src/utility.jl index a16fd28..dd06959 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -330,3 +330,105 @@ function _PMinorIdx(N::Int64, sizes::Vector{Int64}) return minor_idx_dict end + + +""" + priority_central_nodes(adjacency_augment_graph::Array{<:Number}, num_nodes::Int64) + +Returns a vector of order of central nodes as +an input to construct the graph. +""" + +function priority_central_nodes(adjacency_augment_graph::Array{<:Number}, num_nodes::Int64) + edge_weights_sum_list = Vector{Float64}(undef, num_nodes) + central_nodes_list = Vector{Int64}(undef, num_nodes) + + for i in 1:num_nodes + edge_weights_sum_list[i] = sum(adjacency_augment_graph[i, :]) + end + central_nodes_list = sortperm(edge_weights_sum_list, rev = true) + return central_nodes_list +end + + +""" + weighted_adj_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, size::Int64) + +Returns a weighted adjacency matrix for +the connected part of graph. +""" + + +function weighted_adj_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, size::Int64) + weighted_adj_matrix_size = Matrix{Float64}(undef, size, size) + + #collecting vertices connected in graph + vertices_from_edges = Int[] + for edge in edges(G) + push!(vertices_from_edges, src(edge)) + push!(vertices_from_edges, dst(edge)) + end + vertices_from_edges = unique(vertices_from_edges) + + for i in 1:size + for j in i:size + weighted_adj_matrix_size[i, j] = (adjacency_augment_graph.*adjacency_matrix(G))[vertices_from_edges[i],vertices_from_edges[j]] + weighted_adj_matrix_size[j, i] = weighted_adj_matrix_size[i, j] + end + end + + #@show weighted_adj_matrix_size + return weighted_adj_matrix_size +end + +""" + update(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},adjacency_augment_graph::Array{<:Number},adjacency_graph_ac_tuple::Tuple{Array,Float};tol = 1E-6,) + +Returns an updated adjacency_graph_ac_tuple after the kopt refiniement. +""" + +function update( + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + adjacency_augment_graph::Array{<:Number}, + adjacency_graph_ac_tuple::Tuple{Array,Float64}; + tol = 1E-6, +) + if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) - adjacency_graph_ac_tuple[2] >= tol + return Matrix(adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + else + return adjacency_graph_ac_tuple + end +end + +""" + edge_combinations(n::Int64, k::Int64) + +Returns all combinations possible for given n and k. +""" + +function edge_combinations(num_edges::Int64, kopt_parameter::Int64) + if kopt_parameter < 2 || kopt_parameter > 3 + Memento.error(_LOGGER, "kopt_parameter must be either 2 or 3") + elseif num_edges < kopt_parameter + Momento.error(_LOGGER, "num_edges must be greater than or equal to k") + end + + combinations = [] + + if kopt_parameter == 2 + for i in 1:num_edges-1 + for j in i+1:num_edges + push!(combinations, (i,j)) + end + end + elseif kopt_parameter == 3 + for i in 1:num_edges-2 + for j in i+1:num_edges-1 + for l in j+1:num_edges + push!(combinations, (i,j,l)) + end + end + end + end + return combinations +end \ No newline at end of file From f68280a57a145b7c8478b51a3e71e69a3dbbff1c Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Thu, 29 Jun 2023 14:37:29 -0600 Subject: [PATCH 06/35] parameter name change --- examples/run_examples.jl | 4 ++-- src/heuristics.jl | 6 +++--- src/lopt_model.jl | 2 +- src/types.jl | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 4e61d8f..446cd48 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -59,9 +59,9 @@ params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_ model_options = Dict{Symbol,Any}( :eigen_cuts_sizes => [num_nodes], :topology_flow_cuts => true, - :solution_type => "heuristic", + :solution_type => "optimal", :kopt_parameter => 3, - :num_of_central_nodes_verifier => 5, + :num_central_nodes_verifier => 5, ) result = LOpt.run_LOpt( diff --git a/src/heuristics.jl b/src/heuristics.jl index 93283e2..6308e95 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -4,7 +4,7 @@ using Graphs using LinearAlgebra -function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_of_central_nodes_verifier::Int) +function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central_nodes_verifier::Int) num_nodes = data["num_nodes"] adjacency_augment_graph = data["adjacency_augment_graph"] adjacency_base_graph = data["adjacency_base_graph"] @@ -35,9 +35,9 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_of_cent #@show priority_central_nodes_list # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity - adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_of_central_nodes_verifier) + adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_verifier) - Threads.@threads for index in 1:num_of_central_nodes_verifier + Threads.@threads for index in 1:num_central_nodes_verifier # Builds a spanning tree adjacency_graph_list[index] = build_span_tree( num_nodes, diff --git a/src/lopt_model.jl b/src/lopt_model.jl index 4441f50..6d3e5e2 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -24,7 +24,7 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.objective_LOModel(lom) elseif lom.options.solution_type == "heuristic" - LOpt.heuristic_kopt(lom.data, lom.options.kopt_parameter, lom.options.num_of_central_nodes_verifier) + LOpt.heuristic_kopt(lom.data, lom.options.kopt_parameter, lom.options.num_central_nodes_verifier) end elseif lom.options.formulation_type == "max_span_tree" if lom.options.solution_type in ["optimal", "heuristic"] diff --git a/src/types.jl b/src/types.jl index 366e084..8a4f500 100644 --- a/src/types.jl +++ b/src/types.jl @@ -22,7 +22,7 @@ mutable struct LaplacianOptModelOptions sdp_relaxation::Bool kopt_parameter::Int64 - num_of_central_nodes_verifier::Int64 + num_central_nodes_verifier::Int64 best_lower_bound::Float64 best_incumbent::Union{Vector{Tuple{Int64,Int64}},Nothing} @@ -53,7 +53,7 @@ function get_default_options() sdp_relaxation = false # true, false kopt_parameter = 2 # Integer value >= 1 - num_of_central_nodes_verifier = 5 # Integer value >= 1 + num_central_nodes_verifier = 5 # Integer value >= 1 best_lower_bound = 0 # Best known feasible solution's objective best_incumbent = nothing @@ -76,7 +76,7 @@ function get_default_options() cheeger_cuts_factor, sdp_relaxation, kopt_parameter, - num_of_central_nodes_verifier, + num_central_nodes_verifier, best_lower_bound, best_incumbent, tol_zero, From 0b7db193504f8dc6e2674bd60bfc611397973ec2 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Wed, 26 Jul 2023 14:29:14 -0600 Subject: [PATCH 07/35] adding comments --- src/heuristics.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index 6308e95..f8671de 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -37,7 +37,9 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_verifier) - Threads.@threads for index in 1:num_central_nodes_verifier + #JULIA_NUM_THREADS=auto #Uncomment this line for multi threading (Requires atleast Julia 1.7) + #Threads.@threads for index in 1:num_central_nodes_verifier #Uncomment this line for multi threading + for index in 1:num_central_nodes_verifier #Comment this line for multithreading # Builds a spanning tree adjacency_graph_list[index] = build_span_tree( num_nodes, From 07efd489a7bd37cd8951d57defdf5b8224c88399 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Tue, 8 Aug 2023 10:39:19 -0600 Subject: [PATCH 08/35] Updated kopt --- examples/run_examples.jl | 4 +- src/heuristics.jl | 108 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 102 insertions(+), 10 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 446cd48..b0ff9c5 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -59,8 +59,8 @@ params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_ model_options = Dict{Symbol,Any}( :eigen_cuts_sizes => [num_nodes], :topology_flow_cuts => true, - :solution_type => "optimal", - :kopt_parameter => 3, + :solution_type => "heuristic", + :kopt_parameter => 2, :num_central_nodes_verifier => 5, ) diff --git a/src/heuristics.jl b/src/heuristics.jl index f8671de..784f0fd 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -9,7 +9,6 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central adjacency_augment_graph = data["adjacency_augment_graph"] adjacency_base_graph = data["adjacency_base_graph"] num_edges_existing = data["num_edges_existing"] - num_edges_to_augment = data["num_edges_to_augment"] augment_budget = data["augment_budget"] is_base_graph_connected = data["is_base_graph_connected"] @@ -17,7 +16,7 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central edge_augment_list = collect(edges(SimpleGraph(adjacency_augment_graph))) # Making list of tuples of edges and edge weights - edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list),) + edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) edge_edgeweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)]) @@ -37,9 +36,9 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_verifier) - #JULIA_NUM_THREADS=auto #Uncomment this line for multi threading (Requires atleast Julia 1.7) - #Threads.@threads for index in 1:num_central_nodes_verifier #Uncomment this line for multi threading - for index in 1:num_central_nodes_verifier #Comment this line for multithreading + #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) + #Threads.@threads for index in 1:num_central_nodes_verifier #uncomment this line for multi threading + for index in 1:num_central_nodes_verifier #comment this line for multi threading # Builds a spanning tree adjacency_graph_list[index] = build_span_tree( num_nodes, @@ -56,14 +55,47 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central ) end - adjacency_graph_ac_tuple_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] - @show adjacency_graph_ac_tuple_star + adjacency_graph_star, algebraic_connectivity_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] + @show adjacency_graph_star, algebraic_connectivity_star elseif augment_budget >= num_nodes print("Under construction") end else - print("Under construction") + if (num_edges_existing == num_nodes - 1) && (is_base_graph_connected) + # Pose-graph SLAM code goes here + + # Fiedler vector of base graph + fiedler_vector_base_graph = fiedler_vector(adjacency_base_graph) + + # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) + edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + + for (index, edge) in enumerate(edge_augment_list) + edge_fiedlerweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)] * (fiedler_vector_base_graph[src(edge)] - fiedler_vector_base_graph[dst(edge)])^2) + end + + # Sorting the list of tuples in descending order of fiedler weights + sorted_edge_fiedlerweight_list = sort(edge_fiedlerweight_list, by = x -> x[2], rev = true) + + # Base graph + G = SimpleGraph(adjacency_base_graph) + + # Adding edges based on fiedler weights to construct initial graph with required edges + for i = 1:augment_budget + add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) + end + + adjacency_graph_star, algebraic_connectivity_star = refinement_tree( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edge_fiedlerweight_list, + kopt_parameter, + ) + + @show adjacency_graph_star, algebraic_connectivity_star + end end return @@ -320,4 +352,64 @@ function refinement_span_tree( updated_adjacency_graph_ac_tuple = update(G, adjacency_augment_graph, adjacency_graph_ac_tuple) end return updated_adjacency_graph_ac_tuple +end + +function refinement_tree( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edge_fiedlerweight_list, + kopt_parameter, + ) + + if kopt_parameter == 1 + for i in eachindex(sorted_edge_fiedlerweight_list) + if !(has_edge(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1]))) + add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) + algebraic_connectivity_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef, augment_budget + 1) + ite = 1 + for edge in edges(G) + if adjacency_base_graph[src(edge), dst(edge)] == 0 + rem_edge!(G, src(edge), dst(edge)) + algebraic_connectivity_list[ite] = (edge, algebraic_connectivity(adjacency_whole_graph(adjacency_augment_graph,adjacency_base_graph) .* Matrix(adjacency_matrix(G)))) + ite += 1 + add_edge!(G, src(edge), dst(edge)) + end + end + rem_edge!(G, src(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1]), dst(sort(g, by=x -> x[2], rev=true)[1][1])) + end + end + + elseif kopt_parameter == 2 + combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) + for i in eachindex(combinations) + if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && + !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) + add_edge!(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])) + add_edge!(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])) + algebraic_connectivity_list = Vector{Tuple{Tuple{Graphs.SimpleGraphs.SimpleEdge,Graphs.SimpleGraphs.SimpleEdge},Float64}}(undef, length(combinations(1:n1+2, 2))) + ite = 1 + edge_list = collect(edges(G)) + for j in eachindex(length(edge_list)) + for k in j+1:length(edge_list) + if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) + rem_edge!(G, src(edge_list[j]), dst(edge_list[j])) + rem_edge!(G, src(edge_list[k]), dst(edge_list[k])) + algebraic_connectivity_list[ite] = ((edge_list[j], edge_list[k]), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G)))) + ite += 1 + add_edge!(G, src(edge_list[j]), dst(edge_list[j])) + add_edge!(G, src(edge_list[k]), dst(edge_list[k])) + end + end + end + rem_edge!(G, src(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1][1]), dst(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1][1])) + rem_edge!(G, src(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1][2]), dst(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1][2])) + end + end + + elseif kopt_parameter == 3 + combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + end + + return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) end \ No newline at end of file From 2a880625842e5d22732e2fb62c79ecd7cc0addb1 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Thu, 10 Aug 2023 14:48:22 -0600 Subject: [PATCH 09/35] yet to fix heuristics --- examples/run_examples.jl | 6 +- src/heuristics.jl | 361 +++++++++++++++++++++------------------ src/lopt_model.jl | 8 +- src/types.jl | 3 + src/utility.jl | 31 +++- 5 files changed, 234 insertions(+), 175 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index b0ff9c5..ff99d90 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -44,8 +44,8 @@ end #-------------------------------# # User-defined params # #-------------------------------# -num_nodes = 8 -instance = 5 +num_nodes = 10 +instance = 4 data_dict, augment_budget = data_I(num_nodes, instance) params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -60,7 +60,7 @@ model_options = Dict{Symbol,Any}( :eigen_cuts_sizes => [num_nodes], :topology_flow_cuts => true, :solution_type => "heuristic", - :kopt_parameter => 2, + :kopt_parameter => 3, :num_central_nodes_verifier => 5, ) diff --git a/src/heuristics.jl b/src/heuristics.jl index 784f0fd..0cce02f 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,10 +1,9 @@ -# -- UNDER CONSTRUCTION -- # # Heuristics to maximize algebraic connectivity of weighted graphs using Graphs using LinearAlgebra -function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central_nodes_verifier::Int) +function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central_nodes_verifier::Int, num_kopt_swaps_upperbound::Int) num_nodes = data["num_nodes"] adjacency_augment_graph = data["adjacency_augment_graph"] adjacency_base_graph = data["adjacency_base_graph"] @@ -17,6 +16,7 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central # Making list of tuples of edges and edge weights edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + sorted_edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) edge_edgeweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)]) @@ -24,21 +24,19 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central # Sorting the list of tuples in descending order of edge weights sorted_edge_edgeweight_list = sort(edge_edgeweight_list, by = x -> x[2], rev = true) - #@show sorted_edge_edgeweight_list if num_edges_existing == 0 if augment_budget == num_nodes - 1 #spanning tree # Priority central nodes based on sum of edge weights priority_central_nodes_list = priority_central_nodes(adjacency_augment_graph, num_nodes) - #@show priority_central_nodes_list # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_verifier) - #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) - #Threads.@threads for index in 1:num_central_nodes_verifier #uncomment this line for multi threading - for index in 1:num_central_nodes_verifier #comment this line for multi threading + JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) + Threads.@threads for index in 1:num_central_nodes_verifier #uncomment this line for multi threading + # for index in 1:num_central_nodes_verifier #comment this line for multi threading # Builds a spanning tree adjacency_graph_list[index] = build_span_tree( num_nodes, @@ -52,24 +50,24 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central sorted_edge_edgeweight_list, adjacency_graph_list[index], kopt_parameter, + num_kopt_swaps_upperbound, ) end adjacency_graph_star, algebraic_connectivity_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] - @show adjacency_graph_star, algebraic_connectivity_star elseif augment_budget >= num_nodes print("Under construction") end else - if (num_edges_existing == num_nodes - 1) && (is_base_graph_connected) - # Pose-graph SLAM code goes here + if is_base_graph_connected # Fiedler vector of base graph fiedler_vector_base_graph = fiedler_vector(adjacency_base_graph) # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + sorted_edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) edge_fiedlerweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)] * (fiedler_vector_base_graph[src(edge)] - fiedler_vector_base_graph[dst(edge)])^2) @@ -92,12 +90,15 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central adjacency_augment_graph, sorted_edge_fiedlerweight_list, kopt_parameter, + num_kopt_swaps_upperbound, ) - - @show adjacency_graph_star, algebraic_connectivity_star + + elseif !(is_base_graph_connected) + print("Under construction") end end - + # Implement the bridge to results here + @show adjacency_graph_star, algebraic_connectivity_star return end @@ -110,8 +111,8 @@ function build_span_tree( ) G = SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph - uncon_nodes_set = Int64[] # Set contains all unconnected nodes - uncon_nodes_set = collect(1:num_nodes) + uncon_set = Int64[] # Set contains all unconnected nodes + uncon_set = collect(1:num_nodes) dir_con_set = Int64[] # Set contains nodes which are directly connected to central node sec_con_set = Int64[] # Set contains nodes which are connected to nodes in dir_con_set @@ -126,44 +127,43 @@ function build_span_tree( dst(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, src(sorted_edge_edgeweight_list[j][1])) # Removing from unconnected set - deleteat!(uncon_nodes_set, uncon_nodes_set .== src(sorted_edge_edgeweight_list[j][1])) - deleteat!(uncon_nodes_set, uncon_nodes_set .== dst(sorted_edge_edgeweight_list[j][1])) + deleteat!(uncon_set, uncon_set .== src(sorted_edge_edgeweight_list[j][1])) + deleteat!(uncon_set, uncon_set .== dst(sorted_edge_edgeweight_list[j][1])) break end end - #@show connected_components(G) while !is_connected(G) # Connecting nodes until a spanning tree is formed - algebraic_connectivity_list = Vector{Float64}(undef, size(dir_con_set)[1] + 1) - algebraic_connectivity_ranker = Int64[] + algebraic_connectivity_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,1) for j in 1:(size(dir_con_set)[1]+1) for k in eachindex(sorted_edge_edgeweight_list) if j == 1 - if (src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) || - (dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + if (src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || + (dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_set) # Adding edge to central node add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) # Storing current algebraic connectivity - algebraic_connectivity_list[j] = algebraic_connectivity((weighted_adj_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_nodes_set)[1] + 1))) + algebraic_connectivity_tracker = ((src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])),algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) - # Removing the added edge rem_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) break end else - if (src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) || - (dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + if (src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || + (dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_set) # Adding edge to directly connected node add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) - # Storing current algebraic connectivity - algebraic_connectivity_list[j] = algebraic_connectivity((weighted_adj_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_nodes_set)[1] + 1))) - + # Updating current algebraic connectivity + if algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1))) > algebraic_connectivity_tracker[2] + algebraic_connectivity_tracker = ((src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])),algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + end + # Removing the added edge rem_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) break @@ -172,65 +172,41 @@ function build_span_tree( end end - #Sorting algebraic connectivities to choose the best incumbent edge - algebraic_connectivity_ranker = sortperm(algebraic_connectivity_list, rev = true)[1] - - for k in eachindex(sorted_edge_edgeweight_list) - if algebraic_connectivity_ranker == 1 - if (src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) - - # Adding edge to central node - add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) - - # Adding node to directly connected set - push!(dir_con_set, dst(sorted_edge_edgeweight_list[k][1])) - - # Removing node from unconnected nodes set - deleteat!(uncon_nodes_set, uncon_nodes_set .== dst(sorted_edge_edgeweight_list[k][1])) - break - - elseif (dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) - - # Adding edge to central node - add_edge!(G, dst(sorted_edge_edgeweight_list[k][1]), src(sorted_edge_edgeweight_list[k][1])) - - # Adding node to directly connected set - push!(dir_con_set, src(sorted_edge_edgeweight_list[k][1])) - - # Removing node from unconnected nodes set - deleteat!(uncon_nodes_set, uncon_nodes_set .== src(sorted_edge_edgeweight_list[k][1])) - break - end - else - if (src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[algebraic_connectivity_ranker - 1]) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + # Adding edge + add_edge!(G, algebraic_connectivity_tracker[1][1], algebraic_connectivity_tracker[1][2]) + + if (algebraic_connectivity_tracker[1][1] == central_node) && issubset(algebraic_connectivity_tracker[1][2], uncon_set) + # Adding node to directly connected set + push!(dir_con_set, algebraic_connectivity_tracker[1][2]) - # Adding edge to directly connected node - add_edge!(G, dir_con_set[algebraic_connectivity_ranker - 1], dst(sorted_edge_edgeweight_list[k][1])) + # Removing node from unconnected nodes set + deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][2]) - # Adding node to secondary connected set - push!(sec_con_set, dst(sorted_edge_edgeweight_list[k][1])) + elseif (algebraic_connectivity_tracker[1][2] == central_node) && issubset(algebraic_connectivity_tracker[1][1], uncon_set) + # Adding node to directly connected set + push!(dir_con_set, algebraic_connectivity_tracker[1][1]) - # Removing node from unconnected nodes set - deleteat!(uncon_nodes_set, uncon_nodes_set .== dst(sorted_edge_edgeweight_list[k][1])) - break + # Removing node from unconnected nodes set + deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][1]) - elseif (dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[algebraic_connectivity_ranker - 1]) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_nodes_set) + elseif issubset(algebraic_connectivity_tracker[1][1], dir_con_set) && issubset(algebraic_connectivity_tracker[1][2], uncon_set) - # Adding edge to directly connected node - add_edge!(G, dir_con_set[algebraic_connectivity_ranker - 1], src(sorted_edge_edgeweight_list[k][1])) + # Adding node to secondary connected set + push!(sec_con_set, algebraic_connectivity_tracker[1][2]) - # Adding node to secondary connected set - push!(sec_con_set, src(sorted_edge_edgeweight_list[k][1])) + # Removing node from unconnected nodes set + deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][2]) - # Removing node from unconnected nodes set - deleteat!(uncon_nodes_set, uncon_nodes_set .== src(sorted_edge_edgeweight_list[k][1])) - break - end - end + elseif issubset(algebraic_connectivity_tracker[1][2], dir_con_set) && issubset(algebraic_connectivity_tracker[1][1], uncon_set) + # Adding node to secondary connected set + push!(sec_con_set, algebraic_connectivity_tracker[1][1]) + + # Removing node from unconnected nodes set + deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][1]) + end - #@show connected_components(G) + end - #@show G return (Matrix(adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) end @@ -239,102 +215,110 @@ function refinement_span_tree( sorted_edge_edgeweight_list, adjacency_graph_ac_tuple, kopt_parameter, + num_kopt_swaps_upperbound, ) G = SimpleGraph(adjacency_graph_ac_tuple[1]) # Refinement of Algebraic connectivity if kopt_parameter == 1 - cycle_basis_list = Vector{Int64} + cycle_basis_current = Vector{Int64} for i in eachindex(sorted_edge_edgeweight_list) if add_edge!(G, src(sorted_edge_edgeweight_list[i][1]), dst(sorted_edge_edgeweight_list[i][1])) if is_cyclic(G) - cycle_basis_list = cycle_basis(G)[1] - algebraic_connectivity_list = Vector{Float64}(undef, 0) - vertices_list = Vector{Any}(undef, 0) - for j = 1:(length(cycle_basis_list)-1) - for k = (j+1):length(cycle_basis_list) - if rem_edge!(G, cycle_basis_list[j], cycle_basis_list[k]) - push!(algebraic_connectivity_list, algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) - push!(vertices_list, (cycle_basis_list[j], cycle_basis_list[k])) - add_edge!(G, cycle_basis_list[j], cycle_basis_list[k]) + cycle_basis_current = cycle_basis(G)[1] + algebraic_connectivity_tracker = 0.0 + vertices_tracker = (undef, undef) + for j = 1:(length(cycle_basis_current)-1) + for k = (j+1):length(cycle_basis_current) + if rem_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) + if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + vertices_tracker = (cycle_basis_current[j], cycle_basis_current[k]) + end + add_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) end end end - rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2]) + rem_edge!(G, vertices_tracker[1], vertices_tracker[2]) end end end - updated_adjacency_graph_ac_tuple = update(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 2 combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) - cycle_basis_list = Vector{Int64} - for i in eachindex(combinations) + cycle_basis_current = Vector{Int64} + if length(combinations) <= num_kopt_swaps_upperbound + num_swaps = length(combinations) + else + num_swaps = num_kopt_swaps_upperbound + end + for i in 1:num_swaps if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 - add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])) - add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])) + G = add_multiple_edges!(G, [(src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1]))]) if is_cyclic(G) - cycle_basis_list = cycle_basis(G) - algebraic_connectivity_list = Vector{Float64}(undef, 0) - vertices_list = Vector{Any}(undef, 0) - for j in 1:(length(cycle_basis_list[1])-1) - for k in (j+1):length(cycle_basis_list[1]) - for l in 1:(length(cycle_basis_list[2])-1) - for m in (l+1):length(cycle_basis_list[2]) - if (cycle_basis_list[1][j], cycle_basis_list[1][k]) !== (cycle_basis_list[2][l], cycle_basis_list[2][m]) - if adjacency_matrix(G)[cycle_basis_list[1][j], cycle_basis_list[1][k]] == 1 && adjacency_matrix(G)[cycle_basis_list[2][l], cycle_basis_list[2][m]] == 1 - rem_edge!(G, cycle_basis_list[1][j], cycle_basis_list[1][k]) - rem_edge!(G, cycle_basis_list[2][l], cycle_basis_list[2][m]) - push!(algebraic_connectivity_list, algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) - push!(vertices_list, ((cycle_basis_list[1][j], cycle_basis_list[1][k]), (cycle_basis_list[2][l], cycle_basis_list[2][m]))) - add_edge!(G, cycle_basis_list[1][j], cycle_basis_list[1][k]) - add_edge!(G, cycle_basis_list[2][l], cycle_basis_list[2][m]) + cycle_basis_current = cycle_basis(G) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = ((undef, undef), (undef, undef)) + for j in 1:(length(cycle_basis_current[1])-1) + for k in (j+1):length(cycle_basis_current[1]) + for l in 1:(length(cycle_basis_current[2])-1) + for m in (l+1):length(cycle_basis_current[2]) + if (cycle_basis_current[1][j], cycle_basis_current[1][k]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) + if adjacency_matrix(G)[cycle_basis_current[1][j], cycle_basis_current[1][k]] == 1 && adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 + G = rem_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m])]) + if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + vertices_tracker = ((cycle_basis_current[1][j], cycle_basis_current[1][k]), (cycle_basis_current[2][l], cycle_basis_current[2][m])) + end + G = add_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m])]) end end end end end end - rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1][2]) - rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2][2]) + G = rem_multiple_edges!(G, [(vertices_tracker[1][1], vertices_tracker[1][2]),(vertices_tracker[2][1], vertices_tracker[2][2])]) end end end - updated_adjacency_graph_ac_tuple = update(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 3 combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) - cycle_basis_list = Vector{Int64} - for i in eachindex(combinations) + cycle_basis_current = Vector{Int64} + if length(combinations) <= num_kopt_swaps_upperbound + num_swaps = length(combinations) + else + num_swaps = num_kopt_swaps_upperbound + end + + for i in 1:num_swaps if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 && adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1])] == 0 - add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])) - add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])) - add_edge!(G, src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1])) + G = add_multiple_edges!(G, [(src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])),(src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1]))]) if is_cyclic(G) - cycle_basis_list = cycle_basis(G) - algebraic_connectivity_list = Vector{Float64}(undef, 0) - vertices_list = Vector{Any}(undef, 0) - for j in 1:(length(cycle_basis_list[1])-1) - for k in (j+1):length(cycle_basis_list[1]) - for l in 1:(length(cycle_basis_list[2])-1) - for m in (l+1):length(cycle_basis_list[2]) - for p in 1:(length(cycle_basis_list[3])-1) - for q in (p+1):length(cycle_basis_list[3]) - if (cycle_basis_list[1][j], cycle_basis_list[1][k]) !== (cycle_basis_list[2][l], cycle_basis_list[2][m]) && - (cycle_basis_list[1][j], cycle_basis_list[1][k]) !== (cycle_basis_list[3][p], cycle_basis_list[3][q]) && - (cycle_basis_list[3][p], cycle_basis_list[3][q]) !== (cycle_basis_list[2][l], cycle_basis_list[2][m]) - if adjacency_matrix(G)[cycle_basis_list[1][j], cycle_basis_list[1][k]] == 1 && adjacency_matrix(G)[cycle_basis_list[2][l], cycle_basis_list[2][m]] == 1 && adjacency_matrix(G)[cycle_basis_list[3][p], cycle_basis_list[3][q]] == 1 - rem_edge!(G, cycle_basis_list[1][j], cycle_basis_list[1][k]) - rem_edge!(G, cycle_basis_list[2][l], cycle_basis_list[2][m]) - rem_edge!(G, cycle_basis_list[3][p], cycle_basis_list[3][q]) - push!(algebraic_connectivity_list, algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) - push!(vertices_list, ((cycle_basis_list[1][j], cycle_basis_list[1][k]), (cycle_basis_list[2][l], cycle_basis_list[2][m]), (cycle_basis_list[3][p], cycle_basis_list[3][q]))) - add_edge!(G, cycle_basis_list[1][j], cycle_basis_list[1][k]) - add_edge!(G, cycle_basis_list[2][l], cycle_basis_list[2][m]) - add_edge!(G, cycle_basis_list[3][p], cycle_basis_list[3][q]) + cycle_basis_current = cycle_basis(G) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = ((undef, undef), (undef, undef), (undef, undef)) + for j in 1:(length(cycle_basis_current[1])-1) + for k in (j+1):length(cycle_basis_current[1]) + for l in 1:(length(cycle_basis_current[2])-1) + for m in (l+1):length(cycle_basis_current[2]) + for p in 1:(length(cycle_basis_current[3])-1) + for q in (p+1):length(cycle_basis_current[3]) + if (cycle_basis_current[1][j], cycle_basis_current[1][k]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) && + (cycle_basis_current[1][j], cycle_basis_current[1][k]) !== (cycle_basis_current[3][p], cycle_basis_current[3][q]) && + (cycle_basis_current[3][p], cycle_basis_current[3][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) + if adjacency_matrix(G)[cycle_basis_current[1][j], cycle_basis_current[1][k]] == 1 && adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && adjacency_matrix(G)[cycle_basis_current[3][p], cycle_basis_current[3][q]] == 1 + G = rem_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[3][p], cycle_basis_current[3][q])]) + if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + vertices_tracker = ((cycle_basis_current[1][j], cycle_basis_current[1][k]), (cycle_basis_current[2][l], cycle_basis_current[2][m]), (cycle_basis_current[3][p], cycle_basis_current[3][q])) + end + G = add_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[3][p], cycle_basis_current[3][q])]) end end end @@ -343,13 +327,11 @@ function refinement_span_tree( end end end - rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][1][2]) - rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][2][2]) - rem_edge!(G, vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][3][1], vertices_list[sortperm(algebraic_connectivity_list, rev=true)[1]][3][2]) + G = rem_multiple_edges!(G, [(vertices_tracker[1][1], vertices_tracker[1][2]),(vertices_tracker[2][1], vertices_tracker[2][2]),(vertices_tracker[3][1], vertices_tracker[3][2])]) end end end - updated_adjacency_graph_ac_tuple = update(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) end return updated_adjacency_graph_ac_tuple end @@ -360,56 +342,111 @@ function refinement_tree( adjacency_augment_graph, sorted_edge_fiedlerweight_list, kopt_parameter, + num_kopt_swaps_upperbound, ) if kopt_parameter == 1 for i in eachindex(sorted_edge_fiedlerweight_list) if !(has_edge(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1]))) add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) - algebraic_connectivity_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef, augment_budget + 1) - ite = 1 + algebraic_connectivity_tracker = 0.0 + vertices_tracker = (undef, undef) for edge in edges(G) if adjacency_base_graph[src(edge), dst(edge)] == 0 rem_edge!(G, src(edge), dst(edge)) - algebraic_connectivity_list[ite] = (edge, algebraic_connectivity(adjacency_whole_graph(adjacency_augment_graph,adjacency_base_graph) .* Matrix(adjacency_matrix(G)))) - ite += 1 + if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + vertices_tracker = edge + end add_edge!(G, src(edge), dst(edge)) end end - rem_edge!(G, src(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1]), dst(sort(g, by=x -> x[2], rev=true)[1][1])) + rem_edge!(G, vertices_tracker) end end elseif kopt_parameter == 2 combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) - for i in eachindex(combinations) + if length(combinations) <= num_kopt_swaps_upperbound + num_swaps = length(combinations) + else + num_swaps = num_kopt_swaps_upperbound + end + for i in 1:num_swaps if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) - add_edge!(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])) - add_edge!(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])) - algebraic_connectivity_list = Vector{Tuple{Tuple{Graphs.SimpleGraphs.SimpleEdge,Graphs.SimpleGraphs.SimpleEdge},Float64}}(undef, length(combinations(1:n1+2, 2))) - ite = 1 + G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = ((undef, undef),(undef, undef)) edge_list = collect(edges(G)) - for j in eachindex(length(edge_list)) + for j in eachindex(length(edge_list)-1) for k in j+1:length(edge_list) if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) - rem_edge!(G, src(edge_list[j]), dst(edge_list[j])) - rem_edge!(G, src(edge_list[k]), dst(edge_list[k])) - algebraic_connectivity_list[ite] = ((edge_list[j], edge_list[k]), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G)))) - ite += 1 - add_edge!(G, src(edge_list[j]), dst(edge_list[j])) - add_edge!(G, src(edge_list[k]), dst(edge_list[k])) + G = rem_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k]))]) + if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + vertices_tracker = (edge_list[j], edge_list[k]) + end + G = add_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k]))]) end end end - rem_edge!(G, src(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1][1]), dst(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1][1])) - rem_edge!(G, src(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1][2]), dst(sort(algebraic_connectivity_list, by=x -> x[2], rev=true)[1][1][2])) + G = rem_multiple_edges!(G, [(src(vertices_tracker[1]), dst(vertices_tracker[1])), (src(vertices_tracker[2]), dst(vertices_tracker[2]))]) end end elseif kopt_parameter == 3 combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + if length(combinations) <= num_kopt_swaps_upperbound + num_swaps = length(combinations) + else + num_swaps = num_kopt_swaps_upperbound + end + for i in 1:num_swaps + if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && + !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) && + !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))) + G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])), (src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))]) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = ((undef, undef), (undef, undef), (undef, undef)) + edge_list = collect(edges(G)) + + for j in eachindex(length(edge_list)-2) + for k in j+1:(length(edge_list)-1) + for l in k+1:length(edge_list) + if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) + G = rem_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))]) + if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + vertices_tracker = (edge_list[j], edge_list[k]) + end + G = add_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))]) + end + end + end + end + G = rem_multiple_edges!(G, [(src(vertices_tracker[1]), dst(vertices_tracker[1])), (src(vertices_tracker[2]), dst(vertices_tracker[2])), (src(vertices_tracker[3]), dst(vertices_tracker[3]))]) + end + end end return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) +end + +function graph_two_vertex_tracker(G, edge_list) + + for k in j+1:(length(edge_list)-1) + for l in k+1:length(edge_list) + if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) + G = rem_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))]) + if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + vertices_tracker = (edge_list[j], edge_list[k]) + end + G = add_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))]) + end + end + end + + return algebraic_connectivity_tracker, vertices_tracker end \ No newline at end of file diff --git a/src/lopt_model.jl b/src/lopt_model.jl index 6d3e5e2..dd53985 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -24,7 +24,7 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.objective_LOModel(lom) elseif lom.options.solution_type == "heuristic" - LOpt.heuristic_kopt(lom.data, lom.options.kopt_parameter, lom.options.num_central_nodes_verifier) + LOpt.heuristic_kopt(lom.data, lom.options.kopt_parameter, lom.options.num_central_nodes_verifier, lom.options.num_kopt_swaps_upperbound) end elseif lom.options.formulation_type == "max_span_tree" if lom.options.solution_type in ["optimal", "heuristic"] @@ -88,7 +88,7 @@ function optimize_LOModel!(lom::LaplacianOptModel; optimizer = nothing) end if JuMP.mode(lom.model) != JuMP.DIRECT && - lom.model.moi_backend.state == MOI.Utilities.NO_OPTIMIZER + lom.model.moi_backend.state == MOI.Utilities.NO_OPTIMIZER Memento.error( _LOGGER, "No optimizer specified in `optimize_LOModel!` or the given JuMP model.", @@ -172,8 +172,8 @@ end function lazycallback_status(lom::LaplacianOptModel) if ( - size(lom.options.eigen_cuts_sizes)[1] > 0 && - minimum(lom.options.eigen_cuts_sizes) >= 2 + size(lom.options.eigen_cuts_sizes)[1] > 0 && + minimum(lom.options.eigen_cuts_sizes) >= 2 ) || lom.options.topology_flow_cuts || lom.options.soc_linearized_cuts || diff --git a/src/types.jl b/src/types.jl index 8a4f500..ab554d3 100644 --- a/src/types.jl +++ b/src/types.jl @@ -23,6 +23,7 @@ mutable struct LaplacianOptModelOptions kopt_parameter::Int64 num_central_nodes_verifier::Int64 + num_kopt_swaps_upperbound::Int64 best_lower_bound::Float64 best_incumbent::Union{Vector{Tuple{Int64,Int64}},Nothing} @@ -54,6 +55,7 @@ function get_default_options() kopt_parameter = 2 # Integer value >= 1 num_central_nodes_verifier = 5 # Integer value >= 1 + num_kopt_swaps_upperbound = 1E6 # Integer value best_lower_bound = 0 # Best known feasible solution's objective best_incumbent = nothing @@ -77,6 +79,7 @@ function get_default_options() sdp_relaxation, kopt_parameter, num_central_nodes_verifier, + num_kopt_swaps_upperbound, best_lower_bound, best_incumbent, tol_zero, diff --git a/src/utility.jl b/src/utility.jl index dd06959..8a7e7f6 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -352,14 +352,14 @@ end """ - weighted_adj_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, size::Int64) + weighted_adjacency_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, size::Int64) Returns a weighted adjacency matrix for the connected part of graph. """ -function weighted_adj_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, size::Int64) +function weighted_adjacency_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, size::Int64) weighted_adj_matrix_size = Matrix{Float64}(undef, size, size) #collecting vertices connected in graph @@ -377,17 +377,16 @@ function weighted_adj_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjac end end - #@show weighted_adj_matrix_size return weighted_adj_matrix_size end """ - update(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},adjacency_augment_graph::Array{<:Number},adjacency_graph_ac_tuple::Tuple{Array,Float};tol = 1E-6,) + update_kopt_adjacency!(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},adjacency_augment_graph::Array{<:Number},adjacency_graph_ac_tuple::Tuple{Array,Float};tol = 1E-6,) -Returns an updated adjacency_graph_ac_tuple after the kopt refiniement. +Returns an updated adjacency_graph_ac_tuple after the kopt refinement. """ -function update( +function update_kopt_adjacency!( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, adjacency_graph_ac_tuple::Tuple{Array,Float64}; @@ -431,4 +430,24 @@ function edge_combinations(num_edges::Int64, kopt_parameter::Int64) end end return combinations +end + +function add_multiple_edges!( + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + edge_list::Vector{Tuple{Int64,Int64}}, +) + for i in 1:length(edge_list) + add_edge!(G, edge_list[i][1], edge_list[i][2]) + end + return G +end + +function rem_multiple_edges!( + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + edge_list::Vector{Tuple{Int64,Int64}}, +) + for i in 1:length(edge_list) + rem_edge!(G, edge_list[i][1], edge_list[i][2]) + end + return G end \ No newline at end of file From 41da706dc303fb0b4018e2cab14d10e81046485f Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Thu, 10 Aug 2023 16:47:34 -0600 Subject: [PATCH 10/35] updated heuristic and added tests --- examples/run_examples.jl | 6 +-- src/heuristics.jl | 93 +++++++++++++++++++--------------------- src/utility.jl | 31 ++++++++++---- test/heuristics_tests.jl | 76 ++++++++++++++++++++++++++++++-- 4 files changed, 142 insertions(+), 64 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index ff99d90..ed6c9c5 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -44,8 +44,8 @@ end #-------------------------------# # User-defined params # #-------------------------------# -num_nodes = 10 -instance = 4 +num_nodes = 5 +instance = 1 data_dict, augment_budget = data_I(num_nodes, instance) params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -60,7 +60,7 @@ model_options = Dict{Symbol,Any}( :eigen_cuts_sizes => [num_nodes], :topology_flow_cuts => true, :solution_type => "heuristic", - :kopt_parameter => 3, + :kopt_parameter => 1, :num_central_nodes_verifier => 5, ) diff --git a/src/heuristics.jl b/src/heuristics.jl index 0cce02f..4721724 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -11,6 +11,10 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central augment_budget = data["augment_budget"] is_base_graph_connected = data["is_base_graph_connected"] + # Initializing the heuristic solutions + adjacency_graph_star = Matrix{Float64}(zeros(num_nodes, num_nodes)) + algebraic_connectivity_star = 0 + # Making list of all possible edges to augment edge_augment_list = collect(edges(SimpleGraph(adjacency_augment_graph))) @@ -34,9 +38,9 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_verifier) - JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) - Threads.@threads for index in 1:num_central_nodes_verifier #uncomment this line for multi threading - # for index in 1:num_central_nodes_verifier #comment this line for multi threading + #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) + #Threads.@threads for index in 1:num_central_nodes_verifier #uncomment this line for multi threading + for index in 1:num_central_nodes_verifier #comment this line for multi threading # Builds a spanning tree adjacency_graph_list[index] = build_span_tree( num_nodes, @@ -227,19 +231,19 @@ function refinement_span_tree( if is_cyclic(G) cycle_basis_current = cycle_basis(G)[1] algebraic_connectivity_tracker = 0.0 - vertices_tracker = (undef, undef) + vertices_tracker = [(undef, undef)] for j = 1:(length(cycle_basis_current)-1) for k = (j+1):length(cycle_basis_current) if rem_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) - vertices_tracker = (cycle_basis_current[j], cycle_basis_current[k]) + vertices_tracker = [(cycle_basis_current[j], cycle_basis_current[k])] end add_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) end end end - rem_edge!(G, vertices_tracker[1], vertices_tracker[2]) + rem_edge!(G, vertices_tracker[1][1], vertices_tracker[1][2]) end end end @@ -260,7 +264,7 @@ function refinement_span_tree( if is_cyclic(G) cycle_basis_current = cycle_basis(G) algebraic_connectivity_tracker = 0.0 - vertices_tracker = ((undef, undef), (undef, undef)) + vertices_tracker = [(undef, undef), (undef, undef)] for j in 1:(length(cycle_basis_current[1])-1) for k in (j+1):length(cycle_basis_current[1]) for l in 1:(length(cycle_basis_current[2])-1) @@ -270,7 +274,7 @@ function refinement_span_tree( G = rem_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m])]) if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) - vertices_tracker = ((cycle_basis_current[1][j], cycle_basis_current[1][k]), (cycle_basis_current[2][l], cycle_basis_current[2][m])) + vertices_tracker = [(cycle_basis_current[1][j], cycle_basis_current[1][k]), (cycle_basis_current[2][l], cycle_basis_current[2][m])] end G = add_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m])]) end @@ -279,7 +283,7 @@ function refinement_span_tree( end end end - G = rem_multiple_edges!(G, [(vertices_tracker[1][1], vertices_tracker[1][2]),(vertices_tracker[2][1], vertices_tracker[2][2])]) + G = rem_multiple_edges!(G, vertices_tracker) end end end @@ -302,7 +306,7 @@ function refinement_span_tree( if is_cyclic(G) cycle_basis_current = cycle_basis(G) algebraic_connectivity_tracker = 0.0 - vertices_tracker = ((undef, undef), (undef, undef), (undef, undef)) + vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] for j in 1:(length(cycle_basis_current[1])-1) for k in (j+1):length(cycle_basis_current[1]) for l in 1:(length(cycle_basis_current[2])-1) @@ -316,7 +320,7 @@ function refinement_span_tree( G = rem_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[3][p], cycle_basis_current[3][q])]) if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) - vertices_tracker = ((cycle_basis_current[1][j], cycle_basis_current[1][k]), (cycle_basis_current[2][l], cycle_basis_current[2][m]), (cycle_basis_current[3][p], cycle_basis_current[3][q])) + vertices_tracker = [(cycle_basis_current[1][j], cycle_basis_current[1][k]), (cycle_basis_current[2][l], cycle_basis_current[2][m]), (cycle_basis_current[3][p], cycle_basis_current[3][q])] end G = add_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[3][p], cycle_basis_current[3][q])]) end @@ -327,7 +331,7 @@ function refinement_span_tree( end end end - G = rem_multiple_edges!(G, [(vertices_tracker[1][1], vertices_tracker[1][2]),(vertices_tracker[2][1], vertices_tracker[2][2]),(vertices_tracker[3][1], vertices_tracker[3][2])]) + G = rem_multiple_edges!(G, vertices_tracker) end end end @@ -377,21 +381,9 @@ function refinement_tree( !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) algebraic_connectivity_tracker = 0.0 - vertices_tracker = ((undef, undef),(undef, undef)) - edge_list = collect(edges(G)) - for j in eachindex(length(edge_list)-1) - for k in j+1:length(edge_list) - if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) - G = rem_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k]))]) - if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) - vertices_tracker = (edge_list[j], edge_list[k]) - end - G = add_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k]))]) - end - end - end - G = rem_multiple_edges!(G, [(src(vertices_tracker[1]), dst(vertices_tracker[1])), (src(vertices_tracker[2]), dst(vertices_tracker[2]))]) + vertices_tracker = [(undef, undef),(undef, undef)] + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_two_edges!(G, 0, algebraic_connectivity_tracker,vertices_tracker) + G = rem_multiple_edges!(G, vertices_tracker) end end @@ -408,24 +400,13 @@ function refinement_tree( !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))) G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])), (src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))]) algebraic_connectivity_tracker = 0.0 - vertices_tracker = ((undef, undef), (undef, undef), (undef, undef)) - edge_list = collect(edges(G)) - - for j in eachindex(length(edge_list)-2) - for k in j+1:(length(edge_list)-1) - for l in k+1:length(edge_list) - if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) - G = rem_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))]) - if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) - vertices_tracker = (edge_list[j], edge_list[k]) - end - G = add_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))]) - end - end + vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] + for j in eachindex(length(collect(edges(G))) - 2) + if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_two_edges!(G, j, algebraic_connectivity_tracker,vertices_tracker) end end - G = rem_multiple_edges!(G, [(src(vertices_tracker[1]), dst(vertices_tracker[1])), (src(vertices_tracker[2]), dst(vertices_tracker[2])), (src(vertices_tracker[3]), dst(vertices_tracker[3]))]) + G = rem_multiple_edges!(G, vertices_tracker) end end end @@ -433,20 +414,32 @@ function refinement_tree( return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) end -function graph_two_vertex_tracker(G, edge_list) - +function vertices_tracker_two_edges!( + G, + j, # Index for third edge if kopt_parameter is 3 + algebraic_connectivity_tracker, + vertices_tracker + ) + edge_list = collect(edges(G)) for k in j+1:(length(edge_list)-1) for l in k+1:length(edge_list) - if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) - G = rem_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))]) + edges_to_check = [] + if j == 0 + edges_to_check = [(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] + else + edges_to_check = [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] + end + if (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) + G = rem_multiple_edges!(G, edges_to_check) if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) - vertices_tracker = (edge_list[j], edge_list[k]) + vertices_tracker = edges_to_check end - G = add_multiple_edges!(G, [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))]) + G = add_multiple_edges!(G, edges_to_check) end end end return algebraic_connectivity_tracker, vertices_tracker -end \ No newline at end of file +end + diff --git a/src/utility.jl b/src/utility.jl index 8a7e7f6..964287e 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -162,9 +162,11 @@ violated eigen value w.r.t positive semi-definiteness of the input matrix. """ function _violated_eigen_vector(W::Array{<:Number}; tol = 1E-6) W_eigvals = LA.eigvals(W) - + @show W_eigvals if typeof(W_eigvals) != Vector{Float64} - Memento.error(_LOGGER, "PSD matrix (W) cannot have complex eigenvalues") + if !(isapprox(imag(W_eigvals), zeros(size(W_eigvals)[1]), atol = 1E-6)) + Memento.error(_LOGGER, "PSD matrix (W) cannot have complex eigenvalues") + end end if LA.eigmin(W) <= -tol @@ -432,22 +434,35 @@ function edge_combinations(num_edges::Int64, kopt_parameter::Int64) return combinations end +""" + add_multiple_edges!(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},edge_set::Vector{Tuple{Int64,Int64}},) + +Returns updated graph by adding edges. +""" + function add_multiple_edges!( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, - edge_list::Vector{Tuple{Int64,Int64}}, + edge_set::Vector{Tuple{Int64,Int64}}, ) - for i in 1:length(edge_list) - add_edge!(G, edge_list[i][1], edge_list[i][2]) + for i in 1:length(edge_set) + add_edge!(G, edge_set[i][1], edge_set[i][2]) end return G end +""" + rem_multiple_edges!(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},edge_set::Vector{Tuple{Int64,Int64}},) + +Returns updated graph by removing edges. +""" + + function rem_multiple_edges!( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, - edge_list::Vector{Tuple{Int64,Int64}}, + edge_set::Vector{Tuple{Int64,Int64}}, ) - for i in 1:length(edge_list) - rem_edge!(G, edge_list[i][1], edge_list[i][2]) + for i in 1:length(edge_set) + rem_edge!(G, edge_set[i][1], edge_set[i][2]) end return G end \ No newline at end of file diff --git a/test/heuristics_tests.jl b/test/heuristics_tests.jl index cdff182..1df5be1 100644 --- a/test/heuristics_tests.jl +++ b/test/heuristics_tests.jl @@ -1,4 +1,4 @@ -@testset "Heuristic test: k-opt" begin +@testset "Heuristic test: 1-opt_spanning_tree" begin num_nodes = 5 instance = 1 data_dict, augment_budget = data_spanning_tree(num_nodes, instance) @@ -6,8 +6,78 @@ params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) - model_options = - Dict{Symbol,Any}(:solution_type => "heuristic", :time_limit => test_time_limit()) + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 1, + :num_central_nodes_verifier => 5, + :time_limit => test_time_limit() + ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + + @test result["solution_type"] == "heuristic" + @test isapprox(result_lo["objective"], 11.1592, atol = 1E-4) + @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) +end + +@testset "Heuristic test: 2-opt_spanning_tree" begin + num_nodes = 5 + instance = 1 + data_dict, augment_budget = data_spanning_tree(num_nodes, instance) + + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) + + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 2, + :num_central_nodes_verifier => 5, + :time_limit => test_time_limit(), + ) + result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + + @test result["solution_type"] == "heuristic" + @test isapprox(result_lo["objective"], 11.1592, atol = 1E-4) + @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) +end + +@testset "Heuristic test: 3-opt_spanning_tree" begin + num_nodes = 5 + instance = 1 + data_dict, augment_budget = data_spanning_tree(num_nodes, instance) + + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) + + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 3, + :num_central_nodes_verifier => 5, + :time_limit => test_time_limit(), + ) + result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + @test result["solution_type"] == "heuristic" + @test isapprox(result_lo["objective"], 11.1592, atol = 1E-4) + @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) end From dbc46e222557d872b028225c6c0989faeaf113f7 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Fri, 11 Aug 2023 12:20:49 -0600 Subject: [PATCH 11/35] updated test cases --- examples/instances/5_nodes/5_11.json | 4 + examples/run_examples.jl | 8 +- src/heuristics.jl | 97 ++++++++++--------- src/utility.jl | 2 +- test/heuristics_tests.jl | 139 +++++++++++++++++++++++++-- 5 files changed, 190 insertions(+), 60 deletions(-) create mode 100644 examples/instances/5_nodes/5_11.json diff --git a/examples/instances/5_nodes/5_11.json b/examples/instances/5_nodes/5_11.json new file mode 100644 index 0000000..7af50f7 --- /dev/null +++ b/examples/instances/5_nodes/5_11.json @@ -0,0 +1,4 @@ +{"edges_to_augment":[[[1,5],5.1749106],[[2,3],6.1501476],[[2,4],13.66149798]], +"num_nodes":5, +"edges_existing":[[[1,2],34.99664955],[[1,3],3.86014785],[[1,4],11.2685551],[[2,5],21.90200648],[[3,4],29.11616571],[[3,5],27.74393888],[[4,5],2.83029978]], +"augment_budget":1} \ No newline at end of file diff --git a/examples/run_examples.jl b/examples/run_examples.jl index ed6c9c5..066eb61 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -24,7 +24,7 @@ function data_I(num_nodes::Int, instance::Int) file_path = joinpath(@__DIR__, "instances/$(num_nodes)_nodes/$(num_nodes)_$(instance).json") data_dict = LOpt.parse_file(file_path) - augment_budget = (num_nodes - 1) # spanning tree constraint + augment_budget = 1 #(num_nodes - 1) # spanning tree constraint return data_dict, augment_budget end @@ -45,7 +45,7 @@ end # User-defined params # #-------------------------------# num_nodes = 5 -instance = 1 +instance = 11 data_dict, augment_budget = data_I(num_nodes, instance) params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -60,8 +60,8 @@ model_options = Dict{Symbol,Any}( :eigen_cuts_sizes => [num_nodes], :topology_flow_cuts => true, :solution_type => "heuristic", - :kopt_parameter => 1, - :num_central_nodes_verifier => 5, + :kopt_parameter => 3, + :num_central_nodes_verifier => 3, ) result = LOpt.run_LOpt( diff --git a/src/heuristics.jl b/src/heuristics.jl index 4721724..5e90ced 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -106,7 +106,6 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central return end - function build_span_tree( num_nodes, adjacency_augment_graph, @@ -265,24 +264,7 @@ function refinement_span_tree( cycle_basis_current = cycle_basis(G) algebraic_connectivity_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef)] - for j in 1:(length(cycle_basis_current[1])-1) - for k in (j+1):length(cycle_basis_current[1]) - for l in 1:(length(cycle_basis_current[2])-1) - for m in (l+1):length(cycle_basis_current[2]) - if (cycle_basis_current[1][j], cycle_basis_current[1][k]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) - if adjacency_matrix(G)[cycle_basis_current[1][j], cycle_basis_current[1][k]] == 1 && adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 - G = rem_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m])]) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) - vertices_tracker = [(cycle_basis_current[1][j], cycle_basis_current[1][k]), (cycle_basis_current[2][l], cycle_basis_current[2][m])] - end - G = add_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m])]) - end - end - end - end - end - end + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, 0, 0, algebraic_connectivity_tracker, vertices_tracker) G = rem_multiple_edges!(G, vertices_tracker) end end @@ -297,37 +279,18 @@ function refinement_span_tree( else num_swaps = num_kopt_swaps_upperbound end - for i in 1:num_swaps if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 && adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1])] == 0 G = add_multiple_edges!(G, [(src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])),(src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1]))]) if is_cyclic(G) - cycle_basis_current = cycle_basis(G) algebraic_connectivity_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in 1:(length(cycle_basis_current[1])-1) - for k in (j+1):length(cycle_basis_current[1]) - for l in 1:(length(cycle_basis_current[2])-1) - for m in (l+1):length(cycle_basis_current[2]) - for p in 1:(length(cycle_basis_current[3])-1) - for q in (p+1):length(cycle_basis_current[3]) - if (cycle_basis_current[1][j], cycle_basis_current[1][k]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) && - (cycle_basis_current[1][j], cycle_basis_current[1][k]) !== (cycle_basis_current[3][p], cycle_basis_current[3][q]) && - (cycle_basis_current[3][p], cycle_basis_current[3][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) - if adjacency_matrix(G)[cycle_basis_current[1][j], cycle_basis_current[1][k]] == 1 && adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && adjacency_matrix(G)[cycle_basis_current[3][p], cycle_basis_current[3][q]] == 1 - G = rem_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[3][p], cycle_basis_current[3][q])]) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) - vertices_tracker = [(cycle_basis_current[1][j], cycle_basis_current[1][k]), (cycle_basis_current[2][l], cycle_basis_current[2][m]), (cycle_basis_current[3][p], cycle_basis_current[3][q])] - end - G = add_multiple_edges!(G, [(cycle_basis_current[1][j], cycle_basis_current[1][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[3][p], cycle_basis_current[3][q])]) - end - end - end - end - end + for j in 1:(length(cycle_basis(G)[3])-1) + for k in (j+1):length(cycle_basis(G)[3]) + if adjacency_matrix(G)[cycle_basis(G)[3][j], cycle_basis(G)[3][k]] == 1 + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, j, k, algebraic_connectivity_tracker, vertices_tracker) end end end @@ -340,6 +303,46 @@ function refinement_span_tree( return updated_adjacency_graph_ac_tuple end +function vertices_tracker_span_update_two_edges!( + G, + adjacency_augment_graph, + j, # Index for third edge src if kopt_parameter is 3 + k, # Index for third edge dst if kopt_parameter is 3 + algebraic_connectivity_tracker, + vertices_tracker, + ) + cycle_basis_current = cycle_basis(G) + for l in 1:(length(cycle_basis_current[2])-1) + for m in (l+1):length(cycle_basis_current[2]) + for p in 1:(length(cycle_basis_current[1])-1) + for q in (p+1):length(cycle_basis_current[1]) + cycle_basis_to_check = [] + if j == 0 && k == 0 + cycle_basis_to_check = [(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[1][p], cycle_basis_current[1][q])] + cycle_basis_condition = (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) + else + cycle_basis_to_check = [(cycle_basis_current[3][j], cycle_basis_current[3][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[1][p], cycle_basis_current[1][q])] + cycle_basis_condition = (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) && + (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== (cycle_basis_current[1][p], cycle_basis_current[1][q]) && + (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) + end + if cycle_basis_condition + if adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && adjacency_matrix(G)[cycle_basis_current[1][p], cycle_basis_current[1][q]] == 1 + G = rem_multiple_edges!(G, cycle_basis_to_check) + if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + vertices_tracker = cycle_basis_to_check + end + G = add_multiple_edges!(G, cycle_basis_to_check) + end + end + end + end + end + end + return algebraic_connectivity_tracker, vertices_tracker +end + function refinement_tree( G, adjacency_base_graph, @@ -382,13 +385,13 @@ function refinement_tree( G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) algebraic_connectivity_tracker = 0.0 vertices_tracker = [(undef, undef),(undef, undef)] - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_two_edges!(G, 0, algebraic_connectivity_tracker,vertices_tracker) + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, algebraic_connectivity_tracker,vertices_tracker) G = rem_multiple_edges!(G, vertices_tracker) end end elseif kopt_parameter == 3 - combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) if length(combinations) <= num_kopt_swaps_upperbound num_swaps = length(combinations) else @@ -403,7 +406,7 @@ function refinement_tree( vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] for j in eachindex(length(collect(edges(G))) - 2) if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_two_edges!(G, j, algebraic_connectivity_tracker,vertices_tracker) + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, algebraic_connectivity_tracker,vertices_tracker) end end G = rem_multiple_edges!(G, vertices_tracker) @@ -414,8 +417,10 @@ function refinement_tree( return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) end -function vertices_tracker_two_edges!( +function vertices_tracker_update_two_edges!( G, + adjacency_base_graph, + adjacency_augment_graph, j, # Index for third edge if kopt_parameter is 3 algebraic_connectivity_tracker, vertices_tracker diff --git a/src/utility.jl b/src/utility.jl index 964287e..78bb3cb 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -162,7 +162,7 @@ violated eigen value w.r.t positive semi-definiteness of the input matrix. """ function _violated_eigen_vector(W::Array{<:Number}; tol = 1E-6) W_eigvals = LA.eigvals(W) - @show W_eigvals + if typeof(W_eigvals) != Vector{Float64} if !(isapprox(imag(W_eigvals), zeros(size(W_eigvals)[1]), atol = 1E-6)) Memento.error(_LOGGER, "PSD matrix (W) cannot have complex eigenvalues") diff --git a/test/heuristics_tests.jl b/test/heuristics_tests.jl index 1df5be1..24b3161 100644 --- a/test/heuristics_tests.jl +++ b/test/heuristics_tests.jl @@ -1,5 +1,5 @@ @testset "Heuristic test: 1-opt_spanning_tree" begin - num_nodes = 5 + num_nodes = 8 instance = 1 data_dict, augment_budget = data_spanning_tree(num_nodes, instance) @@ -14,6 +14,75 @@ ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + @test result["solution_type"] == "heuristic" + @test isapprox(result_lo["objective"], 22.8042, atol = 1E-4) + @test isapprox(result_lo["solution"]["z_var"][1, 7], 1.0) + @test isapprox(result_lo["solution"]["z_var"][7, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 7], 1.0) + @test isapprox(result_lo["solution"]["z_var"][7, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 7], 1.0) + @test isapprox(result_lo["solution"]["z_var"][7, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 6], 1.0) + @test isapprox(result_lo["solution"]["z_var"][6, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 7], 1.0) + @test isapprox(result_lo["solution"]["z_var"][7, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 7], 1.0) + @test isapprox(result_lo["solution"]["z_var"][7, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][7, 8], 1.0) + @test isapprox(result_lo["solution"]["z_var"][8, 7], 1.0) +end + +@testset "Heuristic test: 2-opt_spanning_tree" begin + num_nodes = 8 + instance = 5 + data_dict, augment_budget = data_spanning_tree(num_nodes, instance) + + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) + + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 2, + :num_central_nodes_verifier => 5, + :time_limit => test_time_limit(), + ) + result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + + @test result["solution_type"] == "heuristic" + @test isapprox(result_lo["objective"], 22.5051, atol = 1E-4) + @test isapprox(result_lo["solution"]["z_var"][1, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 6], 1.0) + @test isapprox(result_lo["solution"]["z_var"][6, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 7], 1.0) + @test isapprox(result_lo["solution"]["z_var"][7, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][7, 8], 1.0) + @test isapprox(result_lo["solution"]["z_var"][8, 7], 1.0) + +end + +@testset "Heuristic test: 3-opt_spanning_tree" begin + num_nodes = 5 + instance = 1 + data_dict, augment_budget = data_spanning_tree(num_nodes, instance) + + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) + + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 3, + :num_central_nodes_verifier => 5, + :time_limit => test_time_limit(), + ) + result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + @test result["solution_type"] == "heuristic" @test isapprox(result_lo["objective"], 11.1592, atol = 1E-4) @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) @@ -26,9 +95,45 @@ @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) end -@testset "Heuristic test: 2-opt_spanning_tree" begin +@testset "Heuristic test: 1-opt_tree" begin num_nodes = 5 - instance = 1 + instance = 11 + data_dict, augment_budget = data_spanning_tree(num_nodes, instance) + + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) + + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 1, + :num_central_nodes_verifier => 3, + :time_limit => test_time_limit(), + ) + result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + + @test result["solution_type"] == "heuristic" + @test isapprox(result_lo["objective"], 36.0425, atol = 1E-4) + @test isapprox(result_lo["solution"]["z_var"][1, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][1, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 4], 1.0) +end + +@testset "Heuristic test: 2-opt_tree" begin + num_nodes = 5 + instance = 11 data_dict, augment_budget = data_spanning_tree(num_nodes, instance) params = @@ -37,26 +142,34 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 2, - :num_central_nodes_verifier => 5, + :num_central_nodes_verifier => 3, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" - @test isapprox(result_lo["objective"], 11.1592, atol = 1E-4) + @test isapprox(result_lo["objective"], 36.0425, atol = 1E-4) + @test isapprox(result_lo["solution"]["z_var"][1, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][1, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 1], 1.0) @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 2], 1.0) @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 4], 1.0) end -@testset "Heuristic test: 3-opt_spanning_tree" begin +@testset "Heuristic test: 3-opt_tree" begin num_nodes = 5 - instance = 1 + instance = 11 data_dict, augment_budget = data_spanning_tree(num_nodes, instance) params = @@ -65,19 +178,27 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 3, - :num_central_nodes_verifier => 5, + :num_central_nodes_verifier => 3, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" - @test isapprox(result_lo["objective"], 11.1592, atol = 1E-4) + @test isapprox(result_lo["objective"], 36.0425, atol = 1E-4) + @test isapprox(result_lo["solution"]["z_var"][1, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 1], 1.0) + @test isapprox(result_lo["solution"]["z_var"][1, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][3, 1], 1.0) @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) + @test isapprox(result_lo["solution"]["z_var"][2, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 2], 1.0) @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) + @test isapprox(result_lo["solution"]["z_var"][4, 5], 1.0) + @test isapprox(result_lo["solution"]["z_var"][5, 4], 1.0) end From c5cd33ff009eb81d606e9635e896bcbc61741a61 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Fri, 11 Aug 2023 13:09:22 -0600 Subject: [PATCH 12/35] corrected the dict file of instance --- examples/instances/5_nodes/5_11.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/instances/5_nodes/5_11.json b/examples/instances/5_nodes/5_11.json index 7af50f7..b1ce165 100644 --- a/examples/instances/5_nodes/5_11.json +++ b/examples/instances/5_nodes/5_11.json @@ -1,4 +1,4 @@ {"edges_to_augment":[[[1,5],5.1749106],[[2,3],6.1501476],[[2,4],13.66149798]], "num_nodes":5, -"edges_existing":[[[1,2],34.99664955],[[1,3],3.86014785],[[1,4],11.2685551],[[2,5],21.90200648],[[3,4],29.11616571],[[3,5],27.74393888],[[4,5],2.83029978]], -"augment_budget":1} \ No newline at end of file +"edges_existing":[[[1,2],34.99664955],[[1,3],3.86014785],[[1,4],11.2685551],[[2,5],21.90200648],[[3,4],29.11616571],[[3,5],27.74393888],[[4,5],2.83029978]] +} \ No newline at end of file From d46501a0b6a67c9425dd5a04c684bf7b9569a9f8 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Sat, 12 Aug 2023 19:09:22 -0600 Subject: [PATCH 13/35] clean ups --- examples/run_examples.jl | 6 +- src/heuristics.jl | 298 +++++++++++++++++++-------------------- src/lopt_model.jl | 4 +- src/solution.jl | 17 +++ src/types.jl | 12 +- src/utility.jl | 22 +-- test/heuristics_tests.jl | 12 +- 7 files changed, 194 insertions(+), 177 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 066eb61..85ad6c0 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -24,7 +24,7 @@ function data_I(num_nodes::Int, instance::Int) file_path = joinpath(@__DIR__, "instances/$(num_nodes)_nodes/$(num_nodes)_$(instance).json") data_dict = LOpt.parse_file(file_path) - augment_budget = 1 #(num_nodes - 1) # spanning tree constraint + augment_budget = (num_nodes - 1) # spanning tree constraint return data_dict, augment_budget end @@ -45,7 +45,7 @@ end # User-defined params # #-------------------------------# num_nodes = 5 -instance = 11 +instance = 1 data_dict, augment_budget = data_I(num_nodes, instance) params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -61,7 +61,7 @@ model_options = Dict{Symbol,Any}( :topology_flow_cuts => true, :solution_type => "heuristic", :kopt_parameter => 3, - :num_central_nodes_verifier => 3, + :num_central_nodes_kopt => 3, ) result = LOpt.run_LOpt( diff --git a/src/heuristics.jl b/src/heuristics.jl index 5e90ced..a7e55ee 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,100 +1,100 @@ # Heuristics to maximize algebraic connectivity of weighted graphs -using Graphs -using LinearAlgebra +function heuristic_kopt(lom::LaplacianOptModel) -function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central_nodes_verifier::Int, num_kopt_swaps_upperbound::Int) - num_nodes = data["num_nodes"] - adjacency_augment_graph = data["adjacency_augment_graph"] - adjacency_base_graph = data["adjacency_base_graph"] - num_edges_existing = data["num_edges_existing"] - augment_budget = data["augment_budget"] - is_base_graph_connected = data["is_base_graph_connected"] + kopt_parameter = lom.options.kopt_parameter + num_central_nodes_kopt = lom.options.num_central_nodes_kopt + num_swaps_bound_kopt = lom.options.num_swaps_bound_kopt + + num_nodes = lom.data["num_nodes"] + adjacency_augment_graph = lom.data["adjacency_augment_graph"] + adjacency_base_graph = lom.data["adjacency_base_graph"] + is_base_graph_connected = lom.data["is_base_graph_connected"] # Initializing the heuristic solutions adjacency_graph_star = Matrix{Float64}(zeros(num_nodes, num_nodes)) algebraic_connectivity_star = 0 # Making list of all possible edges to augment - edge_augment_list = collect(edges(SimpleGraph(adjacency_augment_graph))) + edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) # Making list of tuples of edges and edge weights edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) sorted_edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) - edge_edgeweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)]) + edge_edgeweight_list[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)]) end # Sorting the list of tuples in descending order of edge weights sorted_edge_edgeweight_list = sort(edge_edgeweight_list, by = x -> x[2], rev = true) - if num_edges_existing == 0 - if augment_budget == num_nodes - 1 #spanning tree + if lom.data["num_edges_existing"] == 0 + if lom.data["augment_budget"] == (num_nodes - 1) #spanning tree # Priority central nodes based on sum of edge weights - priority_central_nodes_list = priority_central_nodes(adjacency_augment_graph, num_nodes) + priority_central_nodes_list = LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity - adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_verifier) + adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_kopt) #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) - #Threads.@threads for index in 1:num_central_nodes_verifier #uncomment this line for multi threading - for index in 1:num_central_nodes_verifier #comment this line for multi threading + #Threads.@threads for index in 1:num_central_nodes_kopt #uncomment this line for multi threading + for index in 1:num_central_nodes_kopt #comment this line for multi threading # Builds a spanning tree - adjacency_graph_list[index] = build_span_tree( + adjacency_graph_list[index] = LOpt.build_span_tree( num_nodes, adjacency_augment_graph, sorted_edge_edgeweight_list, priority_central_nodes_list[index], ) - adjacency_graph_list[index] = refinement_span_tree( + adjacency_graph_list[index] = LOpt.refinement_span_tree( adjacency_augment_graph, sorted_edge_edgeweight_list, adjacency_graph_list[index], kopt_parameter, - num_kopt_swaps_upperbound, + num_swaps_bound_kopt, ) end adjacency_graph_star, algebraic_connectivity_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] - elseif augment_budget >= num_nodes + elseif lom.data["augment_budget"] >= num_nodes print("Under construction") end else if is_base_graph_connected # Fiedler vector of base graph - fiedler_vector_base_graph = fiedler_vector(adjacency_base_graph) + fiedler_vector_base_graph = LOpt.fiedler_vector(adjacency_base_graph) # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) sorted_edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) - edge_fiedlerweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)] * (fiedler_vector_base_graph[src(edge)] - fiedler_vector_base_graph[dst(edge)])^2) + edge_fiedlerweight_list[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)] * (fiedler_vector_base_graph[Graphs.src(edge)] - fiedler_vector_base_graph[Graphs.dst(edge)])^2) end # Sorting the list of tuples in descending order of fiedler weights sorted_edge_fiedlerweight_list = sort(edge_fiedlerweight_list, by = x -> x[2], rev = true) # Base graph - G = SimpleGraph(adjacency_base_graph) + G = Graphs.SimpleGraph(adjacency_base_graph) # Adding edges based on fiedler weights to construct initial graph with required edges - for i = 1:augment_budget - add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) + for i = 1:lom.data["augment_budget"] + Graphs.add_edge!(G, Graphs.src(sorted_edge_fiedlerweight_list[i][1]), Graphs.dst(sorted_edge_fiedlerweight_list[i][1])) end - adjacency_graph_star, algebraic_connectivity_star = refinement_tree( + adjacency_graph_star, algebraic_connectivity_star = LOpt.refinement_tree( G, adjacency_base_graph, adjacency_augment_graph, sorted_edge_fiedlerweight_list, kopt_parameter, - num_kopt_swaps_upperbound, + num_swaps_bound_kopt, ) elseif !(is_base_graph_connected) @@ -113,62 +113,62 @@ function build_span_tree( central_node, ) - G = SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph + G = Graphs.SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph uncon_set = Int64[] # Set contains all unconnected nodes uncon_set = collect(1:num_nodes) dir_con_set = Int64[] # Set contains nodes which are directly connected to central node sec_con_set = Int64[] # Set contains nodes which are connected to nodes in dir_con_set for j in eachindex(sorted_edge_edgeweight_list) - if (src(sorted_edge_edgeweight_list[j][1]) == central_node || dst(sorted_edge_edgeweight_list[j][1]) == central_node) + if (Graphs.src(sorted_edge_edgeweight_list[j][1]) == central_node || Graphs.dst(sorted_edge_edgeweight_list[j][1]) == central_node) # First edge of the graph - add_edge!(G, src(sorted_edge_edgeweight_list[j][1]), dst(sorted_edge_edgeweight_list[j][1])) + Graphs.add_edge!(G, Graphs.src(sorted_edge_edgeweight_list[j][1]), Graphs.dst(sorted_edge_edgeweight_list[j][1])) # Adding to directly connected set - src(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, dst(sorted_edge_edgeweight_list[j][1])) - dst(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, src(sorted_edge_edgeweight_list[j][1])) + Graphs.src(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, Graphs.dst(sorted_edge_edgeweight_list[j][1])) + Graphs.dst(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, Graphs.src(sorted_edge_edgeweight_list[j][1])) # Removing from unconnected set - deleteat!(uncon_set, uncon_set .== src(sorted_edge_edgeweight_list[j][1])) - deleteat!(uncon_set, uncon_set .== dst(sorted_edge_edgeweight_list[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.src(sorted_edge_edgeweight_list[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.dst(sorted_edge_edgeweight_list[j][1])) break end end - while !is_connected(G) # Connecting nodes until a spanning tree is formed - algebraic_connectivity_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,1) + while !Graphs.is_connected(G) # Connecting nodes until a spanning tree is formed + ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,1) for j in 1:(size(dir_con_set)[1]+1) for k in eachindex(sorted_edge_edgeweight_list) if j == 1 - if (src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || - (dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_set) + if (Graphs.src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([Graphs.dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || + (Graphs.dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([Graphs.src(sorted_edge_edgeweight_list[k][1])], uncon_set) # Adding edge to central node - add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + Graphs.add_edge!(G, Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])) # Storing current algebraic connectivity - algebraic_connectivity_tracker = ((src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])),algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + ac_tracker = ((Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) # Removing the added edge - rem_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + Graphs.rem_edge!(G, Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])) break end else - if (src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || - (dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_set) + if (Graphs.src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([Graphs.dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || + (Graphs.dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([Graphs.src(sorted_edge_edgeweight_list[k][1])], uncon_set) # Adding edge to directly connected node - add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + Graphs.add_edge!(G, Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])) # Updating current algebraic connectivity - if algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1))) > algebraic_connectivity_tracker[2] - algebraic_connectivity_tracker = ((src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])),algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + if LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1))) > ac_tracker[2] + ac_tracker = ((Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) end # Removing the added edge - rem_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + Graphs.rem_edge!(G, Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])) break end end @@ -176,41 +176,41 @@ function build_span_tree( end # Adding edge - add_edge!(G, algebraic_connectivity_tracker[1][1], algebraic_connectivity_tracker[1][2]) + Graphs.add_edge!(G, ac_tracker[1][1], ac_tracker[1][2]) - if (algebraic_connectivity_tracker[1][1] == central_node) && issubset(algebraic_connectivity_tracker[1][2], uncon_set) + if (ac_tracker[1][1] == central_node) && issubset(ac_tracker[1][2], uncon_set) # Adding node to directly connected set - push!(dir_con_set, algebraic_connectivity_tracker[1][2]) + push!(dir_con_set, ac_tracker[1][2]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][2]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][2]) - elseif (algebraic_connectivity_tracker[1][2] == central_node) && issubset(algebraic_connectivity_tracker[1][1], uncon_set) + elseif (ac_tracker[1][2] == central_node) && issubset(ac_tracker[1][1], uncon_set) # Adding node to directly connected set - push!(dir_con_set, algebraic_connectivity_tracker[1][1]) + push!(dir_con_set, ac_tracker[1][1]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][1]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][1]) - elseif issubset(algebraic_connectivity_tracker[1][1], dir_con_set) && issubset(algebraic_connectivity_tracker[1][2], uncon_set) + elseif issubset(ac_tracker[1][1], dir_con_set) && issubset(ac_tracker[1][2], uncon_set) # Adding node to secondary connected set - push!(sec_con_set, algebraic_connectivity_tracker[1][2]) + push!(sec_con_set, ac_tracker[1][2]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][2]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][2]) - elseif issubset(algebraic_connectivity_tracker[1][2], dir_con_set) && issubset(algebraic_connectivity_tracker[1][1], uncon_set) + elseif issubset(ac_tracker[1][2], dir_con_set) && issubset(ac_tracker[1][1], uncon_set) # Adding node to secondary connected set - push!(sec_con_set, algebraic_connectivity_tracker[1][1]) + push!(sec_con_set, ac_tracker[1][1]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][1]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][1]) end end - return (Matrix(adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) + return (Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G)))) end function refinement_span_tree( @@ -218,83 +218,83 @@ function refinement_span_tree( sorted_edge_edgeweight_list, adjacency_graph_ac_tuple, kopt_parameter, - num_kopt_swaps_upperbound, + num_swaps_bound_kopt, ) - G = SimpleGraph(adjacency_graph_ac_tuple[1]) + G = Graphs.SimpleGraph(adjacency_graph_ac_tuple[1]) # Refinement of Algebraic connectivity if kopt_parameter == 1 cycle_basis_current = Vector{Int64} for i in eachindex(sorted_edge_edgeweight_list) - if add_edge!(G, src(sorted_edge_edgeweight_list[i][1]), dst(sorted_edge_edgeweight_list[i][1])) - if is_cyclic(G) - cycle_basis_current = cycle_basis(G)[1] - algebraic_connectivity_tracker = 0.0 + if Graphs.add_edge!(G, Graphs.src(sorted_edge_edgeweight_list[i][1]), Graphs.dst(sorted_edge_edgeweight_list[i][1])) + if Graphs.is_cyclic(G) + cycle_basis_current = Graphs.cycle_basis(G)[1] + ac_tracker = 0.0 vertices_tracker = [(undef, undef)] for j = 1:(length(cycle_basis_current)-1) for k = (j+1):length(cycle_basis_current) - if rem_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + if Graphs.rem_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) + if LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) vertices_tracker = [(cycle_basis_current[j], cycle_basis_current[k])] end - add_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) + Graphs.add_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) end end end - rem_edge!(G, vertices_tracker[1][1], vertices_tracker[1][2]) + Graphs.rem_edge!(G, vertices_tracker[1][1], vertices_tracker[1][2]) end end end updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 2 - combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + combinations = LOpt.edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) cycle_basis_current = Vector{Int64} - if length(combinations) <= num_kopt_swaps_upperbound + if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else - num_swaps = num_kopt_swaps_upperbound + num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && - adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 - G = add_multiple_edges!(G, [(src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1]))]) - if is_cyclic(G) - cycle_basis_current = cycle_basis(G) - algebraic_connectivity_tracker = 0.0 + if Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edge_edgeweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(Graphs.src(sorted_edge_edgeweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][2]][1]))]) + if Graphs.is_cyclic(G) + cycle_basis_current = Graphs.cycle_basis(G) + ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef)] - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, 0, 0, algebraic_connectivity_tracker, vertices_tracker) - G = rem_multiple_edges!(G, vertices_tracker) + ac_tracker, vertices_tracker = LOpt.vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, 0, 0, ac_tracker, vertices_tracker) + G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end end updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 3 - combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + combinations = LOpt.edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) cycle_basis_current = Vector{Int64} - if length(combinations) <= num_kopt_swaps_upperbound + if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else - num_swaps = num_kopt_swaps_upperbound + num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && - adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 && - adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1])] == 0 - G = add_multiple_edges!(G, [(src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])),(src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1]))]) - if is_cyclic(G) - algebraic_connectivity_tracker = 0.0 + if Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][3]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][3]][1])] == 0 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edge_edgeweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(Graphs.src(sorted_edge_edgeweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][2]][1])),(Graphs.src(sorted_edge_edgeweight_list[combinations[i][3]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][3]][1]))]) + if Graphs.is_cyclic(G) + ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in 1:(length(cycle_basis(G)[3])-1) - for k in (j+1):length(cycle_basis(G)[3]) - if adjacency_matrix(G)[cycle_basis(G)[3][j], cycle_basis(G)[3][k]] == 1 - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, j, k, algebraic_connectivity_tracker, vertices_tracker) + for j in 1:(length(Graphs.cycle_basis(G)[3])-1) + for k in (j+1):length(Graphs.cycle_basis(G)[3]) + if Graphs.adjacency_matrix(G)[Graphs.cycle_basis(G)[3][j], Graphs.cycle_basis(G)[3][k]] == 1 + ac_tracker, vertices_tracker = LOpt.vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, j, k, ac_tracker, vertices_tracker) end end end - G = rem_multiple_edges!(G, vertices_tracker) + G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end end @@ -308,10 +308,10 @@ function vertices_tracker_span_update_two_edges!( adjacency_augment_graph, j, # Index for third edge src if kopt_parameter is 3 k, # Index for third edge dst if kopt_parameter is 3 - algebraic_connectivity_tracker, + ac_tracker, vertices_tracker, ) - cycle_basis_current = cycle_basis(G) + cycle_basis_current = Graphs.cycle_basis(G) for l in 1:(length(cycle_basis_current[2])-1) for m in (l+1):length(cycle_basis_current[2]) for p in 1:(length(cycle_basis_current[1])-1) @@ -327,20 +327,20 @@ function vertices_tracker_span_update_two_edges!( (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) end if cycle_basis_condition - if adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && adjacency_matrix(G)[cycle_basis_current[1][p], cycle_basis_current[1][q]] == 1 - G = rem_multiple_edges!(G, cycle_basis_to_check) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + if Graphs.adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && Graphs.adjacency_matrix(G)[cycle_basis_current[1][p], cycle_basis_current[1][q]] == 1 + G = LOpt.remove_multiple_edges!(G, cycle_basis_to_check) + if LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) vertices_tracker = cycle_basis_to_check end - G = add_multiple_edges!(G, cycle_basis_to_check) + G = LOpt.add_multiple_edges!(G, cycle_basis_to_check) end end end end end end - return algebraic_connectivity_tracker, vertices_tracker + return ac_tracker, vertices_tracker end function refinement_tree( @@ -349,72 +349,72 @@ function refinement_tree( adjacency_augment_graph, sorted_edge_fiedlerweight_list, kopt_parameter, - num_kopt_swaps_upperbound, + num_swaps_bound_kopt, ) if kopt_parameter == 1 for i in eachindex(sorted_edge_fiedlerweight_list) - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1]))) - add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) - algebraic_connectivity_tracker = 0.0 + if !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[i][1]), Graphs.dst(sorted_edge_fiedlerweight_list[i][1]))) + Graphs.add_edge!(G, Graphs.src(sorted_edge_fiedlerweight_list[i][1]), Graphs.dst(sorted_edge_fiedlerweight_list[i][1])) + ac_tracker = 0.0 vertices_tracker = (undef, undef) - for edge in edges(G) - if adjacency_base_graph[src(edge), dst(edge)] == 0 - rem_edge!(G, src(edge), dst(edge)) - if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + for edge in Graphs.edges(G) + if adjacency_base_graph[Graphs.src(edge), Graphs.dst(edge)] == 0 + Graphs.rem_edge!(G, Graphs.src(edge), Graphs.dst(edge)) + if LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) vertices_tracker = edge end - add_edge!(G, src(edge), dst(edge)) + Graphs.add_edge!(G, Graphs.src(edge), Graphs.dst(edge)) end end - rem_edge!(G, vertices_tracker) + Graphs.rem_edge!(G, vertices_tracker) end end elseif kopt_parameter == 2 - combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) - if length(combinations) <= num_kopt_swaps_upperbound + combinations = LOpt.edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) + if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else - num_swaps = num_kopt_swaps_upperbound + num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) - G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) - algebraic_connectivity_tracker = 0.0 + if !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) + ac_tracker = 0.0 vertices_tracker = [(undef, undef),(undef, undef)] - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, algebraic_connectivity_tracker,vertices_tracker) - G = rem_multiple_edges!(G, vertices_tracker) + ac_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, ac_tracker,vertices_tracker) + G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end elseif kopt_parameter == 3 - combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) - if length(combinations) <= num_kopt_swaps_upperbound + combinations = LOpt.edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) + if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else - num_swaps = num_kopt_swaps_upperbound + num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))) - G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])), (src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))]) - algebraic_connectivity_tracker = 0.0 + if !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))) + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])), (Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))]) + ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in eachindex(length(collect(edges(G))) - 2) - if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, algebraic_connectivity_tracker,vertices_tracker) + for j in eachindex(length(collect(Graphs.edges(G))) - 2) + if (adjacency_base_graph[Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])] == 0) + ac_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, ac_tracker,vertices_tracker) end end - G = rem_multiple_edges!(G, vertices_tracker) + G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end end - return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + return Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) end function vertices_tracker_update_two_edges!( @@ -422,29 +422,29 @@ function vertices_tracker_update_two_edges!( adjacency_base_graph, adjacency_augment_graph, j, # Index for third edge if kopt_parameter is 3 - algebraic_connectivity_tracker, + ac_tracker, vertices_tracker ) - edge_list = collect(edges(G)) + edge_list = collect(Graphs.edges(G)) for k in j+1:(length(edge_list)-1) for l in k+1:length(edge_list) edges_to_check = [] if j == 0 - edges_to_check = [(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] + edges_to_check = [(Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l]))] else - edges_to_check = [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] + edges_to_check = [(Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])),(Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l]))] end - if (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) - G = rem_multiple_edges!(G, edges_to_check) - if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + if (adjacency_base_graph[Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])] == 0) && (adjacency_base_graph[Graphs.src(edge_list[l]), Graphs.dst(edge_list[l])] == 0) + G = LOpt.remove_multiple_edges!(G, edges_to_check) + if LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) vertices_tracker = edges_to_check end - G = add_multiple_edges!(G, edges_to_check) + G = LOpt.add_multiple_edges!(G, edges_to_check) end end end - return algebraic_connectivity_tracker, vertices_tracker + return ac_tracker, vertices_tracker end diff --git a/src/lopt_model.jl b/src/lopt_model.jl index dd53985..cf8fc81 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -24,10 +24,10 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.objective_LOModel(lom) elseif lom.options.solution_type == "heuristic" - LOpt.heuristic_kopt(lom.data, lom.options.kopt_parameter, lom.options.num_central_nodes_verifier, lom.options.num_kopt_swaps_upperbound) + LOpt.heuristic_kopt(lom) end elseif lom.options.formulation_type == "max_span_tree" - if lom.options.solution_type in ["optimal", "heuristic"] + if lom.options.solution_type in ["optimal"] LOpt.variable_MaxSpanTree_model(lom) LOpt.constraint_MaxSpanTree_model(lom) LOpt.objective_MaxSpanTree_model(lom) diff --git a/src/solution.jl b/src/solution.jl index 54ebdd2..1bdc20d 100644 --- a/src/solution.jl +++ b/src/solution.jl @@ -102,3 +102,20 @@ function optimality_certificate_MISDP(lom::LaplacianOptModel, result::Dict{Strin end end end + +function build_LOModel_heuristic_result(lom::LaplacianOptModel, solve_time::Number) + + solution = Dict{String,Any}() + + result = Dict{String,Any}( + "optimizer" => JuMP.solver_name(lom.model), + # "objective" => LOpt.get_objective_value(lom.model), + "solve_time" => solve_time, + "solution" => solution, + "solution_type" => lom.options.solution_type, + "adjacency_base_graph" => lom.data["adjacency_base_graph"], + "adjacency_augment_graph" => lom.data["adjacency_augment_graph"], + ) + + return result +end \ No newline at end of file diff --git a/src/types.jl b/src/types.jl index ab554d3..371e2aa 100644 --- a/src/types.jl +++ b/src/types.jl @@ -22,8 +22,8 @@ mutable struct LaplacianOptModelOptions sdp_relaxation::Bool kopt_parameter::Int64 - num_central_nodes_verifier::Int64 - num_kopt_swaps_upperbound::Int64 + num_central_nodes_kopt::Int64 + num_swaps_bound_kopt::Int64 best_lower_bound::Float64 best_incumbent::Union{Vector{Tuple{Int64,Int64}},Nothing} @@ -54,8 +54,8 @@ function get_default_options() sdp_relaxation = false # true, false kopt_parameter = 2 # Integer value >= 1 - num_central_nodes_verifier = 5 # Integer value >= 1 - num_kopt_swaps_upperbound = 1E6 # Integer value + num_central_nodes_kopt = 5 # Integer value >= 1 + num_swaps_bound_kopt = 1E6 # Integer value best_lower_bound = 0 # Best known feasible solution's objective best_incumbent = nothing @@ -78,8 +78,8 @@ function get_default_options() cheeger_cuts_factor, sdp_relaxation, kopt_parameter, - num_central_nodes_verifier, - num_kopt_swaps_upperbound, + num_central_nodes_kopt, + num_swaps_bound_kopt, best_lower_bound, best_incumbent, tol_zero, diff --git a/src/utility.jl b/src/utility.jl index 78bb3cb..bb944c8 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -366,15 +366,15 @@ function weighted_adjacency_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, #collecting vertices connected in graph vertices_from_edges = Int[] - for edge in edges(G) - push!(vertices_from_edges, src(edge)) - push!(vertices_from_edges, dst(edge)) + for edge in Graphs.edges(G) + push!(vertices_from_edges, Graphs.src(edge)) + push!(vertices_from_edges, Graphs.dst(edge)) end vertices_from_edges = unique(vertices_from_edges) for i in 1:size for j in i:size - weighted_adj_matrix_size[i, j] = (adjacency_augment_graph.*adjacency_matrix(G))[vertices_from_edges[i],vertices_from_edges[j]] + weighted_adj_matrix_size[i, j] = (adjacency_augment_graph.*Graphs.adjacency_matrix(G))[vertices_from_edges[i],vertices_from_edges[j]] weighted_adj_matrix_size[j, i] = weighted_adj_matrix_size[i, j] end end @@ -394,8 +394,8 @@ function update_kopt_adjacency!( adjacency_graph_ac_tuple::Tuple{Array,Float64}; tol = 1E-6, ) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) - adjacency_graph_ac_tuple[2] >= tol - return Matrix(adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + if algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) - adjacency_graph_ac_tuple[2] >= tol + return Matrix(Graphs.adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) else return adjacency_graph_ac_tuple end @@ -404,7 +404,7 @@ end """ edge_combinations(n::Int64, k::Int64) -Returns all combinations possible for given n and k. +Returns all combinations possible for given `n` and `k`. """ function edge_combinations(num_edges::Int64, kopt_parameter::Int64) @@ -445,24 +445,24 @@ function add_multiple_edges!( edge_set::Vector{Tuple{Int64,Int64}}, ) for i in 1:length(edge_set) - add_edge!(G, edge_set[i][1], edge_set[i][2]) + Graphs.add_edge!(G, edge_set[i][1], edge_set[i][2]) end return G end """ - rem_multiple_edges!(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},edge_set::Vector{Tuple{Int64,Int64}},) + remove_multiple_edges!(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},edge_set::Vector{Tuple{Int64,Int64}},) Returns updated graph by removing edges. """ -function rem_multiple_edges!( +function remove_multiple_edges!( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, edge_set::Vector{Tuple{Int64,Int64}}, ) for i in 1:length(edge_set) - rem_edge!(G, edge_set[i][1], edge_set[i][2]) + Graphs.rem_edge!(G, edge_set[i][1], edge_set[i][2]) end return G end \ No newline at end of file diff --git a/test/heuristics_tests.jl b/test/heuristics_tests.jl index 24b3161..5bca799 100644 --- a/test/heuristics_tests.jl +++ b/test/heuristics_tests.jl @@ -9,7 +9,7 @@ model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 1, - :num_central_nodes_verifier => 5, + :num_central_nodes_kopt => 5, :time_limit => test_time_limit() ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -43,7 +43,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 2, - :num_central_nodes_verifier => 5, + :num_central_nodes_kopt => 5, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -78,7 +78,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 3, - :num_central_nodes_verifier => 5, + :num_central_nodes_kopt => 5, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -106,7 +106,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 1, - :num_central_nodes_verifier => 3, + :num_central_nodes_kopt => 3, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -142,7 +142,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 2, - :num_central_nodes_verifier => 3, + :num_central_nodes_kopt => 3, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -178,7 +178,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 3, - :num_central_nodes_verifier => 3, + :num_central_nodes_kopt => 3, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) From a63f300a640e1554414fd2681326ee334f93659e Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Sun, 13 Aug 2023 10:39:25 -0600 Subject: [PATCH 14/35] shortened param names --- src/heuristics.jl | 124 +++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index a7e55ee..8306ccb 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -12,22 +12,22 @@ function heuristic_kopt(lom::LaplacianOptModel) is_base_graph_connected = lom.data["is_base_graph_connected"] # Initializing the heuristic solutions - adjacency_graph_star = Matrix{Float64}(zeros(num_nodes, num_nodes)) - algebraic_connectivity_star = 0 + adjacency_star = Matrix{Float64}(zeros(num_nodes, num_nodes)) + ac_star = 0 # Making list of all possible edges to augment edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) # Making list of tuples of edges and edge weights edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - sorted_edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + sorted_edges_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) edge_edgeweight_list[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)]) end # Sorting the list of tuples in descending order of edge weights - sorted_edge_edgeweight_list = sort(edge_edgeweight_list, by = x -> x[2], rev = true) + sorted_edges_wt = sort(edge_edgeweight_list, by = x -> x[2], rev = true) if lom.data["num_edges_existing"] == 0 if lom.data["augment_budget"] == (num_nodes - 1) #spanning tree @@ -45,20 +45,20 @@ function heuristic_kopt(lom::LaplacianOptModel) adjacency_graph_list[index] = LOpt.build_span_tree( num_nodes, adjacency_augment_graph, - sorted_edge_edgeweight_list, + sorted_edges_wt, priority_central_nodes_list[index], ) adjacency_graph_list[index] = LOpt.refinement_span_tree( adjacency_augment_graph, - sorted_edge_edgeweight_list, + sorted_edges_wt, adjacency_graph_list[index], kopt_parameter, num_swaps_bound_kopt, ) end - adjacency_graph_star, algebraic_connectivity_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] + adjacency_star, ac_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] elseif lom.data["augment_budget"] >= num_nodes print("Under construction") @@ -70,29 +70,29 @@ function heuristic_kopt(lom::LaplacianOptModel) fiedler_vector_base_graph = LOpt.fiedler_vector(adjacency_base_graph) # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) - edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - sorted_edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) - edge_fiedlerweight_list[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)] * (fiedler_vector_base_graph[Graphs.src(edge)] - fiedler_vector_base_graph[Graphs.dst(edge)])^2) + edges_fiedler_wt[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)] * (fiedler_vector_base_graph[Graphs.src(edge)] - fiedler_vector_base_graph[Graphs.dst(edge)])^2) end # Sorting the list of tuples in descending order of fiedler weights - sorted_edge_fiedlerweight_list = sort(edge_fiedlerweight_list, by = x -> x[2], rev = true) + sorted_edges_fiedler_wt = sort(edges_fiedler_wt, by = x -> x[2], rev = true) # Base graph G = Graphs.SimpleGraph(adjacency_base_graph) # Adding edges based on fiedler weights to construct initial graph with required edges for i = 1:lom.data["augment_budget"] - Graphs.add_edge!(G, Graphs.src(sorted_edge_fiedlerweight_list[i][1]), Graphs.dst(sorted_edge_fiedlerweight_list[i][1])) + Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) end - adjacency_graph_star, algebraic_connectivity_star = LOpt.refinement_tree( + adjacency_star, ac_star = LOpt.refinement_tree( G, adjacency_base_graph, adjacency_augment_graph, - sorted_edge_fiedlerweight_list, + sorted_edges_fiedler_wt, kopt_parameter, num_swaps_bound_kopt, ) @@ -102,14 +102,14 @@ function heuristic_kopt(lom::LaplacianOptModel) end end # Implement the bridge to results here - @show adjacency_graph_star, algebraic_connectivity_star + @show adjacency_star, ac_star return end function build_span_tree( num_nodes, adjacency_augment_graph, - sorted_edge_edgeweight_list, + sorted_edges_wt, central_node, ) @@ -119,19 +119,19 @@ function build_span_tree( dir_con_set = Int64[] # Set contains nodes which are directly connected to central node sec_con_set = Int64[] # Set contains nodes which are connected to nodes in dir_con_set - for j in eachindex(sorted_edge_edgeweight_list) - if (Graphs.src(sorted_edge_edgeweight_list[j][1]) == central_node || Graphs.dst(sorted_edge_edgeweight_list[j][1]) == central_node) + for j in eachindex(sorted_edges_wt) + if (Graphs.src(sorted_edges_wt[j][1]) == central_node || Graphs.dst(sorted_edges_wt[j][1]) == central_node) # First edge of the graph - Graphs.add_edge!(G, Graphs.src(sorted_edge_edgeweight_list[j][1]), Graphs.dst(sorted_edge_edgeweight_list[j][1])) + Graphs.add_edge!(G, Graphs.src(sorted_edges_wt[j][1]), Graphs.dst(sorted_edges_wt[j][1])) # Adding to directly connected set - Graphs.src(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, Graphs.dst(sorted_edge_edgeweight_list[j][1])) - Graphs.dst(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, Graphs.src(sorted_edge_edgeweight_list[j][1])) + Graphs.src(sorted_edges_wt[j][1]) == central_node && push!(dir_con_set, Graphs.dst(sorted_edges_wt[j][1])) + Graphs.dst(sorted_edges_wt[j][1]) == central_node && push!(dir_con_set, Graphs.src(sorted_edges_wt[j][1])) # Removing from unconnected set - deleteat!(uncon_set, uncon_set .== Graphs.src(sorted_edge_edgeweight_list[j][1])) - deleteat!(uncon_set, uncon_set .== Graphs.dst(sorted_edge_edgeweight_list[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.src(sorted_edges_wt[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.dst(sorted_edges_wt[j][1])) break end end @@ -140,35 +140,35 @@ function build_span_tree( ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,1) for j in 1:(size(dir_con_set)[1]+1) - for k in eachindex(sorted_edge_edgeweight_list) + for k in eachindex(sorted_edges_wt) if j == 1 - if (Graphs.src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([Graphs.dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || - (Graphs.dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([Graphs.src(sorted_edge_edgeweight_list[k][1])], uncon_set) + if (Graphs.src(sorted_edges_wt[k][1]) == central_node) && issubset([Graphs.dst(sorted_edges_wt[k][1])], uncon_set) || + (Graphs.dst(sorted_edges_wt[k][1]) == central_node) && issubset([Graphs.src(sorted_edges_wt[k][1])], uncon_set) # Adding edge to central node - Graphs.add_edge!(G, Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])) + Graphs.add_edge!(G, Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])) # Storing current algebraic connectivity - ac_tracker = ((Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + ac_tracker = ((Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) # Removing the added edge - Graphs.rem_edge!(G, Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])) + Graphs.rem_edge!(G, Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])) break end else - if (Graphs.src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([Graphs.dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || - (Graphs.dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([Graphs.src(sorted_edge_edgeweight_list[k][1])], uncon_set) + if (Graphs.src(sorted_edges_wt[k][1]) == dir_con_set[j-1]) && issubset([Graphs.dst(sorted_edges_wt[k][1])], uncon_set) || + (Graphs.dst(sorted_edges_wt[k][1]) == dir_con_set[j-1]) && issubset([Graphs.src(sorted_edges_wt[k][1])], uncon_set) # Adding edge to directly connected node - Graphs.add_edge!(G, Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])) + Graphs.add_edge!(G, Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])) # Updating current algebraic connectivity if LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1))) > ac_tracker[2] - ac_tracker = ((Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + ac_tracker = ((Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) end # Removing the added edge - Graphs.rem_edge!(G, Graphs.src(sorted_edge_edgeweight_list[k][1]), Graphs.dst(sorted_edge_edgeweight_list[k][1])) + Graphs.rem_edge!(G, Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])) break end end @@ -215,7 +215,7 @@ end function refinement_span_tree( adjacency_augment_graph, - sorted_edge_edgeweight_list, + sorted_edges_wt, adjacency_graph_ac_tuple, kopt_parameter, num_swaps_bound_kopt, @@ -225,8 +225,8 @@ function refinement_span_tree( # Refinement of Algebraic connectivity if kopt_parameter == 1 cycle_basis_current = Vector{Int64} - for i in eachindex(sorted_edge_edgeweight_list) - if Graphs.add_edge!(G, Graphs.src(sorted_edge_edgeweight_list[i][1]), Graphs.dst(sorted_edge_edgeweight_list[i][1])) + for i in eachindex(sorted_edges_wt) + if Graphs.add_edge!(G, Graphs.src(sorted_edges_wt[i][1]), Graphs.dst(sorted_edges_wt[i][1])) if Graphs.is_cyclic(G) cycle_basis_current = Graphs.cycle_basis(G)[1] ac_tracker = 0.0 @@ -246,10 +246,10 @@ function refinement_span_tree( end end end - updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 2 - combinations = LOpt.edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + combinations = LOpt.edge_combinations(length(sorted_edges_wt), kopt_parameter) cycle_basis_current = Vector{Int64} if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) @@ -257,9 +257,9 @@ function refinement_span_tree( num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 - G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edge_edgeweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(Graphs.src(sorted_edge_edgeweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][2]][1]))]) + if Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_wt[combinations[i][1]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_wt[combinations[i][2]][1])] == 0 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_wt[combinations[i][2]][1]))]) if Graphs.is_cyclic(G) cycle_basis_current = Graphs.cycle_basis(G) ac_tracker = 0.0 @@ -269,10 +269,10 @@ function refinement_span_tree( end end end - updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 3 - combinations = LOpt.edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + combinations = LOpt.edge_combinations(length(sorted_edges_wt), kopt_parameter) cycle_basis_current = Vector{Int64} if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) @@ -280,10 +280,10 @@ function refinement_span_tree( num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(sorted_edge_edgeweight_list[combinations[i][3]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][3]][1])] == 0 - G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edge_edgeweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(Graphs.src(sorted_edge_edgeweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][2]][1])),(Graphs.src(sorted_edge_edgeweight_list[combinations[i][3]][1]), Graphs.dst(sorted_edge_edgeweight_list[combinations[i][3]][1]))]) + if Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_wt[combinations[i][1]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_wt[combinations[i][2]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_wt[combinations[i][3]][1])] == 0 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_wt[combinations[i][2]][1])),(Graphs.src(sorted_edges_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_wt[combinations[i][3]][1]))]) if Graphs.is_cyclic(G) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] @@ -298,7 +298,7 @@ function refinement_span_tree( end end end - updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) end return updated_adjacency_graph_ac_tuple end @@ -347,15 +347,15 @@ function refinement_tree( G, adjacency_base_graph, adjacency_augment_graph, - sorted_edge_fiedlerweight_list, + sorted_edges_fiedler_wt, kopt_parameter, num_swaps_bound_kopt, ) if kopt_parameter == 1 - for i in eachindex(sorted_edge_fiedlerweight_list) - if !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[i][1]), Graphs.dst(sorted_edge_fiedlerweight_list[i][1]))) - Graphs.add_edge!(G, Graphs.src(sorted_edge_fiedlerweight_list[i][1]), Graphs.dst(sorted_edge_fiedlerweight_list[i][1])) + for i in eachindex(sorted_edges_fiedler_wt) + if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1]))) + Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) ac_tracker = 0.0 vertices_tracker = (undef, undef) for edge in Graphs.edges(G) @@ -373,16 +373,16 @@ function refinement_tree( end elseif kopt_parameter == 2 - combinations = LOpt.edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) + combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && - !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) - G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) + if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))) + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))]) ac_tracker = 0.0 vertices_tracker = [(undef, undef),(undef, undef)] ac_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, ac_tracker,vertices_tracker) @@ -391,17 +391,17 @@ function refinement_tree( end elseif kopt_parameter == 3 - combinations = LOpt.edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) + combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && - !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) && - !(Graphs.has_edge(G, Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))) - G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])), (Graphs.src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), Graphs.dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))]) + if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][3]][1]))) + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1])), (Graphs.src(sorted_edges_fiedler_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][3]][1]))]) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] for j in eachindex(length(collect(Graphs.edges(G))) - 2) From ca21716c3d919959dccb6aa6ca9a88b4b25a30bf Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Sun, 13 Aug 2023 18:26:46 -0600 Subject: [PATCH 15/35] minor mods --- src/heuristics.jl | 96 ++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index 8306ccb..2975bd6 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -178,14 +178,14 @@ function build_span_tree( # Adding edge Graphs.add_edge!(G, ac_tracker[1][1], ac_tracker[1][2]) - if (ac_tracker[1][1] == central_node) && issubset(ac_tracker[1][2], uncon_set) + if (ac_tracker[1][1] == central_node) && issubset(ac_tracker[1][2], uncon_set) # Adding node to directly connected set push!(dir_con_set, ac_tracker[1][2]) # Removing node from unconnected nodes set deleteat!(uncon_set, uncon_set .== ac_tracker[1][2]) - elseif (ac_tracker[1][2] == central_node) && issubset(ac_tracker[1][1], uncon_set) + elseif (ac_tracker[1][2] == central_node) && issubset(ac_tracker[1][1], uncon_set) # Adding node to directly connected set push!(dir_con_set, ac_tracker[1][1]) @@ -264,7 +264,7 @@ function refinement_span_tree( cycle_basis_current = Graphs.cycle_basis(G) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef)] - ac_tracker, vertices_tracker = LOpt.vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, 0, 0, ac_tracker, vertices_tracker) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_two_edges!(G, adjacency_augment_graph, 0, 0, ac_tracker, vertices_tracker) G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end @@ -290,7 +290,7 @@ function refinement_span_tree( for j in 1:(length(Graphs.cycle_basis(G)[3])-1) for k in (j+1):length(Graphs.cycle_basis(G)[3]) if Graphs.adjacency_matrix(G)[Graphs.cycle_basis(G)[3][j], Graphs.cycle_basis(G)[3][k]] == 1 - ac_tracker, vertices_tracker = LOpt.vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, j, k, ac_tracker, vertices_tracker) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_two_edges!(G, adjacency_augment_graph, j, k, ac_tracker, vertices_tracker) end end end @@ -303,46 +303,6 @@ function refinement_span_tree( return updated_adjacency_graph_ac_tuple end -function vertices_tracker_span_update_two_edges!( - G, - adjacency_augment_graph, - j, # Index for third edge src if kopt_parameter is 3 - k, # Index for third edge dst if kopt_parameter is 3 - ac_tracker, - vertices_tracker, - ) - cycle_basis_current = Graphs.cycle_basis(G) - for l in 1:(length(cycle_basis_current[2])-1) - for m in (l+1):length(cycle_basis_current[2]) - for p in 1:(length(cycle_basis_current[1])-1) - for q in (p+1):length(cycle_basis_current[1]) - cycle_basis_to_check = [] - if j == 0 && k == 0 - cycle_basis_to_check = [(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[1][p], cycle_basis_current[1][q])] - cycle_basis_condition = (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) - else - cycle_basis_to_check = [(cycle_basis_current[3][j], cycle_basis_current[3][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[1][p], cycle_basis_current[1][q])] - cycle_basis_condition = (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) && - (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== (cycle_basis_current[1][p], cycle_basis_current[1][q]) && - (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) - end - if cycle_basis_condition - if Graphs.adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && Graphs.adjacency_matrix(G)[cycle_basis_current[1][p], cycle_basis_current[1][q]] == 1 - G = LOpt.remove_multiple_edges!(G, cycle_basis_to_check) - if LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker - ac_tracker = LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) - vertices_tracker = cycle_basis_to_check - end - G = LOpt.add_multiple_edges!(G, cycle_basis_to_check) - end - end - end - end - end - end - return ac_tracker, vertices_tracker -end - function refinement_tree( G, adjacency_base_graph, @@ -385,7 +345,7 @@ function refinement_tree( G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))]) ac_tracker = 0.0 vertices_tracker = [(undef, undef),(undef, undef)] - ac_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, ac_tracker,vertices_tracker) + ac_tracker, vertices_tracker = _vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, ac_tracker,vertices_tracker) G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end @@ -406,18 +366,21 @@ function refinement_tree( vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] for j in eachindex(length(collect(Graphs.edges(G))) - 2) if (adjacency_base_graph[Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])] == 0) - ac_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, ac_tracker,vertices_tracker) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, ac_tracker,vertices_tracker) end end G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end + + else + Memento.eror(_LOGGER, "kopt_parameter > 3 is currently not supported.") end return Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) end -function vertices_tracker_update_two_edges!( +function _vertices_tracker_update_two_edges!( G, adjacency_base_graph, adjacency_augment_graph, @@ -448,3 +411,42 @@ function vertices_tracker_update_two_edges!( return ac_tracker, vertices_tracker end +function _vertices_tracker_two_edges!( + G, + adjacency_augment_graph, + j, # Index for third edge src if kopt_parameter is 3 + k, # Index for third edge dst if kopt_parameter is 3 + ac_tracker, + vertices_tracker, + ) + cycle_basis_current = Graphs.cycle_basis(G) + for l in 1:(length(cycle_basis_current[2])-1) + for m in (l+1):length(cycle_basis_current[2]) + for p in 1:(length(cycle_basis_current[1])-1) + for q in (p+1):length(cycle_basis_current[1]) + cycle_basis_to_check = [] + if j == 0 && k == 0 + cycle_basis_to_check = [(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[1][p], cycle_basis_current[1][q])] + cycle_basis_condition = (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) + else + cycle_basis_to_check = [(cycle_basis_current[3][j], cycle_basis_current[3][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[1][p], cycle_basis_current[1][q])] + cycle_basis_condition = (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) && + (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== (cycle_basis_current[1][p], cycle_basis_current[1][q]) && + (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) + end + if cycle_basis_condition + if Graphs.adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && Graphs.adjacency_matrix(G)[cycle_basis_current[1][p], cycle_basis_current[1][q]] == 1 + G = LOpt.remove_multiple_edges!(G, cycle_basis_to_check) + if LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) + vertices_tracker = cycle_basis_to_check + end + G = LOpt.add_multiple_edges!(G, cycle_basis_to_check) + end + end + end + end + end + end + return ac_tracker, vertices_tracker +end \ No newline at end of file From f2e212ef19fa487226a897d05e1812a1f9f4c144 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Mon, 14 Aug 2023 13:51:31 -0600 Subject: [PATCH 16/35] refactoring --- examples/run_examples.jl | 5 +- src/heuristics.jl | 228 ++++++++++++++++++++------------------- src/lopt_model.jl | 15 ++- src/solution.jl | 12 +-- 4 files changed, 133 insertions(+), 127 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 85ad6c0..ee2e8bd 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -46,7 +46,8 @@ end #-------------------------------# num_nodes = 5 instance = 1 -data_dict, augment_budget = data_I(num_nodes, instance) +# data_dict, augment_budget = data_I(num_nodes, instance) +data_dict, augment_budget = data_II() params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -61,7 +62,7 @@ model_options = Dict{Symbol,Any}( :topology_flow_cuts => true, :solution_type => "heuristic", :kopt_parameter => 3, - :num_central_nodes_kopt => 3, + :num_central_nodes_kopt => 5, ) result = LOpt.run_LOpt( diff --git a/src/heuristics.jl b/src/heuristics.jl index 2975bd6..80550ec 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,115 +1,119 @@ # Heuristics to maximize algebraic connectivity of weighted graphs -function heuristic_kopt(lom::LaplacianOptModel) +function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) + adjacency_star = [] + ac_star = [] + + if (lom.data["num_edges_existing"] == 0) && (lom.data["augment_budget"] == (lom.data["num_nodes"] - 1)) + _, heuristic_time, solve_bytes_alloc, sec_in_gc = @timed adjacency_star, ac_star = LOpt.heuristic_spanning_tree(lom) + elseif (lom.data["num_edges_existing"] > 0) && (lom.data["is_base_graph_connected"]) + _, heuristic_time, solve_bytes_alloc, sec_in_gc = @timed adjacency_star, ac_star = LOpt.heuristic_base_graph_connected(lom) + else + Memento.error(_LOGGER, "Heuristic for this option is not yet supported.") + end - kopt_parameter = lom.options.kopt_parameter - num_central_nodes_kopt = lom.options.num_central_nodes_kopt - num_swaps_bound_kopt = lom.options.num_swaps_bound_kopt + # Implement the bridge to results here + result_dict = LOpt.build_LOModel_heuristic_result(lom, heuristic_time, adjacency_star, ac_star) + + return merge(lom.result, result_dict) +end + +function heuristic_spanning_tree(lom::LaplacianOptModel) num_nodes = lom.data["num_nodes"] adjacency_augment_graph = lom.data["adjacency_augment_graph"] - adjacency_base_graph = lom.data["adjacency_base_graph"] - is_base_graph_connected = lom.data["is_base_graph_connected"] - - # Initializing the heuristic solutions - adjacency_star = Matrix{Float64}(zeros(num_nodes, num_nodes)) - ac_star = 0 + num_central_nodes_kopt = lom.options.num_central_nodes_kopt # Making list of all possible edges to augment edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) # Making list of tuples of edges and edge weights - edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - sorted_edges_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + edge_wt_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + edge_wt_sorted = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) - edge_edgeweight_list[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)]) + edge_wt_list[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)]) end # Sorting the list of tuples in descending order of edge weights - sorted_edges_wt = sort(edge_edgeweight_list, by = x -> x[2], rev = true) - - if lom.data["num_edges_existing"] == 0 - if lom.data["augment_budget"] == (num_nodes - 1) #spanning tree - - # Priority central nodes based on sum of edge weights - priority_central_nodes_list = LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) - - # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity - adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_kopt) - - #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) - #Threads.@threads for index in 1:num_central_nodes_kopt #uncomment this line for multi threading - for index in 1:num_central_nodes_kopt #comment this line for multi threading - # Builds a spanning tree - adjacency_graph_list[index] = LOpt.build_span_tree( - num_nodes, - adjacency_augment_graph, - sorted_edges_wt, - priority_central_nodes_list[index], - ) - - adjacency_graph_list[index] = LOpt.refinement_span_tree( - adjacency_augment_graph, - sorted_edges_wt, - adjacency_graph_list[index], - kopt_parameter, - num_swaps_bound_kopt, - ) - end - - adjacency_star, ac_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] + edge_wt_sorted = sort(edge_wt_list, by = x -> x[2], rev = true) + + # Priority central nodes based on sum of edge weights + priority_central_nodes_list = LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) + + # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity + adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_kopt) + + #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) + #Threads.@threads for index in 1:num_central_nodes_kopt #uncomment this line for multi threading + for index in 1:num_central_nodes_kopt #comment this line for multi threading + # Builds a spanning tree + adjacency_graph_list[index] = LOpt.build_span_tree( + num_nodes, + adjacency_augment_graph, + edge_wt_sorted, + priority_central_nodes_list[index], + ) + + adjacency_graph_list[index] = LOpt.refinement_span_tree( + adjacency_augment_graph, + edge_wt_sorted, + adjacency_graph_list[index], + lom.options.kopt_parameter, + lom.options.num_swaps_bound_kopt, + ) + end + + adjacency_star, ac_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] + + return adjacency_star, ac_star +end - elseif lom.data["augment_budget"] >= num_nodes - print("Under construction") - end - else - if is_base_graph_connected +function heuristic_base_graph_connected(lom::LaplacianOptModel) - # Fiedler vector of base graph - fiedler_vector_base_graph = LOpt.fiedler_vector(adjacency_base_graph) + adjacency_augment_graph = lom.data["adjacency_augment_graph"] + adjacency_base_graph = lom.data["adjacency_base_graph"] - # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) - edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + # Fiedler vector of base graph + fiedler_vector_base_graph = LOpt.fiedler_vector(adjacency_base_graph) - for (index, edge) in enumerate(edge_augment_list) - edges_fiedler_wt[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)] * (fiedler_vector_base_graph[Graphs.src(edge)] - fiedler_vector_base_graph[Graphs.dst(edge)])^2) - end + # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) + edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) + edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - # Sorting the list of tuples in descending order of fiedler weights - sorted_edges_fiedler_wt = sort(edges_fiedler_wt, by = x -> x[2], rev = true) + for (index, edge) in enumerate(edge_augment_list) + edges_fiedler_wt[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)] * (fiedler_vector_base_graph[Graphs.src(edge)] - fiedler_vector_base_graph[Graphs.dst(edge)])^2) + end - # Base graph - G = Graphs.SimpleGraph(adjacency_base_graph) + # Sorting the list of tuples in descending order of fiedler weights + sorted_edges_fiedler_wt = sort(edges_fiedler_wt, by = x -> x[2], rev = true) - # Adding edges based on fiedler weights to construct initial graph with required edges - for i = 1:lom.data["augment_budget"] - Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) - end + # Base graph + G = Graphs.SimpleGraph(adjacency_base_graph) - adjacency_star, ac_star = LOpt.refinement_tree( - G, - adjacency_base_graph, - adjacency_augment_graph, - sorted_edges_fiedler_wt, - kopt_parameter, - num_swaps_bound_kopt, - ) - - elseif !(is_base_graph_connected) - print("Under construction") - end + # Adding edges based on fiedler weights to construct initial graph with required edges + for i = 1:lom.data["augment_budget"] + Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) end - # Implement the bridge to results here - @show adjacency_star, ac_star - return + + adjacency_star, ac_star = LOpt.refinement_tree( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edges_fiedler_wt, + lom.options.kopt_parameter, + lom.options.num_swaps_bound_kopt, + ) + + @show adjacency_star + return adjacency_star, ac_star end function build_span_tree( num_nodes, adjacency_augment_graph, - sorted_edges_wt, + edge_wt_sorted, central_node, ) @@ -119,19 +123,19 @@ function build_span_tree( dir_con_set = Int64[] # Set contains nodes which are directly connected to central node sec_con_set = Int64[] # Set contains nodes which are connected to nodes in dir_con_set - for j in eachindex(sorted_edges_wt) - if (Graphs.src(sorted_edges_wt[j][1]) == central_node || Graphs.dst(sorted_edges_wt[j][1]) == central_node) + for j in eachindex(edge_wt_sorted) + if (Graphs.src(edge_wt_sorted[j][1]) == central_node || Graphs.dst(edge_wt_sorted[j][1]) == central_node) # First edge of the graph - Graphs.add_edge!(G, Graphs.src(sorted_edges_wt[j][1]), Graphs.dst(sorted_edges_wt[j][1])) + Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[j][1]), Graphs.dst(edge_wt_sorted[j][1])) # Adding to directly connected set - Graphs.src(sorted_edges_wt[j][1]) == central_node && push!(dir_con_set, Graphs.dst(sorted_edges_wt[j][1])) - Graphs.dst(sorted_edges_wt[j][1]) == central_node && push!(dir_con_set, Graphs.src(sorted_edges_wt[j][1])) + Graphs.src(edge_wt_sorted[j][1]) == central_node && push!(dir_con_set, Graphs.dst(edge_wt_sorted[j][1])) + Graphs.dst(edge_wt_sorted[j][1]) == central_node && push!(dir_con_set, Graphs.src(edge_wt_sorted[j][1])) # Removing from unconnected set - deleteat!(uncon_set, uncon_set .== Graphs.src(sorted_edges_wt[j][1])) - deleteat!(uncon_set, uncon_set .== Graphs.dst(sorted_edges_wt[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.src(edge_wt_sorted[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.dst(edge_wt_sorted[j][1])) break end end @@ -140,35 +144,35 @@ function build_span_tree( ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,1) for j in 1:(size(dir_con_set)[1]+1) - for k in eachindex(sorted_edges_wt) + for k in eachindex(edge_wt_sorted) if j == 1 - if (Graphs.src(sorted_edges_wt[k][1]) == central_node) && issubset([Graphs.dst(sorted_edges_wt[k][1])], uncon_set) || - (Graphs.dst(sorted_edges_wt[k][1]) == central_node) && issubset([Graphs.src(sorted_edges_wt[k][1])], uncon_set) + if (Graphs.src(edge_wt_sorted[k][1]) == central_node) && issubset([Graphs.dst(edge_wt_sorted[k][1])], uncon_set) || + (Graphs.dst(edge_wt_sorted[k][1]) == central_node) && issubset([Graphs.src(edge_wt_sorted[k][1])], uncon_set) # Adding edge to central node - Graphs.add_edge!(G, Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])) + Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) # Storing current algebraic connectivity - ac_tracker = ((Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + ac_tracker = ((Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) # Removing the added edge - Graphs.rem_edge!(G, Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])) + Graphs.rem_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) break end else - if (Graphs.src(sorted_edges_wt[k][1]) == dir_con_set[j-1]) && issubset([Graphs.dst(sorted_edges_wt[k][1])], uncon_set) || - (Graphs.dst(sorted_edges_wt[k][1]) == dir_con_set[j-1]) && issubset([Graphs.src(sorted_edges_wt[k][1])], uncon_set) + if (Graphs.src(edge_wt_sorted[k][1]) == dir_con_set[j-1]) && issubset([Graphs.dst(edge_wt_sorted[k][1])], uncon_set) || + (Graphs.dst(edge_wt_sorted[k][1]) == dir_con_set[j-1]) && issubset([Graphs.src(edge_wt_sorted[k][1])], uncon_set) # Adding edge to directly connected node - Graphs.add_edge!(G, Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])) + Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) # Updating current algebraic connectivity if LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1))) > ac_tracker[2] - ac_tracker = ((Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + ac_tracker = ((Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) end # Removing the added edge - Graphs.rem_edge!(G, Graphs.src(sorted_edges_wt[k][1]), Graphs.dst(sorted_edges_wt[k][1])) + Graphs.rem_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) break end end @@ -215,7 +219,7 @@ end function refinement_span_tree( adjacency_augment_graph, - sorted_edges_wt, + edge_wt_sorted, adjacency_graph_ac_tuple, kopt_parameter, num_swaps_bound_kopt, @@ -225,8 +229,8 @@ function refinement_span_tree( # Refinement of Algebraic connectivity if kopt_parameter == 1 cycle_basis_current = Vector{Int64} - for i in eachindex(sorted_edges_wt) - if Graphs.add_edge!(G, Graphs.src(sorted_edges_wt[i][1]), Graphs.dst(sorted_edges_wt[i][1])) + for i in eachindex(edge_wt_sorted) + if Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[i][1]), Graphs.dst(edge_wt_sorted[i][1])) if Graphs.is_cyclic(G) cycle_basis_current = Graphs.cycle_basis(G)[1] ac_tracker = 0.0 @@ -249,7 +253,7 @@ function refinement_span_tree( updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 2 - combinations = LOpt.edge_combinations(length(sorted_edges_wt), kopt_parameter) + combinations = LOpt.edge_combinations(length(edge_wt_sorted), kopt_parameter) cycle_basis_current = Vector{Int64} if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) @@ -257,9 +261,9 @@ function refinement_span_tree( num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_wt[combinations[i][1]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_wt[combinations[i][2]][1])] == 0 - G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_wt[combinations[i][2]][1]))]) + if Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])] == 0 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])),(Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1]))]) if Graphs.is_cyclic(G) cycle_basis_current = Graphs.cycle_basis(G) ac_tracker = 0.0 @@ -272,7 +276,7 @@ function refinement_span_tree( updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 3 - combinations = LOpt.edge_combinations(length(sorted_edges_wt), kopt_parameter) + combinations = LOpt.edge_combinations(length(edge_wt_sorted), kopt_parameter) cycle_basis_current = Vector{Int64} if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) @@ -280,10 +284,10 @@ function refinement_span_tree( num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_wt[combinations[i][1]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_wt[combinations[i][2]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(sorted_edges_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_wt[combinations[i][3]][1])] == 0 - G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_wt[combinations[i][2]][1])),(Graphs.src(sorted_edges_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_wt[combinations[i][3]][1]))]) + if Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][3]][1]), Graphs.dst(edge_wt_sorted[combinations[i][3]][1])] == 0 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])),(Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])),(Graphs.src(edge_wt_sorted[combinations[i][3]][1]), Graphs.dst(edge_wt_sorted[combinations[i][3]][1]))]) if Graphs.is_cyclic(G) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] diff --git a/src/lopt_model.jl b/src/lopt_model.jl index cf8fc81..b69beeb 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -14,7 +14,10 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt._logging_info(lom) if lom.options.formulation_type == "max_λ2" - if lom.options.solution_type == "optimal" + if lom.options.solution_type == "heuristic" + return lom + + elseif lom.options.solution_type == "optimal" # Populate PMinor indices lom.minor_idx_dict = LOpt._PMinorIdx(lom.data["num_nodes"], lom.options.eigen_cuts_sizes) @@ -22,9 +25,7 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.variable_LOModel(lom) LOpt.constraint_LOModel(lom; optimizer = optimizer) LOpt.objective_LOModel(lom) - - elseif lom.options.solution_type == "heuristic" - LOpt.heuristic_kopt(lom) + end elseif lom.options.formulation_type == "max_span_tree" if lom.options.solution_type in ["optimal"] @@ -123,7 +124,11 @@ function run_LOpt( ) data = LOpt.get_data(params) model_lopt = LOpt.build_LOModel(data, optimizer = lom_optimizer, options = options) - result_lopt = LOpt.optimize_LOModel!(model_lopt, optimizer = lom_optimizer) + if (:solution_type in keys(options)) && (options[:solution_type] == "heuristic") + result_lopt = LOpt.heuristic_kopt(model_lopt) + else + result_lopt = LOpt.optimize_LOModel!(model_lopt, optimizer = lom_optimizer) + end if visualize_solution LOpt.visualize_solution( diff --git a/src/solution.jl b/src/solution.jl index 1bdc20d..676b16e 100644 --- a/src/solution.jl +++ b/src/solution.jl @@ -103,16 +103,12 @@ function optimality_certificate_MISDP(lom::LaplacianOptModel, result::Dict{Strin end end -function build_LOModel_heuristic_result(lom::LaplacianOptModel, solve_time::Number) - - solution = Dict{String,Any}() +function build_LOModel_heuristic_result(lom::LaplacianOptModel, heuristic_time::Number, adjacency_star, ac_star) result = Dict{String,Any}( - "optimizer" => JuMP.solver_name(lom.model), - # "objective" => LOpt.get_objective_value(lom.model), - "solve_time" => solve_time, - "solution" => solution, - "solution_type" => lom.options.solution_type, + "heuristic_objective" => ac_star, + "heuristic_solve_time" => heuristic_time, + "heuristic_solution" => adjacency_star, "adjacency_base_graph" => lom.data["adjacency_base_graph"], "adjacency_augment_graph" => lom.data["adjacency_augment_graph"], ) From 2aed63aad41aa99405fc2bacc0ba99cf52001975 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Mon, 14 Aug 2023 13:52:20 -0600 Subject: [PATCH 17/35] remove show --- src/heuristics.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index 80550ec..85ca4af 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -106,7 +106,6 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) lom.options.num_swaps_bound_kopt, ) - @show adjacency_star return adjacency_star, ac_star end From 62926f0c1a7b22841b03175f35f825449953dc0b Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Wed, 16 Aug 2023 15:22:30 -0500 Subject: [PATCH 18/35] corrected heuristics --- examples/run_examples.jl | 5 +- src/heuristics.jl | 152 ++++++++++++++++++++++++++------------- src/utility.jl | 2 +- 3 files changed, 105 insertions(+), 54 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 066eb61..97bf381 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -34,7 +34,7 @@ end =# function data_II() data_dict = Dict{String,Any}() - data_dict["num_nodes"] = 4 + data_dict["num_nodes"] = 5 data_dict["adjacency_base_graph"] = [0 2 0 0; 2 0 3 0; 0 3 0 4; 0 0 4 0] data_dict["adjacency_augment_graph"] = [0 0 4 8; 0 0 0 7; 4 0 0 0; 8 7 0 0] augment_budget = 2 @@ -46,7 +46,8 @@ end #-------------------------------# num_nodes = 5 instance = 11 -data_dict, augment_budget = data_I(num_nodes, instance) +# data_dict, augment_budget = data_I(num_nodes, instance) +data_dict, augment_budget = data_II() params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) diff --git a/src/heuristics.jl b/src/heuristics.jl index 5e90ced..09474f2 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -87,7 +87,7 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central for i = 1:augment_budget add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) end - + @show Matrix(adjacency_matrix(G)) adjacency_graph_star, algebraic_connectivity_star = refinement_tree( G, adjacency_base_graph, @@ -353,25 +353,14 @@ function refinement_tree( ) if kopt_parameter == 1 - for i in eachindex(sorted_edge_fiedlerweight_list) - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1]))) - add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = (undef, undef) - for edge in edges(G) - if adjacency_base_graph[src(edge), dst(edge)] == 0 - rem_edge!(G, src(edge), dst(edge)) - if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) - vertices_tracker = edge - end - add_edge!(G, src(edge), dst(edge)) - end - end - rem_edge!(G, vertices_tracker) - end + if length(sorted_edge_fiedlerweight_list) <= num_kopt_swaps_upperbound + num_swaps = length(sorted_edge_fiedlerweight_list) + else + num_swaps = num_kopt_swaps_upperbound + end + for i = 1:num_swaps + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edge_fiedlerweight_list[i][1]) end - elseif kopt_parameter == 2 combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) if length(combinations) <= num_kopt_swaps_upperbound @@ -379,17 +368,10 @@ function refinement_tree( else num_swaps = num_kopt_swaps_upperbound end - for i in 1:num_swaps - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) - G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = [(undef, undef),(undef, undef)] - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, algebraic_connectivity_tracker,vertices_tracker) - G = rem_multiple_edges!(G, vertices_tracker) - end + @show num_swaps + for i = 1:num_swaps + G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edge_fiedlerweight_list[combinations[i][1]][1], sorted_edge_fiedlerweight_list[combinations[i][2]][1]) end - elseif kopt_parameter == 3 combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) if length(combinations) <= num_kopt_swaps_upperbound @@ -397,44 +379,114 @@ function refinement_tree( else num_swaps = num_kopt_swaps_upperbound end - for i in 1:num_swaps - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))) - G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])), (src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))]) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in eachindex(length(collect(edges(G))) - 2) - if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, algebraic_connectivity_tracker,vertices_tracker) - end + for i = 1:num_swaps + G = refinement_tree_3opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edge_fiedlerweight_list[combinations[i][1]][1], sorted_edge_fiedlerweight_list[combinations[i][2]][1], sorted_edge_fiedlerweight_list[combinations[i][3]][1]) + end + end + return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) +end + +function refinement_tree_1opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check + ) + if !(has_edge(G, src(edge_to_check), dst(edge_to_check))) + add_edge!(G, src(edge_to_check), dst(edge_to_check)) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = (undef, undef) + for edge in edges(G) + if adjacency_base_graph[src(edge), dst(edge)] == 0 + rem_edge!(G, src(edge), dst(edge)) + if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + vertices_tracker = edge end - G = rem_multiple_edges!(G, vertices_tracker) + add_edge!(G, src(edge), dst(edge)) end end + rem_edge!(G, vertices_tracker) end + return G +end - return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) +function refinement_tree_2opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_1, + edge_to_check_2, + ) + if !(has_edge(G, src(edge_to_check_1), dst(edge_to_check_1))) && !(has_edge(G, src(edge_to_check_2), dst(edge_to_check_2))) + G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = [(undef, undef),(undef, undef)] + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, algebraic_connectivity_tracker,vertices_tracker) + G = rem_multiple_edges!(G, vertices_tracker) + elseif !(has_edge(G, src(edge_to_check_1), dst(edge_to_check_1))) && (has_edge( G, src(edge_to_check_2), dst(edge_to_check_2))) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1) + elseif !(has_edge(G, src(edge_to_check_2), dst(edge_to_check_2))) && (has_edge( G, src(edge_to_check_1), dst(edge_to_check_1))) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2) + end + return G +end + +function refinement_tree_3opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_1, + edge_to_check_2, + edge_to_check_3, + ) + con1 = !(has_edge(G, src(edge_to_check_1), dst(edge_to_check_1))) + con2 = !(has_edge(G, src(edge_to_check_2), dst(edge_to_check_2))) + con3 = !(has_edge(G, src(edge_to_check_3), dst(edge_to_check_3))) + if (con1 && con2 && con3) + G = add_multiple_edges!(G, [(src(edge_to_check_1), dst(edge_to_check_1)),(src(edge_to_check_2), dst(edge_to_check_2)), (src(edge_to_check_3), dst(edge_to_check_3))]) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] + for i in eachindex(length(collect(edges(G))) - 2) + if (adjacency_base_graph[src(edge_list[i]), dst(edge_list[i])] == 0) + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, i, algebraic_connectivity_tracker,vertices_tracker) + end + end + G = rem_multiple_edges!(G, vertices_tracker) + elseif (con1 && con2 && !(con3)) + G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1, edge_to_check_2) + elseif (con1 && !(con2) && con3) + G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1, edge_to_check_3) + elseif (!(con1) && con2 && con3) + G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2, edge_to_check_3) + elseif (con1 && !(con2) && !(con3)) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1) + elseif (!(con1) && con2 && !(con3)) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2) + elseif (!(con1) && !(con2) && con3) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_3) + end + return G end function vertices_tracker_update_two_edges!( G, adjacency_base_graph, adjacency_augment_graph, - j, # Index for third edge if kopt_parameter is 3 + i, # Index for third edge if kopt_parameter is 3 algebraic_connectivity_tracker, vertices_tracker ) edge_list = collect(edges(G)) - for k in j+1:(length(edge_list)-1) - for l in k+1:length(edge_list) + for j in i+1:(length(edge_list)-1) + for k in j+1:length(edge_list) edges_to_check = [] - if j == 0 - edges_to_check = [(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] + if i == 0 + edges_to_check = [(src(edge_list[j]), dst(edge_list[j])), (src(edge_list[k]), dst(edge_list[k]))] else - edges_to_check = [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] + edges_to_check = [(src(edge_list[i]), dst(edge_list[i])),(src(edge_list[j]), dst(edge_list[j])), (src(edge_list[k]), dst(edge_list[k]))] end - if (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) + if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) G = rem_multiple_edges!(G, edges_to_check) if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) @@ -444,7 +496,5 @@ function vertices_tracker_update_two_edges!( end end end - return algebraic_connectivity_tracker, vertices_tracker end - diff --git a/src/utility.jl b/src/utility.jl index 78bb3cb..c39f0c9 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -123,7 +123,7 @@ function algebraic_connectivity(adjacency_matrix::Array{<:Number}) L_mat = LOpt.laplacian_matrix(adjacency_matrix) ac = sort(LA.eigvals(L_mat))[2] - + @show ac return ac end From 68e93ede2dff10af96e55cbd8e6d67eaa4105581 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Wed, 16 Aug 2023 15:57:27 -0500 Subject: [PATCH 19/35] updating --- examples/run_examples.jl | 7 +- src/heuristics.jl | 577 ++++++++++++++++++++++----------------- src/lopt_model.jl | 17 +- src/solution.jl | 13 + src/types.jl | 12 +- src/utility.jl | 22 +- test/heuristics_tests.jl | 12 +- 7 files changed, 369 insertions(+), 291 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 97bf381..48d6206 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -24,7 +24,7 @@ function data_I(num_nodes::Int, instance::Int) file_path = joinpath(@__DIR__, "instances/$(num_nodes)_nodes/$(num_nodes)_$(instance).json") data_dict = LOpt.parse_file(file_path) - augment_budget = 1 #(num_nodes - 1) # spanning tree constraint + augment_budget = (num_nodes - 1) # spanning tree constraint return data_dict, augment_budget end @@ -46,8 +46,7 @@ end #-------------------------------# num_nodes = 5 instance = 11 -# data_dict, augment_budget = data_I(num_nodes, instance) -data_dict, augment_budget = data_II() +data_dict, augment_budget = data_I(num_nodes, instance) params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -62,7 +61,7 @@ model_options = Dict{Symbol,Any}( :topology_flow_cuts => true, :solution_type => "heuristic", :kopt_parameter => 3, - :num_central_nodes_verifier => 3, + :num_central_nodes_kopt => 5, ) result = LOpt.run_LOpt( diff --git a/src/heuristics.jl b/src/heuristics.jl index 09474f2..4cdf22b 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,93 +1,102 @@ # Heuristics to maximize algebraic connectivity of weighted graphs -using Graphs -using LinearAlgebra +function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) + adjacency_star = [] + ac_star = [] -function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central_nodes_verifier::Int, num_kopt_swaps_upperbound::Int) - num_nodes = data["num_nodes"] - adjacency_augment_graph = data["adjacency_augment_graph"] - adjacency_base_graph = data["adjacency_base_graph"] - num_edges_existing = data["num_edges_existing"] - augment_budget = data["augment_budget"] - is_base_graph_connected = data["is_base_graph_connected"] + if (lom.data["num_edges_existing"] == 0) && (lom.data["augment_budget"] == (lom.data["num_nodes"] - 1)) + _, heuristic_time, solve_bytes_alloc, sec_in_gc = @timed adjacency_star, ac_star = LOpt.heuristic_spanning_tree(lom) + elseif (lom.data["num_edges_existing"] > 0) && (lom.data["is_base_graph_connected"]) + _, heuristic_time, solve_bytes_alloc, sec_in_gc = @timed adjacency_star, ac_star = LOpt.heuristic_base_graph_connected(lom) + else + Memento.error(_LOGGER, "Heuristic for this option is not yet supported.") + end + + # Implement the bridge to results here + result_dict = LOpt.build_LOModel_heuristic_result(lom, heuristic_time, adjacency_star, ac_star) - # Initializing the heuristic solutions - adjacency_graph_star = Matrix{Float64}(zeros(num_nodes, num_nodes)) - algebraic_connectivity_star = 0 + return merge(lom.result, result_dict) +end + +function heuristic_spanning_tree(lom::LaplacianOptModel) + + num_nodes = lom.data["num_nodes"] + adjacency_augment_graph = lom.data["adjacency_augment_graph"] + num_central_nodes_kopt = lom.options.num_central_nodes_kopt # Making list of all possible edges to augment - edge_augment_list = collect(edges(SimpleGraph(adjacency_augment_graph))) + edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) # Making list of tuples of edges and edge weights - edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - sorted_edge_edgeweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + edge_wt_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + edge_wt_sorted = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) for (index, edge) in enumerate(edge_augment_list) - edge_edgeweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)]) + edge_wt_list[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)]) end # Sorting the list of tuples in descending order of edge weights - sorted_edge_edgeweight_list = sort(edge_edgeweight_list, by = x -> x[2], rev = true) - - if num_edges_existing == 0 - if augment_budget == num_nodes - 1 #spanning tree - - # Priority central nodes based on sum of edge weights - priority_central_nodes_list = priority_central_nodes(adjacency_augment_graph, num_nodes) - - # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity - adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_verifier) - - #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) - #Threads.@threads for index in 1:num_central_nodes_verifier #uncomment this line for multi threading - for index in 1:num_central_nodes_verifier #comment this line for multi threading - # Builds a spanning tree - adjacency_graph_list[index] = build_span_tree( - num_nodes, - adjacency_augment_graph, - sorted_edge_edgeweight_list, - priority_central_nodes_list[index], - ) - - adjacency_graph_list[index] = refinement_span_tree( - adjacency_augment_graph, - sorted_edge_edgeweight_list, - adjacency_graph_list[index], - kopt_parameter, - num_kopt_swaps_upperbound, - ) - end + edge_wt_sorted = sort(edge_wt_list, by = x -> x[2], rev = true) + + # Priority central nodes based on sum of edge weights + priority_central_nodes_list = LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) + + # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity + adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_kopt) + + #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) + #Threads.@threads for index in 1:num_central_nodes_kopt #uncomment this line for multi threading + for index in 1:num_central_nodes_kopt #comment this line for multi threading + # Builds a spanning tree + adjacency_graph_list[index] = LOpt.build_span_tree( + num_nodes, + adjacency_augment_graph, + edge_wt_sorted, + priority_central_nodes_list[index], + ) + + adjacency_graph_list[index] = LOpt.refinement_span_tree( + adjacency_augment_graph, + edge_wt_sorted, + adjacency_graph_list[index], + lom.options.kopt_parameter, + lom.options.num_swaps_bound_kopt, + ) + end + + adjacency_star, ac_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] + + return adjacency_star, ac_star +end - adjacency_graph_star, algebraic_connectivity_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] +function heuristic_base_graph_connected(lom::LaplacianOptModel) - elseif augment_budget >= num_nodes - print("Under construction") - end - else - if is_base_graph_connected + adjacency_augment_graph = lom.data["adjacency_augment_graph"] + adjacency_base_graph = lom.data["adjacency_base_graph"] - # Fiedler vector of base graph - fiedler_vector_base_graph = fiedler_vector(adjacency_base_graph) + # Fiedler vector of base graph + fiedler_vector_base_graph = LOpt.fiedler_vector(adjacency_base_graph) - # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) - edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - sorted_edge_fiedlerweight_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) + edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) + edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - for (index, edge) in enumerate(edge_augment_list) - edge_fiedlerweight_list[index] = (edge, adjacency_augment_graph[src(edge), dst(edge)] * (fiedler_vector_base_graph[src(edge)] - fiedler_vector_base_graph[dst(edge)])^2) - end + for (index, edge) in enumerate(edge_augment_list) + edges_fiedler_wt[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)] * (fiedler_vector_base_graph[Graphs.src(edge)] - fiedler_vector_base_graph[Graphs.dst(edge)])^2) + end - # Sorting the list of tuples in descending order of fiedler weights - sorted_edge_fiedlerweight_list = sort(edge_fiedlerweight_list, by = x -> x[2], rev = true) + # Sorting the list of tuples in descending order of fiedler weights + sorted_edges_fiedler_wt = sort(edges_fiedler_wt, by = x -> x[2], rev = true) - # Base graph - G = SimpleGraph(adjacency_base_graph) + # Base graph + G = Graphs.SimpleGraph(adjacency_base_graph) # Adding edges based on fiedler weights to construct initial graph with required edges for i = 1:augment_budget add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) end - @show Matrix(adjacency_matrix(G)) + adjacency_graph_star, algebraic_connectivity_star = refinement_tree( G, adjacency_base_graph, @@ -97,78 +106,72 @@ function heuristic_kopt(data::Dict{String,Any}, kopt_parameter::Int, num_central num_kopt_swaps_upperbound, ) - elseif !(is_base_graph_connected) - print("Under construction") - end - end - # Implement the bridge to results here - @show adjacency_graph_star, algebraic_connectivity_star - return + return adjacency_star, ac_star end function build_span_tree( num_nodes, adjacency_augment_graph, - sorted_edge_edgeweight_list, + edge_wt_sorted, central_node, ) - G = SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph + G = Graphs.SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph uncon_set = Int64[] # Set contains all unconnected nodes uncon_set = collect(1:num_nodes) dir_con_set = Int64[] # Set contains nodes which are directly connected to central node sec_con_set = Int64[] # Set contains nodes which are connected to nodes in dir_con_set - for j in eachindex(sorted_edge_edgeweight_list) - if (src(sorted_edge_edgeweight_list[j][1]) == central_node || dst(sorted_edge_edgeweight_list[j][1]) == central_node) + for j in eachindex(edge_wt_sorted) + if (Graphs.src(edge_wt_sorted[j][1]) == central_node || Graphs.dst(edge_wt_sorted[j][1]) == central_node) # First edge of the graph - add_edge!(G, src(sorted_edge_edgeweight_list[j][1]), dst(sorted_edge_edgeweight_list[j][1])) + Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[j][1]), Graphs.dst(edge_wt_sorted[j][1])) # Adding to directly connected set - src(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, dst(sorted_edge_edgeweight_list[j][1])) - dst(sorted_edge_edgeweight_list[j][1]) == central_node && push!(dir_con_set, src(sorted_edge_edgeweight_list[j][1])) + Graphs.src(edge_wt_sorted[j][1]) == central_node && push!(dir_con_set, Graphs.dst(edge_wt_sorted[j][1])) + Graphs.dst(edge_wt_sorted[j][1]) == central_node && push!(dir_con_set, Graphs.src(edge_wt_sorted[j][1])) # Removing from unconnected set - deleteat!(uncon_set, uncon_set .== src(sorted_edge_edgeweight_list[j][1])) - deleteat!(uncon_set, uncon_set .== dst(sorted_edge_edgeweight_list[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.src(edge_wt_sorted[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.dst(edge_wt_sorted[j][1])) break end end - while !is_connected(G) # Connecting nodes until a spanning tree is formed - algebraic_connectivity_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,1) + while !Graphs.is_connected(G) # Connecting nodes until a spanning tree is formed + ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,1) for j in 1:(size(dir_con_set)[1]+1) - for k in eachindex(sorted_edge_edgeweight_list) + for k in eachindex(edge_wt_sorted) if j == 1 - if (src(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || - (dst(sorted_edge_edgeweight_list[k][1]) == central_node) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_set) + if (Graphs.src(edge_wt_sorted[k][1]) == central_node) && issubset([Graphs.dst(edge_wt_sorted[k][1])], uncon_set) || + (Graphs.dst(edge_wt_sorted[k][1]) == central_node) && issubset([Graphs.src(edge_wt_sorted[k][1])], uncon_set) # Adding edge to central node - add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) # Storing current algebraic connectivity - algebraic_connectivity_tracker = ((src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])),algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + ac_tracker = ((Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) # Removing the added edge - rem_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + Graphs.rem_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) break end else - if (src(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([dst(sorted_edge_edgeweight_list[k][1])], uncon_set) || - (dst(sorted_edge_edgeweight_list[k][1]) == dir_con_set[j-1]) && issubset([src(sorted_edge_edgeweight_list[k][1])], uncon_set) + if (Graphs.src(edge_wt_sorted[k][1]) == dir_con_set[j-1]) && issubset([Graphs.dst(edge_wt_sorted[k][1])], uncon_set) || + (Graphs.dst(edge_wt_sorted[k][1]) == dir_con_set[j-1]) && issubset([Graphs.src(edge_wt_sorted[k][1])], uncon_set) # Adding edge to directly connected node - add_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) # Updating current algebraic connectivity - if algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1))) > algebraic_connectivity_tracker[2] - algebraic_connectivity_tracker = ((src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])),algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + if LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1))) > ac_tracker[2] + ac_tracker = ((Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) end # Removing the added edge - rem_edge!(G, src(sorted_edge_edgeweight_list[k][1]), dst(sorted_edge_edgeweight_list[k][1])) + Graphs.rem_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) break end end @@ -176,142 +179,250 @@ function build_span_tree( end # Adding edge - add_edge!(G, algebraic_connectivity_tracker[1][1], algebraic_connectivity_tracker[1][2]) + Graphs.add_edge!(G, ac_tracker[1][1], ac_tracker[1][2]) - if (algebraic_connectivity_tracker[1][1] == central_node) && issubset(algebraic_connectivity_tracker[1][2], uncon_set) + if (ac_tracker[1][1] == central_node) && issubset(ac_tracker[1][2], uncon_set) # Adding node to directly connected set - push!(dir_con_set, algebraic_connectivity_tracker[1][2]) + push!(dir_con_set, ac_tracker[1][2]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][2]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][2]) - elseif (algebraic_connectivity_tracker[1][2] == central_node) && issubset(algebraic_connectivity_tracker[1][1], uncon_set) + elseif (ac_tracker[1][2] == central_node) && issubset(ac_tracker[1][1], uncon_set) # Adding node to directly connected set - push!(dir_con_set, algebraic_connectivity_tracker[1][1]) + push!(dir_con_set, ac_tracker[1][1]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][1]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][1]) - elseif issubset(algebraic_connectivity_tracker[1][1], dir_con_set) && issubset(algebraic_connectivity_tracker[1][2], uncon_set) + elseif issubset(ac_tracker[1][1], dir_con_set) && issubset(ac_tracker[1][2], uncon_set) # Adding node to secondary connected set - push!(sec_con_set, algebraic_connectivity_tracker[1][2]) + push!(sec_con_set, ac_tracker[1][2]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][2]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][2]) - elseif issubset(algebraic_connectivity_tracker[1][2], dir_con_set) && issubset(algebraic_connectivity_tracker[1][1], uncon_set) + elseif issubset(ac_tracker[1][2], dir_con_set) && issubset(ac_tracker[1][1], uncon_set) # Adding node to secondary connected set - push!(sec_con_set, algebraic_connectivity_tracker[1][1]) + push!(sec_con_set, ac_tracker[1][1]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== algebraic_connectivity_tracker[1][1]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][1]) end end - return (Matrix(adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G)))) + return (Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G)))) end function refinement_span_tree( adjacency_augment_graph, - sorted_edge_edgeweight_list, + edge_wt_sorted, adjacency_graph_ac_tuple, kopt_parameter, - num_kopt_swaps_upperbound, + num_swaps_bound_kopt, ) - G = SimpleGraph(adjacency_graph_ac_tuple[1]) + G = Graphs.SimpleGraph(adjacency_graph_ac_tuple[1]) # Refinement of Algebraic connectivity if kopt_parameter == 1 cycle_basis_current = Vector{Int64} - for i in eachindex(sorted_edge_edgeweight_list) - if add_edge!(G, src(sorted_edge_edgeweight_list[i][1]), dst(sorted_edge_edgeweight_list[i][1])) - if is_cyclic(G) - cycle_basis_current = cycle_basis(G)[1] - algebraic_connectivity_tracker = 0.0 + for i in eachindex(edge_wt_sorted) + if Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[i][1]), Graphs.dst(edge_wt_sorted[i][1])) + if Graphs.is_cyclic(G) + cycle_basis_current = Graphs.cycle_basis(G)[1] + ac_tracker = 0.0 vertices_tracker = [(undef, undef)] for j = 1:(length(cycle_basis_current)-1) for k = (j+1):length(cycle_basis_current) - if rem_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + if Graphs.rem_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) + if LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) vertices_tracker = [(cycle_basis_current[j], cycle_basis_current[k])] end - add_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) + Graphs.add_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) end end end - rem_edge!(G, vertices_tracker[1][1], vertices_tracker[1][2]) + Graphs.rem_edge!(G, vertices_tracker[1][1], vertices_tracker[1][2]) end end end - updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 2 - combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + combinations = LOpt.edge_combinations(length(edge_wt_sorted), kopt_parameter) cycle_basis_current = Vector{Int64} - if length(combinations) <= num_kopt_swaps_upperbound + if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else - num_swaps = num_kopt_swaps_upperbound + num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && - adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 - G = add_multiple_edges!(G, [(src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1]))]) - if is_cyclic(G) - cycle_basis_current = cycle_basis(G) - algebraic_connectivity_tracker = 0.0 + if Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])] == 0 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])),(Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1]))]) + if Graphs.is_cyclic(G) + cycle_basis_current = Graphs.cycle_basis(G) + ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef)] - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, 0, 0, algebraic_connectivity_tracker, vertices_tracker) - G = rem_multiple_edges!(G, vertices_tracker) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_two_edges!(G, adjacency_augment_graph, 0, 0, ac_tracker, vertices_tracker) + G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end end - updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) elseif kopt_parameter == 3 - combinations = edge_combinations(length(sorted_edge_edgeweight_list), kopt_parameter) + combinations = LOpt.edge_combinations(length(edge_wt_sorted), kopt_parameter) cycle_basis_current = Vector{Int64} - if length(combinations) <= num_kopt_swaps_upperbound + if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else - num_swaps = num_kopt_swaps_upperbound + num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])] == 0 && - adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])] == 0 && - adjacency_matrix(G)[src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1])] == 0 - G = add_multiple_edges!(G, [(src(sorted_edge_edgeweight_list[combinations[i][1]][1]), dst(sorted_edge_edgeweight_list[combinations[i][1]][1])),(src(sorted_edge_edgeweight_list[combinations[i][2]][1]), dst(sorted_edge_edgeweight_list[combinations[i][2]][1])),(src(sorted_edge_edgeweight_list[combinations[i][3]][1]), dst(sorted_edge_edgeweight_list[combinations[i][3]][1]))]) - if is_cyclic(G) - algebraic_connectivity_tracker = 0.0 + if Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])] == 0 && + Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][3]][1]), Graphs.dst(edge_wt_sorted[combinations[i][3]][1])] == 0 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])),(Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])),(Graphs.src(edge_wt_sorted[combinations[i][3]][1]), Graphs.dst(edge_wt_sorted[combinations[i][3]][1]))]) + if Graphs.is_cyclic(G) + ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in 1:(length(cycle_basis(G)[3])-1) - for k in (j+1):length(cycle_basis(G)[3]) - if adjacency_matrix(G)[cycle_basis(G)[3][j], cycle_basis(G)[3][k]] == 1 - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_span_update_two_edges!(G, adjacency_augment_graph, j, k, algebraic_connectivity_tracker, vertices_tracker) + for j in 1:(length(Graphs.cycle_basis(G)[3])-1) + for k in (j+1):length(Graphs.cycle_basis(G)[3]) + if Graphs.adjacency_matrix(G)[Graphs.cycle_basis(G)[3][j], Graphs.cycle_basis(G)[3][k]] == 1 + ac_tracker, vertices_tracker = LOpt._vertices_tracker_two_edges!(G, adjacency_augment_graph, j, k, ac_tracker, vertices_tracker) end end end - G = rem_multiple_edges!(G, vertices_tracker) + G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end end - updated_adjacency_graph_ac_tuple = update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) end return updated_adjacency_graph_ac_tuple end -function vertices_tracker_span_update_two_edges!( +function refinement_tree( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edges_fiedler_wt, + kopt_parameter, + num_swaps_bound_kopt, + ) + + if kopt_parameter == 1 + for i in eachindex(sorted_edges_fiedler_wt) + if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1]))) + Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) + ac_tracker = 0.0 + vertices_tracker = (undef, undef) + for edge in Graphs.edges(G) + if adjacency_base_graph[Graphs.src(edge), Graphs.dst(edge)] == 0 + Graphs.rem_edge!(G, Graphs.src(edge), Graphs.dst(edge)) + if LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) + vertices_tracker = edge + end + Graphs.add_edge!(G, Graphs.src(edge), Graphs.dst(edge)) + end + end + Graphs.rem_edge!(G, vertices_tracker) + end + end + + elseif kopt_parameter == 2 + combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) + if length(combinations) <= num_swaps_bound_kopt + num_swaps = length(combinations) + else + num_swaps = num_swaps_bound_kopt + end + for i in 1:num_swaps + if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))) + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))]) + ac_tracker = 0.0 + vertices_tracker = [(undef, undef),(undef, undef)] + ac_tracker, vertices_tracker = _vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, ac_tracker,vertices_tracker) + G = LOpt.remove_multiple_edges!(G, vertices_tracker) + end + end + + elseif kopt_parameter == 3 + combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) + if length(combinations) <= num_swaps_bound_kopt + num_swaps = length(combinations) + else + num_swaps = num_swaps_bound_kopt + end + for i in 1:num_swaps + if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))) && + !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][3]][1]))) + G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1])), (Graphs.src(sorted_edges_fiedler_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][3]][1]))]) + ac_tracker = 0.0 + vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] + for j in eachindex(length(collect(Graphs.edges(G))) - 2) + if (adjacency_base_graph[Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])] == 0) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, ac_tracker,vertices_tracker) + end + end + G = LOpt.remove_multiple_edges!(G, vertices_tracker) + end + end + + else + Memento.eror(_LOGGER, "kopt_parameter > 3 is currently not supported.") + end + + return Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) +end + +function _vertices_tracker_update_two_edges!( + G, + adjacency_base_graph, + adjacency_augment_graph, + j, # Index for third edge if kopt_parameter is 3 + ac_tracker, + vertices_tracker + ) + edge_list = collect(Graphs.edges(G)) + for k in j+1:(length(edge_list)-1) + for l in k+1:length(edge_list) + edges_to_check = [] + if j == 0 + edges_to_check = [(Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l]))] + else + edges_to_check = [(Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])),(Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l]))] + end + if (adjacency_base_graph[Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])] == 0) && (adjacency_base_graph[Graphs.src(edge_list[l]), Graphs.dst(edge_list[l])] == 0) + G = LOpt.remove_multiple_edges!(G, edges_to_check) + if LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) + vertices_tracker = edges_to_check + end + G = LOpt.add_multiple_edges!(G, edges_to_check) + end + end + end + + return ac_tracker, vertices_tracker +end + +function _vertices_tracker_two_edges!( G, adjacency_augment_graph, j, # Index for third edge src if kopt_parameter is 3 k, # Index for third edge dst if kopt_parameter is 3 - algebraic_connectivity_tracker, + ac_tracker, vertices_tracker, ) - cycle_basis_current = cycle_basis(G) + cycle_basis_current = Graphs.cycle_basis(G) for l in 1:(length(cycle_basis_current[2])-1) for m in (l+1):length(cycle_basis_current[2]) for p in 1:(length(cycle_basis_current[1])-1) @@ -327,13 +438,13 @@ function vertices_tracker_span_update_two_edges!( (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) end if cycle_basis_condition - if adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && adjacency_matrix(G)[cycle_basis_current[1][p], cycle_basis_current[1][q]] == 1 - G = rem_multiple_edges!(G, cycle_basis_to_check) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + if Graphs.adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && Graphs.adjacency_matrix(G)[cycle_basis_current[1][p], cycle_basis_current[1][q]] == 1 + G = LOpt.remove_multiple_edges!(G, cycle_basis_to_check) + if LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) vertices_tracker = cycle_basis_to_check end - G = add_multiple_edges!(G, cycle_basis_to_check) + G = LOpt.add_multiple_edges!(G, cycle_basis_to_check) end end end @@ -353,14 +464,25 @@ function refinement_tree( ) if kopt_parameter == 1 - if length(sorted_edge_fiedlerweight_list) <= num_kopt_swaps_upperbound - num_swaps = length(sorted_edge_fiedlerweight_list) - else - num_swaps = num_kopt_swaps_upperbound - end - for i = 1:num_swaps - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edge_fiedlerweight_list[i][1]) + for i in eachindex(sorted_edge_fiedlerweight_list) + if !(has_edge(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1]))) + add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = (undef, undef) + for edge in edges(G) + if adjacency_base_graph[src(edge), dst(edge)] == 0 + rem_edge!(G, src(edge), dst(edge)) + if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker + algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) + vertices_tracker = edge + end + add_edge!(G, src(edge), dst(edge)) + end + end + rem_edge!(G, vertices_tracker) + end end + elseif kopt_parameter == 2 combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) if length(combinations) <= num_kopt_swaps_upperbound @@ -368,10 +490,17 @@ function refinement_tree( else num_swaps = num_kopt_swaps_upperbound end - @show num_swaps - for i = 1:num_swaps - G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edge_fiedlerweight_list[combinations[i][1]][1], sorted_edge_fiedlerweight_list[combinations[i][2]][1]) + for i in 1:num_swaps + if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && + !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) + G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = [(undef, undef),(undef, undef)] + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, algebraic_connectivity_tracker,vertices_tracker) + G = rem_multiple_edges!(G, vertices_tracker) + end end + elseif kopt_parameter == 3 combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) if length(combinations) <= num_kopt_swaps_upperbound @@ -379,114 +508,44 @@ function refinement_tree( else num_swaps = num_kopt_swaps_upperbound end - for i = 1:num_swaps - G = refinement_tree_3opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edge_fiedlerweight_list[combinations[i][1]][1], sorted_edge_fiedlerweight_list[combinations[i][2]][1], sorted_edge_fiedlerweight_list[combinations[i][3]][1]) - end - end - return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) -end - -function refinement_tree_1opt!( - G, - adjacency_base_graph, - adjacency_augment_graph, - edge_to_check - ) - if !(has_edge(G, src(edge_to_check), dst(edge_to_check))) - add_edge!(G, src(edge_to_check), dst(edge_to_check)) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = (undef, undef) - for edge in edges(G) - if adjacency_base_graph[src(edge), dst(edge)] == 0 - rem_edge!(G, src(edge), dst(edge)) - if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) - vertices_tracker = edge + for i in 1:num_swaps + if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && + !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) && + !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))) + G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])), (src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))]) + algebraic_connectivity_tracker = 0.0 + vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] + for j in eachindex(length(collect(edges(G))) - 2) + if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) + algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, algebraic_connectivity_tracker,vertices_tracker) + end end - add_edge!(G, src(edge), dst(edge)) + G = rem_multiple_edges!(G, vertices_tracker) end end - rem_edge!(G, vertices_tracker) end - return G -end -function refinement_tree_2opt!( - G, - adjacency_base_graph, - adjacency_augment_graph, - edge_to_check_1, - edge_to_check_2, - ) - if !(has_edge(G, src(edge_to_check_1), dst(edge_to_check_1))) && !(has_edge(G, src(edge_to_check_2), dst(edge_to_check_2))) - G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = [(undef, undef),(undef, undef)] - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, algebraic_connectivity_tracker,vertices_tracker) - G = rem_multiple_edges!(G, vertices_tracker) - elseif !(has_edge(G, src(edge_to_check_1), dst(edge_to_check_1))) && (has_edge( G, src(edge_to_check_2), dst(edge_to_check_2))) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1) - elseif !(has_edge(G, src(edge_to_check_2), dst(edge_to_check_2))) && (has_edge( G, src(edge_to_check_1), dst(edge_to_check_1))) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2) - end - return G -end - -function refinement_tree_3opt!( - G, - adjacency_base_graph, - adjacency_augment_graph, - edge_to_check_1, - edge_to_check_2, - edge_to_check_3, - ) - con1 = !(has_edge(G, src(edge_to_check_1), dst(edge_to_check_1))) - con2 = !(has_edge(G, src(edge_to_check_2), dst(edge_to_check_2))) - con3 = !(has_edge(G, src(edge_to_check_3), dst(edge_to_check_3))) - if (con1 && con2 && con3) - G = add_multiple_edges!(G, [(src(edge_to_check_1), dst(edge_to_check_1)),(src(edge_to_check_2), dst(edge_to_check_2)), (src(edge_to_check_3), dst(edge_to_check_3))]) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for i in eachindex(length(collect(edges(G))) - 2) - if (adjacency_base_graph[src(edge_list[i]), dst(edge_list[i])] == 0) - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, i, algebraic_connectivity_tracker,vertices_tracker) - end - end - G = rem_multiple_edges!(G, vertices_tracker) - elseif (con1 && con2 && !(con3)) - G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1, edge_to_check_2) - elseif (con1 && !(con2) && con3) - G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1, edge_to_check_3) - elseif (!(con1) && con2 && con3) - G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2, edge_to_check_3) - elseif (con1 && !(con2) && !(con3)) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1) - elseif (!(con1) && con2 && !(con3)) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2) - elseif (!(con1) && !(con2) && con3) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_3) - end - return G + return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) end function vertices_tracker_update_two_edges!( G, adjacency_base_graph, adjacency_augment_graph, - i, # Index for third edge if kopt_parameter is 3 + j, # Index for third edge if kopt_parameter is 3 algebraic_connectivity_tracker, vertices_tracker ) edge_list = collect(edges(G)) - for j in i+1:(length(edge_list)-1) - for k in j+1:length(edge_list) + for k in j+1:(length(edge_list)-1) + for l in k+1:length(edge_list) edges_to_check = [] - if i == 0 - edges_to_check = [(src(edge_list[j]), dst(edge_list[j])), (src(edge_list[k]), dst(edge_list[k]))] + if j == 0 + edges_to_check = [(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] else - edges_to_check = [(src(edge_list[i]), dst(edge_list[i])),(src(edge_list[j]), dst(edge_list[j])), (src(edge_list[k]), dst(edge_list[k]))] + edges_to_check = [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] end - if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) && (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) + if (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) G = rem_multiple_edges!(G, edges_to_check) if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) @@ -496,5 +555,7 @@ function vertices_tracker_update_two_edges!( end end end + return algebraic_connectivity_tracker, vertices_tracker end + diff --git a/src/lopt_model.jl b/src/lopt_model.jl index dd53985..b69beeb 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -14,7 +14,10 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt._logging_info(lom) if lom.options.formulation_type == "max_λ2" - if lom.options.solution_type == "optimal" + if lom.options.solution_type == "heuristic" + return lom + + elseif lom.options.solution_type == "optimal" # Populate PMinor indices lom.minor_idx_dict = LOpt._PMinorIdx(lom.data["num_nodes"], lom.options.eigen_cuts_sizes) @@ -22,12 +25,10 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.variable_LOModel(lom) LOpt.constraint_LOModel(lom; optimizer = optimizer) LOpt.objective_LOModel(lom) - - elseif lom.options.solution_type == "heuristic" - LOpt.heuristic_kopt(lom.data, lom.options.kopt_parameter, lom.options.num_central_nodes_verifier, lom.options.num_kopt_swaps_upperbound) + end elseif lom.options.formulation_type == "max_span_tree" - if lom.options.solution_type in ["optimal", "heuristic"] + if lom.options.solution_type in ["optimal"] LOpt.variable_MaxSpanTree_model(lom) LOpt.constraint_MaxSpanTree_model(lom) LOpt.objective_MaxSpanTree_model(lom) @@ -123,7 +124,11 @@ function run_LOpt( ) data = LOpt.get_data(params) model_lopt = LOpt.build_LOModel(data, optimizer = lom_optimizer, options = options) - result_lopt = LOpt.optimize_LOModel!(model_lopt, optimizer = lom_optimizer) + if (:solution_type in keys(options)) && (options[:solution_type] == "heuristic") + result_lopt = LOpt.heuristic_kopt(model_lopt) + else + result_lopt = LOpt.optimize_LOModel!(model_lopt, optimizer = lom_optimizer) + end if visualize_solution LOpt.visualize_solution( diff --git a/src/solution.jl b/src/solution.jl index 54ebdd2..676b16e 100644 --- a/src/solution.jl +++ b/src/solution.jl @@ -102,3 +102,16 @@ function optimality_certificate_MISDP(lom::LaplacianOptModel, result::Dict{Strin end end end + +function build_LOModel_heuristic_result(lom::LaplacianOptModel, heuristic_time::Number, adjacency_star, ac_star) + + result = Dict{String,Any}( + "heuristic_objective" => ac_star, + "heuristic_solve_time" => heuristic_time, + "heuristic_solution" => adjacency_star, + "adjacency_base_graph" => lom.data["adjacency_base_graph"], + "adjacency_augment_graph" => lom.data["adjacency_augment_graph"], + ) + + return result +end \ No newline at end of file diff --git a/src/types.jl b/src/types.jl index ab554d3..371e2aa 100644 --- a/src/types.jl +++ b/src/types.jl @@ -22,8 +22,8 @@ mutable struct LaplacianOptModelOptions sdp_relaxation::Bool kopt_parameter::Int64 - num_central_nodes_verifier::Int64 - num_kopt_swaps_upperbound::Int64 + num_central_nodes_kopt::Int64 + num_swaps_bound_kopt::Int64 best_lower_bound::Float64 best_incumbent::Union{Vector{Tuple{Int64,Int64}},Nothing} @@ -54,8 +54,8 @@ function get_default_options() sdp_relaxation = false # true, false kopt_parameter = 2 # Integer value >= 1 - num_central_nodes_verifier = 5 # Integer value >= 1 - num_kopt_swaps_upperbound = 1E6 # Integer value + num_central_nodes_kopt = 5 # Integer value >= 1 + num_swaps_bound_kopt = 1E6 # Integer value best_lower_bound = 0 # Best known feasible solution's objective best_incumbent = nothing @@ -78,8 +78,8 @@ function get_default_options() cheeger_cuts_factor, sdp_relaxation, kopt_parameter, - num_central_nodes_verifier, - num_kopt_swaps_upperbound, + num_central_nodes_kopt, + num_swaps_bound_kopt, best_lower_bound, best_incumbent, tol_zero, diff --git a/src/utility.jl b/src/utility.jl index c39f0c9..dc03843 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -366,15 +366,15 @@ function weighted_adjacency_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, #collecting vertices connected in graph vertices_from_edges = Int[] - for edge in edges(G) - push!(vertices_from_edges, src(edge)) - push!(vertices_from_edges, dst(edge)) + for edge in Graphs.edges(G) + push!(vertices_from_edges, Graphs.src(edge)) + push!(vertices_from_edges, Graphs.dst(edge)) end vertices_from_edges = unique(vertices_from_edges) for i in 1:size for j in i:size - weighted_adj_matrix_size[i, j] = (adjacency_augment_graph.*adjacency_matrix(G))[vertices_from_edges[i],vertices_from_edges[j]] + weighted_adj_matrix_size[i, j] = (adjacency_augment_graph.*Graphs.adjacency_matrix(G))[vertices_from_edges[i],vertices_from_edges[j]] weighted_adj_matrix_size[j, i] = weighted_adj_matrix_size[i, j] end end @@ -394,8 +394,8 @@ function update_kopt_adjacency!( adjacency_graph_ac_tuple::Tuple{Array,Float64}; tol = 1E-6, ) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) - adjacency_graph_ac_tuple[2] >= tol - return Matrix(adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(adjacency_matrix(G))) + if algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) - adjacency_graph_ac_tuple[2] >= tol + return Matrix(Graphs.adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) else return adjacency_graph_ac_tuple end @@ -404,7 +404,7 @@ end """ edge_combinations(n::Int64, k::Int64) -Returns all combinations possible for given n and k. +Returns all combinations possible for given `n` and `k`. """ function edge_combinations(num_edges::Int64, kopt_parameter::Int64) @@ -445,24 +445,24 @@ function add_multiple_edges!( edge_set::Vector{Tuple{Int64,Int64}}, ) for i in 1:length(edge_set) - add_edge!(G, edge_set[i][1], edge_set[i][2]) + Graphs.add_edge!(G, edge_set[i][1], edge_set[i][2]) end return G end """ - rem_multiple_edges!(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},edge_set::Vector{Tuple{Int64,Int64}},) + remove_multiple_edges!(G::Graphs.SimpleGraphs.SimpleGraph{<:Number},edge_set::Vector{Tuple{Int64,Int64}},) Returns updated graph by removing edges. """ -function rem_multiple_edges!( +function remove_multiple_edges!( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, edge_set::Vector{Tuple{Int64,Int64}}, ) for i in 1:length(edge_set) - rem_edge!(G, edge_set[i][1], edge_set[i][2]) + Graphs.rem_edge!(G, edge_set[i][1], edge_set[i][2]) end return G end \ No newline at end of file diff --git a/test/heuristics_tests.jl b/test/heuristics_tests.jl index 24b3161..5bca799 100644 --- a/test/heuristics_tests.jl +++ b/test/heuristics_tests.jl @@ -9,7 +9,7 @@ model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 1, - :num_central_nodes_verifier => 5, + :num_central_nodes_kopt => 5, :time_limit => test_time_limit() ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -43,7 +43,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 2, - :num_central_nodes_verifier => 5, + :num_central_nodes_kopt => 5, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -78,7 +78,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 3, - :num_central_nodes_verifier => 5, + :num_central_nodes_kopt => 5, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -106,7 +106,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 1, - :num_central_nodes_verifier => 3, + :num_central_nodes_kopt => 3, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -142,7 +142,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 2, - :num_central_nodes_verifier => 3, + :num_central_nodes_kopt => 3, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -178,7 +178,7 @@ end model_options = Dict{Symbol,Any}( :solution_type => "heuristic", :kopt_parameter => 3, - :num_central_nodes_verifier => 3, + :num_central_nodes_kopt => 3, :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) From c4d3b0ff58932f09b5ce2b770e1715fd9c8093fe Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Wed, 16 Aug 2023 15:58:05 -0500 Subject: [PATCH 20/35] staging --- examples/run_examples.jl | 9 +- src/heuristics.jl | 188 +++++++++------------------------------ src/lopt_model.jl | 13 ++- src/solution.jl | 2 +- src/types.jl | 6 +- src/utility.jl | 36 +++++--- 6 files changed, 80 insertions(+), 174 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index 48d6206..f8bfa97 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -34,7 +34,7 @@ end =# function data_II() data_dict = Dict{String,Any}() - data_dict["num_nodes"] = 5 + data_dict["num_nodes"] = 4 data_dict["adjacency_base_graph"] = [0 2 0 0; 2 0 3 0; 0 3 0 4; 0 0 4 0] data_dict["adjacency_augment_graph"] = [0 0 4 8; 0 0 0 7; 4 0 0 0; 8 7 0 0] augment_budget = 2 @@ -45,8 +45,9 @@ end # User-defined params # #-------------------------------# num_nodes = 5 -instance = 11 -data_dict, augment_budget = data_I(num_nodes, instance) +instance = 1 +# data_dict, augment_budget = data_I(num_nodes, instance) +data_dict, augment_budget = data_II() params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -70,4 +71,4 @@ result = LOpt.run_LOpt( options = model_options, visualize_solution = false, # Make it true to plot the graph solution visualizing_tool = "tikz", # "graphviz" is another option -) +) \ No newline at end of file diff --git a/src/heuristics.jl b/src/heuristics.jl index 4cdf22b..3af986e 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -38,33 +38,33 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) # Sorting the list of tuples in descending order of edge weights edge_wt_sorted = sort(edge_wt_list, by = x -> x[2], rev = true) - # Priority central nodes based on sum of edge weights - priority_central_nodes_list = LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) - - # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity - adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_kopt) - - #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) - #Threads.@threads for index in 1:num_central_nodes_kopt #uncomment this line for multi threading - for index in 1:num_central_nodes_kopt #comment this line for multi threading - # Builds a spanning tree - adjacency_graph_list[index] = LOpt.build_span_tree( - num_nodes, - adjacency_augment_graph, - edge_wt_sorted, - priority_central_nodes_list[index], - ) - - adjacency_graph_list[index] = LOpt.refinement_span_tree( - adjacency_augment_graph, - edge_wt_sorted, - adjacency_graph_list[index], - lom.options.kopt_parameter, - lom.options.num_swaps_bound_kopt, - ) - end - - adjacency_star, ac_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] + # Priority central nodes based on sum of edge weights + priority_central_nodes_list = LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) + + # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity + adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_kopt) + + #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) + #Threads.@threads for index in 1:num_central_nodes_kopt #uncomment this line for multi threading + for index in 1:num_central_nodes_kopt #comment this line for multi threading + # Builds a spanning tree + adjacency_graph_list[index] = LOpt.build_span_tree( + num_nodes, + adjacency_augment_graph, + edge_wt_sorted, + priority_central_nodes_list[index], + ) + + adjacency_graph_list[index] = LOpt.refinement_span_tree( + adjacency_augment_graph, + edge_wt_sorted, + adjacency_graph_list[index], + lom.options.kopt_parameter, + lom.options.num_swaps_bound_kopt, + ) + end + + adjacency_star, ac_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] return adjacency_star, ac_star end @@ -92,19 +92,19 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) # Base graph G = Graphs.SimpleGraph(adjacency_base_graph) - # Adding edges based on fiedler weights to construct initial graph with required edges - for i = 1:augment_budget - add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) - end + # Adding edges based on fiedler weights to construct initial graph with required edges + for i = 1:lom.data["augment_budget"] + Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) + end - adjacency_graph_star, algebraic_connectivity_star = refinement_tree( - G, - adjacency_base_graph, - adjacency_augment_graph, - sorted_edge_fiedlerweight_list, - kopt_parameter, - num_kopt_swaps_upperbound, - ) + adjacency_star, ac_star = LOpt.refinement_tree( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edges_fiedler_wt, + lom.options.kopt_parameter, + lom.options.num_swaps_bound_kopt, + ) return adjacency_star, ac_star end @@ -451,111 +451,5 @@ function _vertices_tracker_two_edges!( end end end - return algebraic_connectivity_tracker, vertices_tracker -end - -function refinement_tree( - G, - adjacency_base_graph, - adjacency_augment_graph, - sorted_edge_fiedlerweight_list, - kopt_parameter, - num_kopt_swaps_upperbound, - ) - - if kopt_parameter == 1 - for i in eachindex(sorted_edge_fiedlerweight_list) - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1]))) - add_edge!(G, src(sorted_edge_fiedlerweight_list[i][1]), dst(sorted_edge_fiedlerweight_list[i][1])) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = (undef, undef) - for edge in edges(G) - if adjacency_base_graph[src(edge), dst(edge)] == 0 - rem_edge!(G, src(edge), dst(edge)) - if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) - vertices_tracker = edge - end - add_edge!(G, src(edge), dst(edge)) - end - end - rem_edge!(G, vertices_tracker) - end - end - - elseif kopt_parameter == 2 - combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) - if length(combinations) <= num_kopt_swaps_upperbound - num_swaps = length(combinations) - else - num_swaps = num_kopt_swaps_upperbound - end - for i in 1:num_swaps - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) - G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))]) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = [(undef, undef),(undef, undef)] - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, algebraic_connectivity_tracker,vertices_tracker) - G = rem_multiple_edges!(G, vertices_tracker) - end - end - - elseif kopt_parameter == 3 - combinations = edge_combinations(length(sorted_edge_fiedlerweight_list), kopt_parameter) - if length(combinations) <= num_kopt_swaps_upperbound - num_swaps = length(combinations) - else - num_swaps = num_kopt_swaps_upperbound - end - for i in 1:num_swaps - if !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1]))) && - !(has_edge(G, src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))) - G = add_multiple_edges!(G, [(src(sorted_edge_fiedlerweight_list[combinations[i][1]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][1]][1])),(src(sorted_edge_fiedlerweight_list[combinations[i][2]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][2]][1])), (src(sorted_edge_fiedlerweight_list[combinations[i][3]][1]), dst(sorted_edge_fiedlerweight_list[combinations[i][3]][1]))]) - algebraic_connectivity_tracker = 0.0 - vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in eachindex(length(collect(edges(G))) - 2) - if (adjacency_base_graph[src(edge_list[j]), dst(edge_list[j])] == 0) - algebraic_connectivity_tracker, vertices_tracker = vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, algebraic_connectivity_tracker,vertices_tracker) - end - end - G = rem_multiple_edges!(G, vertices_tracker) - end - end - end - - return Matrix(adjacency_matrix(G)), algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) -end - -function vertices_tracker_update_two_edges!( - G, - adjacency_base_graph, - adjacency_augment_graph, - j, # Index for third edge if kopt_parameter is 3 - algebraic_connectivity_tracker, - vertices_tracker - ) - edge_list = collect(edges(G)) - for k in j+1:(length(edge_list)-1) - for l in k+1:length(edge_list) - edges_to_check = [] - if j == 0 - edges_to_check = [(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] - else - edges_to_check = [(src(edge_list[j]), dst(edge_list[j])),(src(edge_list[k]), dst(edge_list[k])), (src(edge_list[l]), dst(edge_list[l]))] - end - if (adjacency_base_graph[src(edge_list[k]), dst(edge_list[k])] == 0) && (adjacency_base_graph[src(edge_list[l]), dst(edge_list[l])] == 0) - G = rem_multiple_edges!(G, edges_to_check) - if algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) > algebraic_connectivity_tracker - algebraic_connectivity_tracker = algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(adjacency_matrix(G))) - vertices_tracker = edges_to_check - end - G = add_multiple_edges!(G, edges_to_check) - end - end - end - - return algebraic_connectivity_tracker, vertices_tracker -end - + return ac_tracker, vertices_tracker +end \ No newline at end of file diff --git a/src/lopt_model.jl b/src/lopt_model.jl index b69beeb..550f1a0 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -25,7 +25,6 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.variable_LOModel(lom) LOpt.constraint_LOModel(lom; optimizer = optimizer) LOpt.objective_LOModel(lom) - end elseif lom.options.formulation_type == "max_span_tree" if lom.options.solution_type in ["optimal"] @@ -179,11 +178,11 @@ function lazycallback_status(lom::LaplacianOptModel) if ( size(lom.options.eigen_cuts_sizes)[1] > 0 && minimum(lom.options.eigen_cuts_sizes) >= 2 - ) || - lom.options.topology_flow_cuts || - lom.options.soc_linearized_cuts || - lom.options.cheeger_cuts || - lom.options.sdp_relaxation + ) || + lom.options.topology_flow_cuts || + lom.options.soc_linearized_cuts || + lom.options.cheeger_cuts || + lom.options.sdp_relaxation return true else return false @@ -219,4 +218,4 @@ function _logging_info(lom::LaplacianOptModel) elseif lom.options.solution_type == "heuristic" Memento.info(_LOGGER, "Applying heuristics to obtain a lower bound") end -end +end \ No newline at end of file diff --git a/src/solution.jl b/src/solution.jl index 676b16e..8f7e214 100644 --- a/src/solution.jl +++ b/src/solution.jl @@ -88,7 +88,7 @@ a boolean if the integer solution satisfies the optimality certificate for the M """ function optimality_certificate_MISDP(lom::LaplacianOptModel, result::Dict{String,Any}) if ("z_var" in keys(result["solution"])) && - !(lom.options.formulation_type == "max_span_tree") + !(lom.options.formulation_type == "max_span_tree") adjacency_full_graph = lom.data["adjacency_augment_graph"] (lom.data["num_edges_existing"] > 0) && (adjacency_full_graph += lom.data["adjacency_base_graph"]) diff --git a/src/types.jl b/src/types.jl index 371e2aa..84cd318 100644 --- a/src/types.jl +++ b/src/types.jl @@ -53,8 +53,8 @@ function get_default_options() sdp_relaxation = false # true, false - kopt_parameter = 2 # Integer value >= 1 - num_central_nodes_kopt = 5 # Integer value >= 1 + kopt_parameter = 2 # 1 <= Integer value <= 3 + num_central_nodes_kopt = 5 # 1 <= Integer value <= size of instance num_swaps_bound_kopt = 1E6 # Integer value best_lower_bound = 0 # Best known feasible solution's objective best_incumbent = nothing @@ -140,4 +140,4 @@ mutable struct GraphData return graph_data end -end +end \ No newline at end of file diff --git a/src/utility.jl b/src/utility.jl index dc03843..dbac69d 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -123,7 +123,7 @@ function algebraic_connectivity(adjacency_matrix::Array{<:Number}) L_mat = LOpt.laplacian_matrix(adjacency_matrix) ac = sort(LA.eigvals(L_mat))[2] - @show ac + return ac end @@ -333,7 +333,6 @@ function _PMinorIdx(N::Int64, sizes::Vector{Int64}) return minor_idx_dict end - """ priority_central_nodes(adjacency_augment_graph::Array{<:Number}, num_nodes::Int64) @@ -341,7 +340,10 @@ Returns a vector of order of central nodes as an input to construct the graph. """ -function priority_central_nodes(adjacency_augment_graph::Array{<:Number}, num_nodes::Int64) +function priority_central_nodes( + adjacency_augment_graph::Array{<:Number}, + num_nodes::Int64, +) edge_weights_sum_list = Vector{Float64}(undef, num_nodes) central_nodes_list = Vector{Int64}(undef, num_nodes) @@ -352,7 +354,6 @@ function priority_central_nodes(adjacency_augment_graph::Array{<:Number}, num_no return central_nodes_list end - """ weighted_adjacency_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, size::Int64) @@ -360,8 +361,11 @@ Returns a weighted adjacency matrix for the connected part of graph. """ - -function weighted_adjacency_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, size::Int64) +function weighted_adjacency_matrix( + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + adjacency_augment_graph::Array{<:Number}, + size::Int64, +) weighted_adj_matrix_size = Matrix{Float64}(undef, size, size) #collecting vertices connected in graph @@ -374,7 +378,11 @@ function weighted_adjacency_matrix(G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, for i in 1:size for j in i:size - weighted_adj_matrix_size[i, j] = (adjacency_augment_graph.*Graphs.adjacency_matrix(G))[vertices_from_edges[i],vertices_from_edges[j]] + weighted_adj_matrix_size[i, j] = + (adjacency_augment_graph.*Graphs.adjacency_matrix(G))[ + vertices_from_edges[i], + vertices_from_edges[j], + ] weighted_adj_matrix_size[j, i] = weighted_adj_matrix_size[i, j] end end @@ -394,8 +402,13 @@ function update_kopt_adjacency!( adjacency_graph_ac_tuple::Tuple{Array,Float64}; tol = 1E-6, ) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) - adjacency_graph_ac_tuple[2] >= tol - return Matrix(Graphs.adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) + if algebraic_connectivity( + adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G)), + ) - adjacency_graph_ac_tuple[2] >= tol + return Matrix(Graphs.adjacency_matrix(G)), + algebraic_connectivity( + adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G)), + ) else return adjacency_graph_ac_tuple end @@ -419,14 +432,14 @@ function edge_combinations(num_edges::Int64, kopt_parameter::Int64) if kopt_parameter == 2 for i in 1:num_edges-1 for j in i+1:num_edges - push!(combinations, (i,j)) + push!(combinations, (i, j)) end end elseif kopt_parameter == 3 for i in 1:num_edges-2 for j in i+1:num_edges-1 for l in j+1:num_edges - push!(combinations, (i,j,l)) + push!(combinations, (i, j, l)) end end end @@ -456,7 +469,6 @@ end Returns updated graph by removing edges. """ - function remove_multiple_edges!( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, edge_set::Vector{Tuple{Int64,Int64}}, From 5603ed33416009b7f245d294a5f815dd81e7123d Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Wed, 16 Aug 2023 16:48:20 -0500 Subject: [PATCH 21/35] corrected the heuristics --- examples/run_examples.jl | 10 +-- src/heuristics.jl | 189 ++++++++++++++++++++++++++------------- src/utility.jl | 1 - 3 files changed, 131 insertions(+), 69 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index f8bfa97..a2507dc 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -44,9 +44,9 @@ end #-------------------------------# # User-defined params # #-------------------------------# -num_nodes = 5 -instance = 1 -# data_dict, augment_budget = data_I(num_nodes, instance) +num_nodes = 10 +instance = 4 +#data_dict, augment_budget = data_I(num_nodes, instance) data_dict, augment_budget = data_II() params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -58,10 +58,10 @@ params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_ # For more model options, check https://github.com/harshangrjn/LaplacianOpt.jl/blob/master/src/types.jl model_options = Dict{Symbol,Any}( - :eigen_cuts_sizes => [num_nodes], + :eigen_cuts_sizes => [num_nodes,2,3,4], :topology_flow_cuts => true, :solution_type => "heuristic", - :kopt_parameter => 3, + :kopt_parameter => 1, :num_central_nodes_kopt => 5, ) diff --git a/src/heuristics.jl b/src/heuristics.jl index 3af986e..78ffd65 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -97,15 +97,19 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) end - adjacency_star, ac_star = LOpt.refinement_tree( - G, - adjacency_base_graph, - adjacency_augment_graph, - sorted_edges_fiedler_wt, - lom.options.kopt_parameter, - lom.options.num_swaps_bound_kopt, - ) - + if lom.data["augment_budget"] > 0 + adjacency_star, ac_star = LOpt.refinement_tree( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edges_fiedler_wt, + lom.options.kopt_parameter, + lom.options.num_swaps_bound_kopt, + ) + else + adjacency_star, ac_star = Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) + end + return adjacency_star, ac_star end @@ -267,7 +271,7 @@ function refinement_span_tree( cycle_basis_current = Graphs.cycle_basis(G) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef)] - ac_tracker, vertices_tracker = LOpt._vertices_tracker_two_edges!(G, adjacency_augment_graph, 0, 0, ac_tracker, vertices_tracker) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_span_two_edges!(G, adjacency_augment_graph, 0, 0, ac_tracker, vertices_tracker) G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end @@ -293,7 +297,7 @@ function refinement_span_tree( for j in 1:(length(Graphs.cycle_basis(G)[3])-1) for k in (j+1):length(Graphs.cycle_basis(G)[3]) if Graphs.adjacency_matrix(G)[Graphs.cycle_basis(G)[3][j], Graphs.cycle_basis(G)[3][k]] == 1 - ac_tracker, vertices_tracker = LOpt._vertices_tracker_two_edges!(G, adjacency_augment_graph, j, k, ac_tracker, vertices_tracker) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_span_two_edges!(G, adjacency_augment_graph, j, k, ac_tracker, vertices_tracker) end end end @@ -316,70 +320,46 @@ function refinement_tree( ) if kopt_parameter == 1 - for i in eachindex(sorted_edges_fiedler_wt) - if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1]))) - Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) - ac_tracker = 0.0 - vertices_tracker = (undef, undef) - for edge in Graphs.edges(G) - if adjacency_base_graph[Graphs.src(edge), Graphs.dst(edge)] == 0 - Graphs.rem_edge!(G, Graphs.src(edge), Graphs.dst(edge)) - if LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker - ac_tracker = LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) - vertices_tracker = edge - end - Graphs.add_edge!(G, Graphs.src(edge), Graphs.dst(edge)) - end - end - Graphs.rem_edge!(G, vertices_tracker) - end - end - - elseif kopt_parameter == 2 - combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) - if length(combinations) <= num_swaps_bound_kopt - num_swaps = length(combinations) + if length(sorted_edges_fiedler_wt) <= num_swaps_bound_kopt + num_swaps = length(sorted_edges_fiedler_wt) else num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1]))) && - !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))) - G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))]) - ac_tracker = 0.0 - vertices_tracker = [(undef, undef),(undef, undef)] - ac_tracker, vertices_tracker = _vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, ac_tracker,vertices_tracker) - G = LOpt.remove_multiple_edges!(G, vertices_tracker) - end + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edges_fiedler_wt[i][1]) end - elseif kopt_parameter == 3 - combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) - if length(combinations) <= num_swaps_bound_kopt - num_swaps = length(combinations) + elseif kopt_parameter == 2 + if lom.data["augment_budget"] >= 2 + combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) + if length(combinations) <= num_swaps_bound_kopt + num_swaps = length(combinations) + else + num_swaps = num_swaps_bound_kopt + end + for i in 1:num_swaps + G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edges_fiedler_wt[combinations[i][1]][1], sorted_edges_fiedler_wt[combinations[i][2]][1]) + end else - num_swaps = num_swaps_bound_kopt + Memento.eror(_LOGGER, "Augment budget should be greater than one.") end - for i in 1:num_swaps - if !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1]))) && - !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1]))) && - !(Graphs.has_edge(G, Graphs.src(sorted_edges_fiedler_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][3]][1]))) - G = LOpt.add_multiple_edges!(G, [(Graphs.src(sorted_edges_fiedler_wt[combinations[i][1]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][1]][1])),(Graphs.src(sorted_edges_fiedler_wt[combinations[i][2]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][2]][1])), (Graphs.src(sorted_edges_fiedler_wt[combinations[i][3]][1]), Graphs.dst(sorted_edges_fiedler_wt[combinations[i][3]][1]))]) - ac_tracker = 0.0 - vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in eachindex(length(collect(Graphs.edges(G))) - 2) - if (adjacency_base_graph[Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])] == 0) - ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, ac_tracker,vertices_tracker) - end - end - G = LOpt.remove_multiple_edges!(G, vertices_tracker) + elseif kopt_parameter == 3 + if lom.data["augment_budget"] >= 3 + combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) + if length(combinations) <= num_swaps_bound_kopt + num_swaps = length(combinations) + else + num_swaps = num_swaps_bound_kopt + end + for i in 1:num_swaps + G = refinement_tree_3opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edges_fiedler_wt[combinations[i][1]][1], sorted_edges_fiedler_wt[combinations[i][2]][1],sorted_edges_fiedler_wt[combinations[i][3]][1]) end + else + Memento.eror(_LOGGER, "Augment budget should be greater than two.") end - else Memento.eror(_LOGGER, "kopt_parameter > 3 is currently not supported.") end - return Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) end @@ -414,7 +394,7 @@ function _vertices_tracker_update_two_edges!( return ac_tracker, vertices_tracker end -function _vertices_tracker_two_edges!( +function _vertices_tracker_update_span_two_edges!( G, adjacency_augment_graph, j, # Index for third edge src if kopt_parameter is 3 @@ -452,4 +432,87 @@ function _vertices_tracker_two_edges!( end end return ac_tracker, vertices_tracker +end + +function refinement_tree_1opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check + ) + if !(Graphs.has_edge(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check))) + Graphs.add_edge!(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check)) + ac_tracker = 0.0 + vertices_tracker = (undef, undef) + for edge in Graphs.edges(G) + if adjacency_base_graph[Graphs.src(edge), Graphs.dst(edge)] == 0 + Graphs.rem_edge!(G, Graphs.src(edge), Graphs.dst(edge)) + if LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) + vertices_tracker = edge + end + Graphs.add_edge!(G, Graphs.src(edge), Graphs.dst(edge)) + end + end + Graphs.rem_edge!(G, vertices_tracker) + end + return G +end + +function refinement_tree_2opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_1, + edge_to_check_2 + ) + if !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) && !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) + G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1)),(Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))]) + ac_tracker = 0.0 + vertices_tracker = [(undef, undef),(undef, undef)] + ac_tracker, vertices_tracker = _vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, ac_tracker,vertices_tracker) + G = LOpt.remove_multiple_edges!(G, vertices_tracker) + elseif !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) && (Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1) + elseif !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) && (Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2) + end + return G +end + +function refinement_tree_3opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_1, + edge_to_check_2, + edge_to_check_3, + ) + con1 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) + con2 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) + con3 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_3), Graphs.dst(edge_to_check_3))) + if con1 && con2 && con3 + G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1)),(Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2)),(Graphs.src(edge_to_check_3), Graphs.dst(edge_to_check_3))]) + ac_tracker = 0.0 + vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] + for j in eachindex(length(collect(Graphs.edges(G))) - 2) + if (adjacency_base_graph[Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])] == 0) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, ac_tracker,vertices_tracker) + end + end + G = LOpt.remove_multiple_edges!(G, vertices_tracker) + elseif con1 && con2 && !(con3) + G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1, edge_to_check_2) + elseif con1 && !(con2) && con3 + G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1, edge_to_check_3) + elseif !(con1) && con2 && con3 + G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2, edge_to_check_3) + elseif con1 && !(con2) && !(con3) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1) + elseif !(con1) && con2 && !(con3) + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2) + elseif !(con1) && !(con2) && con3 + G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_3) + end + return G end \ No newline at end of file diff --git a/src/utility.jl b/src/utility.jl index dbac69d..31401a9 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -123,7 +123,6 @@ function algebraic_connectivity(adjacency_matrix::Array{<:Number}) L_mat = LOpt.laplacian_matrix(adjacency_matrix) ac = sort(LA.eigvals(L_mat))[2] - return ac end From 5819e04665a0523a83ae90bf858a4df998f18ca7 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Wed, 16 Aug 2023 17:14:32 -0500 Subject: [PATCH 22/35] testing --- examples/run_examples.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index f3f898b..a2b8f04 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -45,8 +45,9 @@ end # User-defined params # #-------------------------------# num_nodes = 5 -instance = 11 -data_dict, augment_budget = data_I(num_nodes, instance) +instance = 1 +#data_dict, augment_budget = data_I(num_nodes, instance) +data_dict, augment_budget = data_II() params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) From 05edac8ef85bdfad631c3f0369014da777e6b1bb Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Wed, 16 Aug 2023 17:20:48 -0500 Subject: [PATCH 23/35] trying to resolve --- src/heuristics.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index 78ffd65..51ea2d0 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,6 +1,7 @@ # Heuristics to maximize algebraic connectivity of weighted graphs function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) + @show lom.data["augment_budget"] adjacency_star = [] ac_star = [] @@ -70,7 +71,7 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) end function heuristic_base_graph_connected(lom::LaplacianOptModel) - + @show lom.data["augment_budget"] adjacency_augment_graph = lom.data["adjacency_augment_graph"] adjacency_base_graph = lom.data["adjacency_base_graph"] @@ -98,6 +99,7 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) end if lom.data["augment_budget"] > 0 + @show lom.data["augment_budget"] adjacency_star, ac_star = LOpt.refinement_tree( G, adjacency_base_graph, @@ -318,7 +320,7 @@ function refinement_tree( kopt_parameter, num_swaps_bound_kopt, ) - + if kopt_parameter == 1 if length(sorted_edges_fiedler_wt) <= num_swaps_bound_kopt num_swaps = length(sorted_edges_fiedler_wt) From 7dca2441de7a53be411f04cf0a8444cd5eb8db9c Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Wed, 16 Aug 2023 17:31:27 -0500 Subject: [PATCH 24/35] Corrected --- examples/run_examples.jl | 2 +- src/heuristics.jl | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/run_examples.jl b/examples/run_examples.jl index a2b8f04..c0cf915 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -61,7 +61,7 @@ model_options = Dict{Symbol,Any}( :eigen_cuts_sizes => [num_nodes,2,3,4], :topology_flow_cuts => true, :solution_type => "heuristic", - :kopt_parameter => 3, + :kopt_parameter => 2, :num_central_nodes_kopt => 5, ) diff --git a/src/heuristics.jl b/src/heuristics.jl index 51ea2d0..aa1d2b7 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,7 +1,6 @@ # Heuristics to maximize algebraic connectivity of weighted graphs function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) - @show lom.data["augment_budget"] adjacency_star = [] ac_star = [] @@ -71,7 +70,7 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) end function heuristic_base_graph_connected(lom::LaplacianOptModel) - @show lom.data["augment_budget"] + adjacency_augment_graph = lom.data["adjacency_augment_graph"] adjacency_base_graph = lom.data["adjacency_base_graph"] @@ -99,7 +98,6 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) end if lom.data["augment_budget"] > 0 - @show lom.data["augment_budget"] adjacency_star, ac_star = LOpt.refinement_tree( G, adjacency_base_graph, @@ -107,6 +105,7 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) sorted_edges_fiedler_wt, lom.options.kopt_parameter, lom.options.num_swaps_bound_kopt, + lom.data["augment_budget"] ) else adjacency_star, ac_star = Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) @@ -319,8 +318,9 @@ function refinement_tree( sorted_edges_fiedler_wt, kopt_parameter, num_swaps_bound_kopt, + augment_budget ) - + if kopt_parameter == 1 if length(sorted_edges_fiedler_wt) <= num_swaps_bound_kopt num_swaps = length(sorted_edges_fiedler_wt) @@ -332,7 +332,7 @@ function refinement_tree( end elseif kopt_parameter == 2 - if lom.data["augment_budget"] >= 2 + if augment_budget >= 2 combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) @@ -346,7 +346,7 @@ function refinement_tree( Memento.eror(_LOGGER, "Augment budget should be greater than one.") end elseif kopt_parameter == 3 - if lom.data["augment_budget"] >= 3 + if augment_budget >= 3 combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) @@ -357,7 +357,7 @@ function refinement_tree( G = refinement_tree_3opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edges_fiedler_wt[combinations[i][1]][1], sorted_edges_fiedler_wt[combinations[i][2]][1],sorted_edges_fiedler_wt[combinations[i][3]][1]) end else - Memento.eror(_LOGGER, "Augment budget should be greater than two.") + Memento.error(_LOGGER, "Augment budget should be greater than two.") end else Memento.eror(_LOGGER, "kopt_parameter > 3 is currently not supported.") From 7f25028c09a3e2e6b53d8f9f646b8036299f3300 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Thu, 17 Aug 2023 22:19:04 -0600 Subject: [PATCH 25/35] bug fixes and tests update --- CHANGELOG.md | 6 + Project.toml | 2 +- examples/run_examples.jl | 26 +- src/heuristics.jl | 628 ++++++++++++++++++++++++++++++--------- src/log.jl | 33 +- src/lopt_model.jl | 26 +- src/solution.jl | 24 +- src/types.jl | 4 +- src/utility.jl | 17 +- test/heuristics_tests.jl | 189 ++++++------ 10 files changed, 657 insertions(+), 298 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 692bbdd..9365352 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ LaplacianOpt.jl Change Log ========================= +### v0.6.0 +- Added mutiple heuristics to handle both spanning trees and graphs with loops +- Refactored `log.jl` to handle solutions from heuristics +- Included more used options for heuristic in `model_options` +- Updated docs and unit tests to reflect above changes + ### v0.5.0 - Generalized eigen cuts to handle any size of non-negative minors (`2x2`-`NxN`) - Dropped support for `constraint_eigen_cuts_on_2minors` and `constraint_eigen_cuts_on_3minors` diff --git a/Project.toml b/Project.toml index 55218cb..3bc1730 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "LaplacianOpt" uuid = "bb20392f-64fb-4001-92e8-14b3aedd5a9e" authors = ["Harsha Nagarajan"] -version = "0.5.0" +version = "0.6.0" [deps] DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" diff --git a/examples/run_examples.jl b/examples/run_examples.jl index c0cf915..af7dce0 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -1,16 +1,15 @@ import LaplacianOpt as LOpt using JuMP using CPLEX -using Gurobi # using GLPK include("optimizer.jl") -#----------------------------------# -# MIP solver # -# (Cplex 22.1 performs the best) # -#----------------------------------# -lopt_optimizer = get_gurobi() +#-----------------------------------# +# MIP solver # +# (> Cplex 22.1 performs the best) # +#-----------------------------------# +lopt_optimizer = get_cplex() #-------------------------------------# # User-defined input graphs # @@ -44,10 +43,10 @@ end #-------------------------------# # User-defined params # #-------------------------------# -num_nodes = 5 +num_nodes = 8 instance = 1 -#data_dict, augment_budget = data_I(num_nodes, instance) -data_dict, augment_budget = data_II() +data_dict, augment_budget = data_I(num_nodes, instance) +# data_dict, augment_budget = data_II() params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) @@ -58,11 +57,12 @@ params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_ # For more model options, check https://github.com/harshangrjn/LaplacianOpt.jl/blob/master/src/types.jl model_options = Dict{Symbol,Any}( - :eigen_cuts_sizes => [num_nodes,2,3,4], + :eigen_cuts_sizes => [num_nodes, 2], :topology_flow_cuts => true, - :solution_type => "heuristic", + :solution_type => "optimal", :kopt_parameter => 2, :num_central_nodes_kopt => 5, + :num_swaps_bound_kopt => 1E4, ) result = LOpt.run_LOpt( @@ -70,5 +70,5 @@ result = LOpt.run_LOpt( lopt_optimizer; options = model_options, visualize_solution = false, # Make it true to plot the graph solution - visualizing_tool = "tikz", # "graphviz" is another option -) \ No newline at end of file + visualizing_tool = "tikz", # "tikz", "graphviz" are the options +) diff --git a/src/heuristics.jl b/src/heuristics.jl index aa1d2b7..cbb7971 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -4,22 +4,25 @@ function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) adjacency_star = [] ac_star = [] - if (lom.data["num_edges_existing"] == 0) && (lom.data["augment_budget"] == (lom.data["num_nodes"] - 1)) - _, heuristic_time, solve_bytes_alloc, sec_in_gc = @timed adjacency_star, ac_star = LOpt.heuristic_spanning_tree(lom) + if (lom.data["num_edges_existing"] == 0) && + (lom.data["augment_budget"] == (lom.data["num_nodes"] - 1)) + _, heuristic_time, solve_bytes_alloc, sec_in_gc = + @timed adjacency_star, ac_star = LOpt.heuristic_spanning_tree(lom) elseif (lom.data["num_edges_existing"] > 0) && (lom.data["is_base_graph_connected"]) - _, heuristic_time, solve_bytes_alloc, sec_in_gc = @timed adjacency_star, ac_star = LOpt.heuristic_base_graph_connected(lom) + _, heuristic_time, solve_bytes_alloc, sec_in_gc = + @timed adjacency_star, ac_star = LOpt.heuristic_base_graph_connected(lom) else Memento.error(_LOGGER, "Heuristic for this option is not yet supported.") end # Implement the bridge to results here - result_dict = LOpt.build_LOModel_heuristic_result(lom, heuristic_time, adjacency_star, ac_star) + result_dict = + LOpt.build_LOModel_heuristic_result(lom, heuristic_time, adjacency_star, ac_star) return merge(lom.result, result_dict) end function heuristic_spanning_tree(lom::LaplacianOptModel) - num_nodes = lom.data["num_nodes"] adjacency_augment_graph = lom.data["adjacency_augment_graph"] num_central_nodes_kopt = lom.options.num_central_nodes_kopt @@ -28,18 +31,26 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) # Making list of tuples of edges and edge weights - edge_wt_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - edge_wt_sorted = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + edge_wt_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}( + undef, + length(edge_augment_list), + ) + edge_wt_sorted = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}( + undef, + length(edge_augment_list), + ) for (index, edge) in enumerate(edge_augment_list) - edge_wt_list[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)]) + edge_wt_list[index] = + (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)]) end # Sorting the list of tuples in descending order of edge weights edge_wt_sorted = sort(edge_wt_list, by = x -> x[2], rev = true) # Priority central nodes based on sum of edge weights - priority_central_nodes_list = LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) + priority_central_nodes_list = + LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_kopt) @@ -49,10 +60,10 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) for index in 1:num_central_nodes_kopt #comment this line for multi threading # Builds a spanning tree adjacency_graph_list[index] = LOpt.build_span_tree( - num_nodes, - adjacency_augment_graph, - edge_wt_sorted, - priority_central_nodes_list[index], + num_nodes, + adjacency_augment_graph, + edge_wt_sorted, + priority_central_nodes_list[index], ) adjacency_graph_list[index] = LOpt.refinement_span_tree( @@ -64,13 +75,13 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) ) end - adjacency_star, ac_star = adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] + adjacency_star, ac_star = + adjacency_graph_list[sortperm(adjacency_graph_list, by = x -> x[2], rev = true)[1]] return adjacency_star, ac_star end function heuristic_base_graph_connected(lom::LaplacianOptModel) - adjacency_augment_graph = lom.data["adjacency_augment_graph"] adjacency_base_graph = lom.data["adjacency_base_graph"] @@ -79,11 +90,24 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) - edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) - sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,length(edge_augment_list)) + edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}( + undef, + length(edge_augment_list), + ) + sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}( + undef, + length(edge_augment_list), + ) for (index, edge) in enumerate(edge_augment_list) - edges_fiedler_wt[index] = (edge, adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)] * (fiedler_vector_base_graph[Graphs.src(edge)] - fiedler_vector_base_graph[Graphs.dst(edge)])^2) + edges_fiedler_wt[index] = ( + edge, + adjacency_augment_graph[Graphs.src(edge), Graphs.dst(edge)] * + ( + fiedler_vector_base_graph[Graphs.src(edge)] - + fiedler_vector_base_graph[Graphs.dst(edge)] + )^2, + ) end # Sorting the list of tuples in descending order of fiedler weights @@ -93,8 +117,12 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) G = Graphs.SimpleGraph(adjacency_base_graph) # Adding edges based on fiedler weights to construct initial graph with required edges - for i = 1:lom.data["augment_budget"] - Graphs.add_edge!(G, Graphs.src(sorted_edges_fiedler_wt[i][1]), Graphs.dst(sorted_edges_fiedler_wt[i][1])) + for i in 1:lom.data["augment_budget"] + Graphs.add_edge!( + G, + Graphs.src(sorted_edges_fiedler_wt[i][1]), + Graphs.dst(sorted_edges_fiedler_wt[i][1]), + ) end if lom.data["augment_budget"] > 0 @@ -105,22 +133,20 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) sorted_edges_fiedler_wt, lom.options.kopt_parameter, lom.options.num_swaps_bound_kopt, - lom.data["augment_budget"] + lom.data["augment_budget"], ) else - adjacency_star, ac_star = Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) + adjacency_star, ac_star = Matrix(Graphs.adjacency_matrix(G)), + LOpt.algebraic_connectivity( + (adjacency_augment_graph + adjacency_base_graph) .* + Matrix(Graphs.adjacency_matrix(G)), + ) end - + return adjacency_star, ac_star end -function build_span_tree( - num_nodes, - adjacency_augment_graph, - edge_wt_sorted, - central_node, - ) - +function build_span_tree(num_nodes, adjacency_augment_graph, edge_wt_sorted, central_node) G = Graphs.SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph uncon_set = Int64[] # Set contains all unconnected nodes uncon_set = collect(1:num_nodes) @@ -128,55 +154,108 @@ function build_span_tree( sec_con_set = Int64[] # Set contains nodes which are connected to nodes in dir_con_set for j in eachindex(edge_wt_sorted) - if (Graphs.src(edge_wt_sorted[j][1]) == central_node || Graphs.dst(edge_wt_sorted[j][1]) == central_node) - + if ( + Graphs.src(edge_wt_sorted[j][1]) == central_node || + Graphs.dst(edge_wt_sorted[j][1]) == central_node + ) + # First edge of the graph - Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[j][1]), Graphs.dst(edge_wt_sorted[j][1])) + Graphs.add_edge!( + G, + Graphs.src(edge_wt_sorted[j][1]), + Graphs.dst(edge_wt_sorted[j][1]), + ) # Adding to directly connected set - Graphs.src(edge_wt_sorted[j][1]) == central_node && push!(dir_con_set, Graphs.dst(edge_wt_sorted[j][1])) - Graphs.dst(edge_wt_sorted[j][1]) == central_node && push!(dir_con_set, Graphs.src(edge_wt_sorted[j][1])) + Graphs.src(edge_wt_sorted[j][1]) == central_node && + push!(dir_con_set, Graphs.dst(edge_wt_sorted[j][1])) + Graphs.dst(edge_wt_sorted[j][1]) == central_node && + push!(dir_con_set, Graphs.src(edge_wt_sorted[j][1])) # Removing from unconnected set - deleteat!(uncon_set, uncon_set .== Graphs.src(edge_wt_sorted[j][1])) + deleteat!(uncon_set, uncon_set .== Graphs.src(edge_wt_sorted[j][1])) deleteat!(uncon_set, uncon_set .== Graphs.dst(edge_wt_sorted[j][1])) break end end while !Graphs.is_connected(G) # Connecting nodes until a spanning tree is formed - ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef,1) + ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef, 1) for j in 1:(size(dir_con_set)[1]+1) for k in eachindex(edge_wt_sorted) if j == 1 - if (Graphs.src(edge_wt_sorted[k][1]) == central_node) && issubset([Graphs.dst(edge_wt_sorted[k][1])], uncon_set) || - (Graphs.dst(edge_wt_sorted[k][1]) == central_node) && issubset([Graphs.src(edge_wt_sorted[k][1])], uncon_set) - + if (Graphs.src(edge_wt_sorted[k][1]) == central_node) && + issubset([Graphs.dst(edge_wt_sorted[k][1])], uncon_set) || + (Graphs.dst(edge_wt_sorted[k][1]) == central_node) && + issubset([Graphs.src(edge_wt_sorted[k][1])], uncon_set) + # Adding edge to central node - Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) - + Graphs.add_edge!( + G, + Graphs.src(edge_wt_sorted[k][1]), + Graphs.dst(edge_wt_sorted[k][1]), + ) + # Storing current algebraic connectivity - ac_tracker = ((Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) - + ac_tracker = ( + ( + Graphs.src(edge_wt_sorted[k][1]), + Graphs.dst(edge_wt_sorted[k][1]), + ), + LOpt.algebraic_connectivity((weighted_adjacency_matrix( + G, + adjacency_augment_graph, + num_nodes - size(uncon_set)[1] + 1, + ))), + ) + # Removing the added edge - Graphs.rem_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) + Graphs.rem_edge!( + G, + Graphs.src(edge_wt_sorted[k][1]), + Graphs.dst(edge_wt_sorted[k][1]), + ) break end else - if (Graphs.src(edge_wt_sorted[k][1]) == dir_con_set[j-1]) && issubset([Graphs.dst(edge_wt_sorted[k][1])], uncon_set) || - (Graphs.dst(edge_wt_sorted[k][1]) == dir_con_set[j-1]) && issubset([Graphs.src(edge_wt_sorted[k][1])], uncon_set) - + if (Graphs.src(edge_wt_sorted[k][1]) == dir_con_set[j-1]) && + issubset([Graphs.dst(edge_wt_sorted[k][1])], uncon_set) || + (Graphs.dst(edge_wt_sorted[k][1]) == dir_con_set[j-1]) && + issubset([Graphs.src(edge_wt_sorted[k][1])], uncon_set) + # Adding edge to directly connected node - Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) + Graphs.add_edge!( + G, + Graphs.src(edge_wt_sorted[k][1]), + Graphs.dst(edge_wt_sorted[k][1]), + ) # Updating current algebraic connectivity - if LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1))) > ac_tracker[2] - ac_tracker = ((Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])),LOpt.algebraic_connectivity((weighted_adjacency_matrix(G, adjacency_augment_graph, num_nodes - size(uncon_set)[1] + 1)))) + if LOpt.algebraic_connectivity((weighted_adjacency_matrix( + G, + adjacency_augment_graph, + num_nodes - size(uncon_set)[1] + 1, + ))) > ac_tracker[2] + ac_tracker = ( + ( + Graphs.src(edge_wt_sorted[k][1]), + Graphs.dst(edge_wt_sorted[k][1]), + ), + LOpt.algebraic_connectivity((weighted_adjacency_matrix( + G, + adjacency_augment_graph, + num_nodes - size(uncon_set)[1] + 1, + ))), + ) end # Removing the added edge - Graphs.rem_edge!(G, Graphs.src(edge_wt_sorted[k][1]), Graphs.dst(edge_wt_sorted[k][1])) + Graphs.rem_edge!( + G, + Graphs.src(edge_wt_sorted[k][1]), + Graphs.dst(edge_wt_sorted[k][1]), + ) break end end @@ -185,22 +264,23 @@ function build_span_tree( # Adding edge Graphs.add_edge!(G, ac_tracker[1][1], ac_tracker[1][2]) - + if (ac_tracker[1][1] == central_node) && issubset(ac_tracker[1][2], uncon_set) # Adding node to directly connected set push!(dir_con_set, ac_tracker[1][2]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== ac_tracker[1][2]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][2]) elseif (ac_tracker[1][2] == central_node) && issubset(ac_tracker[1][1], uncon_set) # Adding node to directly connected set push!(dir_con_set, ac_tracker[1][1]) # Removing node from unconnected nodes set - deleteat!(uncon_set, uncon_set .== ac_tracker[1][1]) + deleteat!(uncon_set, uncon_set .== ac_tracker[1][1]) - elseif issubset(ac_tracker[1][1], dir_con_set) && issubset(ac_tracker[1][2], uncon_set) + elseif issubset(ac_tracker[1][1], dir_con_set) && + issubset(ac_tracker[1][2], uncon_set) # Adding node to secondary connected set push!(sec_con_set, ac_tracker[1][2]) @@ -208,17 +288,21 @@ function build_span_tree( # Removing node from unconnected nodes set deleteat!(uncon_set, uncon_set .== ac_tracker[1][2]) - elseif issubset(ac_tracker[1][2], dir_con_set) && issubset(ac_tracker[1][1], uncon_set) + elseif issubset(ac_tracker[1][2], dir_con_set) && + issubset(ac_tracker[1][1], uncon_set) # Adding node to secondary connected set push!(sec_con_set, ac_tracker[1][1]) # Removing node from unconnected nodes set deleteat!(uncon_set, uncon_set .== ac_tracker[1][1]) - end - end - return (Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G)))) + return ( + Matrix(Graphs.adjacency_matrix(G)), + LOpt.algebraic_connectivity( + adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G)), + ), + ) end function refinement_span_tree( @@ -227,26 +311,44 @@ function refinement_span_tree( adjacency_graph_ac_tuple, kopt_parameter, num_swaps_bound_kopt, - ) - +) G = Graphs.SimpleGraph(adjacency_graph_ac_tuple[1]) # Refinement of Algebraic connectivity if kopt_parameter == 1 cycle_basis_current = Vector{Int64} for i in eachindex(edge_wt_sorted) - if Graphs.add_edge!(G, Graphs.src(edge_wt_sorted[i][1]), Graphs.dst(edge_wt_sorted[i][1])) + if Graphs.add_edge!( + G, + Graphs.src(edge_wt_sorted[i][1]), + Graphs.dst(edge_wt_sorted[i][1]), + ) if Graphs.is_cyclic(G) cycle_basis_current = Graphs.cycle_basis(G)[1] ac_tracker = 0.0 vertices_tracker = [(undef, undef)] - for j = 1:(length(cycle_basis_current)-1) - for k = (j+1):length(cycle_basis_current) - if Graphs.rem_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) - if LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker - ac_tracker = LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) - vertices_tracker = [(cycle_basis_current[j], cycle_basis_current[k])] + for j in 1:(length(cycle_basis_current)-1) + for k in (j+1):length(cycle_basis_current) + if Graphs.rem_edge!( + G, + cycle_basis_current[j], + cycle_basis_current[k], + ) + if LOpt.algebraic_connectivity( + adjacency_augment_graph .* + Matrix(Graphs.adjacency_matrix(G)), + ) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity( + adjacency_augment_graph .* + Matrix(Graphs.adjacency_matrix(G)), + ) + vertices_tracker = + [(cycle_basis_current[j], cycle_basis_current[k])] end - Graphs.add_edge!(G, cycle_basis_current[j], cycle_basis_current[k]) + Graphs.add_edge!( + G, + cycle_basis_current[j], + cycle_basis_current[k], + ) end end end @@ -254,7 +356,11 @@ function refinement_span_tree( end end end - updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!( + G, + adjacency_augment_graph, + adjacency_graph_ac_tuple, + ) elseif kopt_parameter == 2 combinations = LOpt.edge_combinations(length(edge_wt_sorted), kopt_parameter) @@ -265,20 +371,50 @@ function refinement_span_tree( num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])] == 0 - G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])),(Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1]))]) + if Graphs.adjacency_matrix(G)[ + Graphs.src(edge_wt_sorted[combinations[i][1]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][1]][1]), + ] == 0 && + Graphs.adjacency_matrix(G)[ + Graphs.src(edge_wt_sorted[combinations[i][2]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][2]][1]), + ] == 0 + G = LOpt.add_multiple_edges!( + G, + [ + ( + Graphs.src(edge_wt_sorted[combinations[i][1]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][1]][1]), + ), + ( + Graphs.src(edge_wt_sorted[combinations[i][2]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][2]][1]), + ), + ], + ) if Graphs.is_cyclic(G) cycle_basis_current = Graphs.cycle_basis(G) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef)] - ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_span_two_edges!(G, adjacency_augment_graph, 0, 0, ac_tracker, vertices_tracker) + ac_tracker, vertices_tracker = + LOpt._vertices_tracker_update_span_two_edges!( + G, + adjacency_augment_graph, + 0, + 0, + ac_tracker, + vertices_tracker, + ) G = LOpt.remove_multiple_edges!(G, vertices_tracker) end end end - updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) - + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!( + G, + adjacency_augment_graph, + adjacency_graph_ac_tuple, + ) + elseif kopt_parameter == 3 combinations = LOpt.edge_combinations(length(edge_wt_sorted), kopt_parameter) cycle_basis_current = Vector{Int64} @@ -288,17 +424,53 @@ function refinement_span_tree( num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - if Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])] == 0 && - Graphs.adjacency_matrix(G)[Graphs.src(edge_wt_sorted[combinations[i][3]][1]), Graphs.dst(edge_wt_sorted[combinations[i][3]][1])] == 0 - G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_wt_sorted[combinations[i][1]][1]), Graphs.dst(edge_wt_sorted[combinations[i][1]][1])),(Graphs.src(edge_wt_sorted[combinations[i][2]][1]), Graphs.dst(edge_wt_sorted[combinations[i][2]][1])),(Graphs.src(edge_wt_sorted[combinations[i][3]][1]), Graphs.dst(edge_wt_sorted[combinations[i][3]][1]))]) + if Graphs.adjacency_matrix(G)[ + Graphs.src(edge_wt_sorted[combinations[i][1]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][1]][1]), + ] == 0 && + Graphs.adjacency_matrix(G)[ + Graphs.src(edge_wt_sorted[combinations[i][2]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][2]][1]), + ] == 0 && + Graphs.adjacency_matrix(G)[ + Graphs.src(edge_wt_sorted[combinations[i][3]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][3]][1]), + ] == 0 + G = LOpt.add_multiple_edges!( + G, + [ + ( + Graphs.src(edge_wt_sorted[combinations[i][1]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][1]][1]), + ), + ( + Graphs.src(edge_wt_sorted[combinations[i][2]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][2]][1]), + ), + ( + Graphs.src(edge_wt_sorted[combinations[i][3]][1]), + Graphs.dst(edge_wt_sorted[combinations[i][3]][1]), + ), + ], + ) if Graphs.is_cyclic(G) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] for j in 1:(length(Graphs.cycle_basis(G)[3])-1) for k in (j+1):length(Graphs.cycle_basis(G)[3]) - if Graphs.adjacency_matrix(G)[Graphs.cycle_basis(G)[3][j], Graphs.cycle_basis(G)[3][k]] == 1 - ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_span_two_edges!(G, adjacency_augment_graph, j, k, ac_tracker, vertices_tracker) + if Graphs.adjacency_matrix(G)[ + Graphs.cycle_basis(G)[3][j], + Graphs.cycle_basis(G)[3][k], + ] == 1 + ac_tracker, vertices_tracker = + LOpt._vertices_tracker_update_span_two_edges!( + G, + adjacency_augment_graph, + j, + k, + ac_tracker, + vertices_tracker, + ) end end end @@ -306,21 +478,24 @@ function refinement_span_tree( end end end - updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!(G, adjacency_augment_graph, adjacency_graph_ac_tuple) + updated_adjacency_graph_ac_tuple = LOpt.update_kopt_adjacency!( + G, + adjacency_augment_graph, + adjacency_graph_ac_tuple, + ) end return updated_adjacency_graph_ac_tuple end function refinement_tree( - G, + G, adjacency_base_graph, adjacency_augment_graph, - sorted_edges_fiedler_wt, + sorted_edges_fiedler_wt, kopt_parameter, num_swaps_bound_kopt, - augment_budget - ) - + augment_budget, +) if kopt_parameter == 1 if length(sorted_edges_fiedler_wt) <= num_swaps_bound_kopt num_swaps = length(sorted_edges_fiedler_wt) @@ -328,33 +503,53 @@ function refinement_tree( num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edges_fiedler_wt[i][1]) + G = refinement_tree_1opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edges_fiedler_wt[i][1], + ) end elseif kopt_parameter == 2 if augment_budget >= 2 - combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) + combinations = + LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edges_fiedler_wt[combinations[i][1]][1], sorted_edges_fiedler_wt[combinations[i][2]][1]) + G = refinement_tree_2opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edges_fiedler_wt[combinations[i][1]][1], + sorted_edges_fiedler_wt[combinations[i][2]][1], + ) end else Memento.eror(_LOGGER, "Augment budget should be greater than one.") end elseif kopt_parameter == 3 if augment_budget >= 3 - combinations = LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) + combinations = + LOpt.edge_combinations(length(sorted_edges_fiedler_wt), kopt_parameter) if length(combinations) <= num_swaps_bound_kopt num_swaps = length(combinations) else num_swaps = num_swaps_bound_kopt end for i in 1:num_swaps - G = refinement_tree_3opt!(G, adjacency_base_graph, adjacency_augment_graph, sorted_edges_fiedler_wt[combinations[i][1]][1], sorted_edges_fiedler_wt[combinations[i][2]][1],sorted_edges_fiedler_wt[combinations[i][3]][1]) + G = refinement_tree_3opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + sorted_edges_fiedler_wt[combinations[i][1]][1], + sorted_edges_fiedler_wt[combinations[i][2]][1], + sorted_edges_fiedler_wt[combinations[i][3]][1], + ) end else Memento.error(_LOGGER, "Augment budget should be greater than two.") @@ -362,37 +557,64 @@ function refinement_tree( else Memento.eror(_LOGGER, "kopt_parameter > 3 is currently not supported.") end - return Matrix(Graphs.adjacency_matrix(G)), LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) + return Matrix(Graphs.adjacency_matrix(G)), + LOpt.algebraic_connectivity( + (adjacency_augment_graph + adjacency_base_graph) .* + Matrix(Graphs.adjacency_matrix(G)), + ) end function _vertices_tracker_update_two_edges!( - G, + G, adjacency_base_graph, adjacency_augment_graph, j, # Index for third edge if kopt_parameter is 3 - ac_tracker, - vertices_tracker - ) + ac_tracker, + vertices_tracker, +) edge_list = collect(Graphs.edges(G)) for k in j+1:(length(edge_list)-1) for l in k+1:length(edge_list) edges_to_check = [] if j == 0 - edges_to_check = [(Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l]))] + edges_to_check = [ + (Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), + (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l])), + ] else - edges_to_check = [(Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])),(Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l]))] + edges_to_check = [ + (Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])), + (Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), + (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l])), + ] end - if (adjacency_base_graph[Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])] == 0) && (adjacency_base_graph[Graphs.src(edge_list[l]), Graphs.dst(edge_list[l])] == 0) + if ( + adjacency_base_graph[ + Graphs.src(edge_list[k]), + Graphs.dst(edge_list[k]), + ] == 0 + ) && ( + adjacency_base_graph[ + Graphs.src(edge_list[l]), + Graphs.dst(edge_list[l]), + ] == 0 + ) G = LOpt.remove_multiple_edges!(G, edges_to_check) - if LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker - ac_tracker = LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) + if LOpt.algebraic_connectivity( + (adjacency_augment_graph + adjacency_base_graph) .* + Matrix(Graphs.adjacency_matrix(G)), + ) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity( + (adjacency_augment_graph + adjacency_base_graph) .* + Matrix(Graphs.adjacency_matrix(G)), + ) vertices_tracker = edges_to_check end G = LOpt.add_multiple_edges!(G, edges_to_check) end end end - + return ac_tracker, vertices_tracker end @@ -403,7 +625,7 @@ function _vertices_tracker_update_span_two_edges!( k, # Index for third edge dst if kopt_parameter is 3 ac_tracker, vertices_tracker, - ) +) cycle_basis_current = Graphs.cycle_basis(G) for l in 1:(length(cycle_basis_current[2])-1) for m in (l+1):length(cycle_basis_current[2]) @@ -411,19 +633,45 @@ function _vertices_tracker_update_span_two_edges!( for q in (p+1):length(cycle_basis_current[1]) cycle_basis_to_check = [] if j == 0 && k == 0 - cycle_basis_to_check = [(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[1][p], cycle_basis_current[1][q])] - cycle_basis_condition = (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) + cycle_basis_to_check = [ + (cycle_basis_current[2][l], cycle_basis_current[2][m]), + (cycle_basis_current[1][p], cycle_basis_current[1][q]), + ] + cycle_basis_condition = + (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== + (cycle_basis_current[2][l], cycle_basis_current[2][m]) else - cycle_basis_to_check = [(cycle_basis_current[3][j], cycle_basis_current[3][k]),(cycle_basis_current[2][l], cycle_basis_current[2][m]),(cycle_basis_current[1][p], cycle_basis_current[1][q])] - cycle_basis_condition = (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) && - (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== (cycle_basis_current[1][p], cycle_basis_current[1][q]) && - (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) + cycle_basis_to_check = [ + (cycle_basis_current[3][j], cycle_basis_current[3][k]), + (cycle_basis_current[2][l], cycle_basis_current[2][m]), + (cycle_basis_current[1][p], cycle_basis_current[1][q]), + ] + cycle_basis_condition = + (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== + (cycle_basis_current[2][l], cycle_basis_current[2][m]) && + (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== + (cycle_basis_current[1][p], cycle_basis_current[1][q]) && + (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== + (cycle_basis_current[2][l], cycle_basis_current[2][m]) end if cycle_basis_condition - if Graphs.adjacency_matrix(G)[cycle_basis_current[2][l], cycle_basis_current[2][m]] == 1 && Graphs.adjacency_matrix(G)[cycle_basis_current[1][p], cycle_basis_current[1][q]] == 1 + if Graphs.adjacency_matrix(G)[ + cycle_basis_current[2][l], + cycle_basis_current[2][m], + ] == 1 && + Graphs.adjacency_matrix(G)[ + cycle_basis_current[1][p], + cycle_basis_current[1][q], + ] == 1 G = LOpt.remove_multiple_edges!(G, cycle_basis_to_check) - if LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker - ac_tracker = LOpt.algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) + if LOpt.algebraic_connectivity( + adjacency_augment_graph .* + Matrix(Graphs.adjacency_matrix(G)), + ) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity( + adjacency_augment_graph .* + Matrix(Graphs.adjacency_matrix(G)), + ) vertices_tracker = cycle_basis_to_check end G = LOpt.add_multiple_edges!(G, cycle_basis_to_check) @@ -437,11 +685,11 @@ function _vertices_tracker_update_span_two_edges!( end function refinement_tree_1opt!( - G, + G, adjacency_base_graph, - adjacency_augment_graph, - edge_to_check - ) + adjacency_augment_graph, + edge_to_check, +) if !(Graphs.has_edge(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check))) Graphs.add_edge!(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check)) ac_tracker = 0.0 @@ -449,8 +697,14 @@ function refinement_tree_1opt!( for edge in Graphs.edges(G) if adjacency_base_graph[Graphs.src(edge), Graphs.dst(edge)] == 0 Graphs.rem_edge!(G, Graphs.src(edge), Graphs.dst(edge)) - if LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) > ac_tracker - ac_tracker = LOpt.algebraic_connectivity((adjacency_augment_graph + adjacency_base_graph) .* Matrix(Graphs.adjacency_matrix(G))) + if LOpt.algebraic_connectivity( + (adjacency_augment_graph + adjacency_base_graph) .* + Matrix(Graphs.adjacency_matrix(G)), + ) > ac_tracker + ac_tracker = LOpt.algebraic_connectivity( + (adjacency_augment_graph + adjacency_base_graph) .* + Matrix(Graphs.adjacency_matrix(G)), + ) vertices_tracker = edge end Graphs.add_edge!(G, Graphs.src(edge), Graphs.dst(edge)) @@ -462,22 +716,54 @@ function refinement_tree_1opt!( end function refinement_tree_2opt!( - G, + G, adjacency_base_graph, - adjacency_augment_graph, + adjacency_augment_graph, edge_to_check_1, - edge_to_check_2 - ) - if !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) && !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) - G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1)),(Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))]) + edge_to_check_2, +) + if !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) && + !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) + G = LOpt.add_multiple_edges!( + G, + [ + (Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1)), + (Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2)), + ], + ) ac_tracker = 0.0 - vertices_tracker = [(undef, undef),(undef, undef)] - ac_tracker, vertices_tracker = _vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, 0, ac_tracker,vertices_tracker) + vertices_tracker = [(undef, undef), (undef, undef)] + ac_tracker, vertices_tracker = _vertices_tracker_update_two_edges!( + G, + adjacency_base_graph, + adjacency_augment_graph, + 0, + ac_tracker, + vertices_tracker, + ) G = LOpt.remove_multiple_edges!(G, vertices_tracker) - elseif !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) && (Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1) - elseif !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) && (Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2) + elseif !(Graphs.has_edge( + G, + Graphs.src(edge_to_check_1), + Graphs.dst(edge_to_check_1), + )) && (Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) + G = refinement_tree_1opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_1, + ) + elseif !(Graphs.has_edge( + G, + Graphs.src(edge_to_check_2), + Graphs.dst(edge_to_check_2), + )) && (Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) + G = refinement_tree_1opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_2, + ) end return G end @@ -489,32 +775,84 @@ function refinement_tree_3opt!( edge_to_check_1, edge_to_check_2, edge_to_check_3, - ) +) con1 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) con2 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) con3 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_3), Graphs.dst(edge_to_check_3))) - if con1 && con2 && con3 - G = LOpt.add_multiple_edges!(G, [(Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1)),(Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2)),(Graphs.src(edge_to_check_3), Graphs.dst(edge_to_check_3))]) + if con1 && con2 && con3 + G = LOpt.add_multiple_edges!( + G, + [ + (Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1)), + (Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2)), + (Graphs.src(edge_to_check_3), Graphs.dst(edge_to_check_3)), + ], + ) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] for j in eachindex(length(collect(Graphs.edges(G))) - 2) - if (adjacency_base_graph[Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])] == 0) - ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_two_edges!(G, adjacency_base_graph, adjacency_augment_graph, j, ac_tracker,vertices_tracker) + if ( + adjacency_base_graph[ + Graphs.src(edge_list[j]), + Graphs.dst(edge_list[j]), + ] == 0 + ) + ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_two_edges!( + G, + adjacency_base_graph, + adjacency_augment_graph, + j, + ac_tracker, + vertices_tracker, + ) end end G = LOpt.remove_multiple_edges!(G, vertices_tracker) elseif con1 && con2 && !(con3) - G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1, edge_to_check_2) + G = refinement_tree_2opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_1, + edge_to_check_2, + ) elseif con1 && !(con2) && con3 - G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1, edge_to_check_3) + G = refinement_tree_2opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_1, + edge_to_check_3, + ) elseif !(con1) && con2 && con3 - G = refinement_tree_2opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2, edge_to_check_3) + G = refinement_tree_2opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_2, + edge_to_check_3, + ) elseif con1 && !(con2) && !(con3) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_1) + G = refinement_tree_1opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_1, + ) elseif !(con1) && con2 && !(con3) - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_2) + G = refinement_tree_1opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_2, + ) elseif !(con1) && !(con2) && con3 - G = refinement_tree_1opt!(G, adjacency_base_graph, adjacency_augment_graph, edge_to_check_3) + G = refinement_tree_1opt!( + G, + adjacency_base_graph, + adjacency_augment_graph, + edge_to_check_3, + ) end return G -end \ No newline at end of file +end diff --git a/src/log.jl b/src/log.jl index 8e762a0..b1126d9 100644 --- a/src/log.jl +++ b/src/log.jl @@ -5,36 +5,31 @@ function visualize_solution( plot_file_format = "pdf", display_edge_weights = true, ) - num_edges_existing = data["num_edges_existing"] - adjacency_base_graph = data["adjacency_base_graph"] - adjacency_augment_graph = data["adjacency_augment_graph"] - - adjacency_full_graph = adjacency_augment_graph - (num_edges_existing > 0) && (adjacency_full_graph += adjacency_base_graph) - - if results["primal_status"] != MOI.FEASIBLE_POINT - Memento.error( - _LOGGER, - "Non-feasible primal status. Graph solution may not be exact", - ) + adjacency_full_graph = data["adjacency_augment_graph"] + (data["num_edges_existing"] > 0) && + (adjacency_full_graph += data["adjacency_base_graph"]) + + if results["solution_type"] == "optimal" + solution_mat = abs.(results["solution"]["z_var"]) + elseif results["solution_type"] == "heuristic" + solution_mat = abs.(results["heuristic_solution"]) end - M = - (isapprox.(results["solution"]["z_var"], 0, atol = 1E-5)) + - (isapprox.(results["solution"]["z_var"], 1, atol = 1E-5)) - - if sum(M) == data["num_nodes"]^2 + if sum( + (isapprox.(solution_mat, 0, atol = 1E-5)) + + (isapprox.(solution_mat, 1, atol = 1E-5)), + ) == data["num_nodes"]^2 Memento.info(_LOGGER, "Plotting the graph of integral solution") if visualizing_tool == "tikz" LOpt.plot_tikzgraph( - abs.(results["solution"]["z_var"]) .* adjacency_full_graph, + solution_mat .* adjacency_full_graph, plot_file_format = plot_file_format, display_edge_weights = display_edge_weights, ) elseif visualizing_tool == "graphviz" LOpt.plot_graphviz( - abs.(results["solution"]["z_var"]) .* adjacency_full_graph, + solution_mat .* adjacency_full_graph, display_edge_weights = display_edge_weights, ) end diff --git a/src/lopt_model.jl b/src/lopt_model.jl index 29544ca..56633c5 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -5,7 +5,7 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.set_option(lom, :eigen_cuts_sizes, [lom.data["num_nodes"], 2]) # Update defaults to user-defined options - if options !== nothing + if !isnothing(options) for i in keys(options) LOpt.set_option(lom, i, options[i]) end @@ -25,7 +25,6 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no LOpt.variable_LOModel(lom) LOpt.constraint_LOModel(lom; optimizer = optimizer) LOpt.objective_LOModel(lom) - end elseif lom.options.formulation_type == "max_span_tree" if lom.options.solution_type in ["optimal"] @@ -89,7 +88,7 @@ function optimize_LOModel!(lom::LaplacianOptModel; optimizer = nothing) end if JuMP.mode(lom.model) != JuMP.DIRECT && - lom.model.moi_backend.state == MOI.Utilities.NO_OPTIMIZER + lom.model.moi_backend.state == MOI.Utilities.NO_OPTIMIZER Memento.error( _LOGGER, "No optimizer specified in `optimize_LOModel!` or the given JuMP model.", @@ -124,9 +123,10 @@ function run_LOpt( ) data = LOpt.get_data(params) model_lopt = LOpt.build_LOModel(data, optimizer = lom_optimizer, options = options) - if (:solution_type in keys(options)) && (options[:solution_type] == "heuristic") + + if model_lopt.options.solution_type == "heuristic" result_lopt = LOpt.heuristic_kopt(model_lopt) - else + elseif model_lopt.options.solution_type == "optimal" result_lopt = LOpt.optimize_LOModel!(model_lopt, optimizer = lom_optimizer) end @@ -177,13 +177,13 @@ end function lazycallback_status(lom::LaplacianOptModel) if ( - size(lom.options.eigen_cuts_sizes)[1] > 0 && - minimum(lom.options.eigen_cuts_sizes) >= 2 - ) || - lom.options.topology_flow_cuts || - lom.options.soc_linearized_cuts || - lom.options.cheeger_cuts || - lom.options.sdp_relaxation + size(lom.options.eigen_cuts_sizes)[1] > 0 && + minimum(lom.options.eigen_cuts_sizes) >= 2 + ) || + lom.options.topology_flow_cuts || + lom.options.soc_linearized_cuts || + lom.options.cheeger_cuts || + lom.options.sdp_relaxation return true else return false @@ -219,4 +219,4 @@ function _logging_info(lom::LaplacianOptModel) elseif lom.options.solution_type == "heuristic" Memento.info(_LOGGER, "Applying heuristics to obtain a lower bound") end -end \ No newline at end of file +end diff --git a/src/solution.jl b/src/solution.jl index 8f7e214..b1651a9 100644 --- a/src/solution.jl +++ b/src/solution.jl @@ -21,6 +21,13 @@ function build_LOModel_result(lom::LaplacianOptModel, solve_time::Number) ) end + if JuMP.primal_status(lom.model) != MOI.FEASIBLE_POINT + Memento.error( + _LOGGER, + "Non-feasible primal status. Graph solution may not be exact", + ) + end + result = Dict{String,Any}( "optimizer" => JuMP.solver_name(lom.model), "termination_status" => JuMP.termination_status(lom.model), @@ -88,7 +95,7 @@ a boolean if the integer solution satisfies the optimality certificate for the M """ function optimality_certificate_MISDP(lom::LaplacianOptModel, result::Dict{String,Any}) if ("z_var" in keys(result["solution"])) && - !(lom.options.formulation_type == "max_span_tree") + !(lom.options.formulation_type == "max_span_tree") adjacency_full_graph = lom.data["adjacency_augment_graph"] (lom.data["num_edges_existing"] > 0) && (adjacency_full_graph += lom.data["adjacency_base_graph"]) @@ -103,15 +110,20 @@ function optimality_certificate_MISDP(lom::LaplacianOptModel, result::Dict{Strin end end -function build_LOModel_heuristic_result(lom::LaplacianOptModel, heuristic_time::Number, adjacency_star, ac_star) - +function build_LOModel_heuristic_result( + lom::LaplacianOptModel, + heuristic_time::Number, + heuristic_solution, + heuristic_objective::Number, +) result = Dict{String,Any}( - "heuristic_objective" => ac_star, + "heuristic_objective" => heuristic_objective, "heuristic_solve_time" => heuristic_time, - "heuristic_solution" => adjacency_star, + "heuristic_solution" => heuristic_solution, + "solution_type" => lom.options.solution_type, "adjacency_base_graph" => lom.data["adjacency_base_graph"], "adjacency_augment_graph" => lom.data["adjacency_augment_graph"], ) return result -end \ No newline at end of file +end diff --git a/src/types.jl b/src/types.jl index 84cd318..6d2fde6 100644 --- a/src/types.jl +++ b/src/types.jl @@ -55,7 +55,7 @@ function get_default_options() kopt_parameter = 2 # 1 <= Integer value <= 3 num_central_nodes_kopt = 5 # 1 <= Integer value <= size of instance - num_swaps_bound_kopt = 1E6 # Integer value + num_swaps_bound_kopt = 1E4 # Integer value best_lower_bound = 0 # Best known feasible solution's objective best_incumbent = nothing @@ -140,4 +140,4 @@ mutable struct GraphData return graph_data end -end \ No newline at end of file +end diff --git a/src/utility.jl b/src/utility.jl index 71cee4c..92dba69 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -377,7 +377,11 @@ function weighted_adjacency_matrix( for i in 1:size for j in i:size - weighted_adj_matrix_size[i, j] = (adjacency_augment_graph.*Graphs.adjacency_matrix(G))[vertices_from_edges[i],vertices_from_edges[j]] + weighted_adj_matrix_size[i, j] = + (adjacency_augment_graph.*Graphs.adjacency_matrix(G))[ + vertices_from_edges[i], + vertices_from_edges[j], + ] weighted_adj_matrix_size[j, i] = weighted_adj_matrix_size[i, j] end end @@ -397,8 +401,13 @@ function update_kopt_adjacency!( adjacency_graph_ac_tuple::Tuple{Array,Float64}; tol = 1E-6, ) - if algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) - adjacency_graph_ac_tuple[2] >= tol - return Matrix(Graphs.adjacency_matrix(G)), algebraic_connectivity(adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G))) + if algebraic_connectivity( + adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G)), + ) - adjacency_graph_ac_tuple[2] >= tol + return Matrix(Graphs.adjacency_matrix(G)), + algebraic_connectivity( + adjacency_augment_graph .* Matrix(Graphs.adjacency_matrix(G)), + ) else return adjacency_graph_ac_tuple end @@ -467,4 +476,4 @@ function remove_multiple_edges!( Graphs.rem_edge!(G, edge_set[i][1], edge_set[i][2]) end return G -end \ No newline at end of file +end diff --git a/test/heuristics_tests.jl b/test/heuristics_tests.jl index 5bca799..2ae18f7 100644 --- a/test/heuristics_tests.jl +++ b/test/heuristics_tests.jl @@ -7,29 +7,29 @@ Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) model_options = Dict{Symbol,Any}( - :solution_type => "heuristic", - :kopt_parameter => 1, - :num_central_nodes_kopt => 5, - :time_limit => test_time_limit() + :solution_type => "heuristic", + :kopt_parameter => 1, + :num_central_nodes_kopt => 5, + :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" - @test isapprox(result_lo["objective"], 22.8042, atol = 1E-4) - @test isapprox(result_lo["solution"]["z_var"][1, 7], 1.0) - @test isapprox(result_lo["solution"]["z_var"][7, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 7], 1.0) - @test isapprox(result_lo["solution"]["z_var"][7, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 7], 1.0) - @test isapprox(result_lo["solution"]["z_var"][7, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 6], 1.0) - @test isapprox(result_lo["solution"]["z_var"][6, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 7], 1.0) - @test isapprox(result_lo["solution"]["z_var"][7, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 7], 1.0) - @test isapprox(result_lo["solution"]["z_var"][7, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][7, 8], 1.0) - @test isapprox(result_lo["solution"]["z_var"][8, 7], 1.0) + @test isapprox(result["heuristic_objective"], 22.8042, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 7], 1.0) + @test isapprox(result["heuristic_solution"][7, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 7], 1.0) + @test isapprox(result["heuristic_solution"][7, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 7], 1.0) + @test isapprox(result["heuristic_solution"][7, 3], 1.0) + @test isapprox(result["heuristic_solution"][4, 6], 1.0) + @test isapprox(result["heuristic_solution"][6, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 7], 1.0) + @test isapprox(result["heuristic_solution"][7, 4], 1.0) + @test isapprox(result["heuristic_solution"][5, 7], 1.0) + @test isapprox(result["heuristic_solution"][7, 5], 1.0) + @test isapprox(result["heuristic_solution"][7, 8], 1.0) + @test isapprox(result["heuristic_solution"][8, 7], 1.0) end @testset "Heuristic test: 2-opt_spanning_tree" begin @@ -49,22 +49,21 @@ end result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" - @test isapprox(result_lo["objective"], 22.5051, atol = 1E-4) - @test isapprox(result_lo["solution"]["z_var"][1, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 6], 1.0) - @test isapprox(result_lo["solution"]["z_var"][6, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 7], 1.0) - @test isapprox(result_lo["solution"]["z_var"][7, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][7, 8], 1.0) - @test isapprox(result_lo["solution"]["z_var"][8, 7], 1.0) - + @test isapprox(result["heuristic_objective"], 22.5051, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 3], 1.0) + @test isapprox(result["heuristic_solution"][4, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 4], 1.0) + @test isapprox(result["heuristic_solution"][5, 6], 1.0) + @test isapprox(result["heuristic_solution"][6, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 7], 1.0) + @test isapprox(result["heuristic_solution"][7, 5], 1.0) + @test isapprox(result["heuristic_solution"][7, 8], 1.0) + @test isapprox(result["heuristic_solution"][8, 7], 1.0) end @testset "Heuristic test: 3-opt_spanning_tree" begin @@ -84,15 +83,15 @@ end result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" - @test isapprox(result_lo["objective"], 11.1592, atol = 1E-4) - @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) + @test isapprox(result["heuristic_objective"], 11.1592, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 3], 1.0) end @testset "Heuristic test: 1-opt_tree" begin @@ -112,23 +111,23 @@ end result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" - @test isapprox(result_lo["objective"], 36.0425, atol = 1E-4) - @test isapprox(result_lo["solution"]["z_var"][1, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][1, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 4], 1.0) + @test isapprox(result["heuristic_objective"], 46.028980895, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 1], 1.0) + @test isapprox(result["heuristic_solution"][1, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 1], 1.0) + @test isapprox(result["heuristic_solution"][1, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 3], 1.0) + @test isapprox(result["heuristic_solution"][4, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 4], 1.0) end @testset "Heuristic test: 2-opt_tree" begin @@ -148,23 +147,23 @@ end result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" - @test isapprox(result_lo["objective"], 36.0425, atol = 1E-4) - @test isapprox(result_lo["solution"]["z_var"][1, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][1, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 4], 1.0) + @test isapprox(result["heuristic_objective"], 46.028980895, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 1], 1.0) + @test isapprox(result["heuristic_solution"][1, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 1], 1.0) + @test isapprox(result["heuristic_solution"][1, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 3], 1.0) + @test isapprox(result["heuristic_solution"][4, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 4], 1.0) end @testset "Heuristic test: 3-opt_tree" begin @@ -184,21 +183,21 @@ end result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" - @test isapprox(result_lo["objective"], 36.0425, atol = 1E-4) - @test isapprox(result_lo["solution"]["z_var"][1, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][1, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][1, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 1], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][2, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 2], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 4], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][3, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 3], 1.0) - @test isapprox(result_lo["solution"]["z_var"][4, 5], 1.0) - @test isapprox(result_lo["solution"]["z_var"][5, 4], 1.0) + @test isapprox(result["heuristic_objective"], 46.028980895, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 1], 1.0) + @test isapprox(result["heuristic_solution"][1, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 1], 1.0) + @test isapprox(result["heuristic_solution"][1, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 3], 1.0) + @test isapprox(result["heuristic_solution"][4, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 4], 1.0) end From e09726dc533ff789532cd5304607d20fed6c4530 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Fri, 18 Aug 2023 10:08:40 -0600 Subject: [PATCH 26/35] more clean up --- src/heuristics.jl | 123 +++++++++++++++++++++++++--------------------- src/utility.jl | 9 ++-- 2 files changed, 72 insertions(+), 60 deletions(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index cbb7971..180a93e 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,6 +1,9 @@ # Heuristics to maximize algebraic connectivity of weighted graphs -function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) +function heuristic_kopt( + lom::LaplacianOptModel, + optimizer = nothing +) adjacency_star = [] ac_star = [] @@ -22,7 +25,9 @@ function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) return merge(lom.result, result_dict) end -function heuristic_spanning_tree(lom::LaplacianOptModel) +function heuristic_spanning_tree( + lom::LaplacianOptModel +) num_nodes = lom.data["num_nodes"] adjacency_augment_graph = lom.data["adjacency_augment_graph"] num_central_nodes_kopt = lom.options.num_central_nodes_kopt @@ -31,11 +36,11 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) # Making list of tuples of edges and edge weights - edge_wt_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}( + edge_wt_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}( undef, length(edge_augment_list), ) - edge_wt_sorted = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}( + edge_wt_sorted = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}( undef, length(edge_augment_list), ) @@ -53,19 +58,18 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity - adjacency_graph_list = Vector{Tuple{Any,Any}}(undef, num_central_nodes_kopt) + adjacency_graph_list = Vector{Tuple{Matrix{<:Number}, <:Number}}(undef, num_central_nodes_kopt) #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) #Threads.@threads for index in 1:num_central_nodes_kopt #uncomment this line for multi threading for index in 1:num_central_nodes_kopt #comment this line for multi threading - # Builds a spanning tree + # Build a spanning tree adjacency_graph_list[index] = LOpt.build_span_tree( num_nodes, adjacency_augment_graph, edge_wt_sorted, priority_central_nodes_list[index], ) - adjacency_graph_list[index] = LOpt.refinement_span_tree( adjacency_augment_graph, edge_wt_sorted, @@ -81,7 +85,9 @@ function heuristic_spanning_tree(lom::LaplacianOptModel) return adjacency_star, ac_star end -function heuristic_base_graph_connected(lom::LaplacianOptModel) +function heuristic_base_graph_connected( + lom::LaplacianOptModel +) adjacency_augment_graph = lom.data["adjacency_augment_graph"] adjacency_base_graph = lom.data["adjacency_base_graph"] @@ -90,11 +96,11 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) - edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}( + edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}( undef, length(edge_augment_list), ) - sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}( + sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}( undef, length(edge_augment_list), ) @@ -146,7 +152,12 @@ function heuristic_base_graph_connected(lom::LaplacianOptModel) return adjacency_star, ac_star end -function build_span_tree(num_nodes, adjacency_augment_graph, edge_wt_sorted, central_node) +function build_span_tree( + num_nodes::Int64, + adjacency_augment_graph::Matrix{<:Number}, + edge_wt_sorted::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}, + central_node::Int64 +) G = Graphs.SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph uncon_set = Int64[] # Set contains all unconnected nodes uncon_set = collect(1:num_nodes) @@ -180,7 +191,7 @@ function build_span_tree(num_nodes, adjacency_augment_graph, edge_wt_sorted, cen end while !Graphs.is_connected(G) # Connecting nodes until a spanning tree is formed - ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,Float64}}(undef, 1) + ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}(undef, 1) for j in 1:(size(dir_con_set)[1]+1) for k in eachindex(edge_wt_sorted) @@ -306,11 +317,11 @@ function build_span_tree(num_nodes, adjacency_augment_graph, edge_wt_sorted, cen end function refinement_span_tree( - adjacency_augment_graph, - edge_wt_sorted, - adjacency_graph_ac_tuple, - kopt_parameter, - num_swaps_bound_kopt, + adjacency_augment_graph::Matrix{<:Number}, + edge_wt_sorted::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}, + adjacency_graph_ac_tuple::Tuple{Matrix{<:Number}, <:Number}, + kopt_parameter::Int64, + num_swaps_bound_kopt::Int64, ) G = Graphs.SimpleGraph(adjacency_graph_ac_tuple[1]) # Refinement of Algebraic connectivity @@ -488,13 +499,13 @@ function refinement_span_tree( end function refinement_tree( - G, - adjacency_base_graph, - adjacency_augment_graph, - sorted_edges_fiedler_wt, - kopt_parameter, - num_swaps_bound_kopt, - augment_budget, + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + adjacency_base_graph::Matrix{<:Number}, + adjacency_augment_graph::Matrix{<:Number}, + sorted_edges_fiedler_wt::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}, + kopt_parameter::Int64, + num_swaps_bound_kopt::Int64, + augment_budget::Int64, ) if kopt_parameter == 1 if length(sorted_edges_fiedler_wt) <= num_swaps_bound_kopt @@ -565,25 +576,25 @@ function refinement_tree( end function _vertices_tracker_update_two_edges!( - G, - adjacency_base_graph, - adjacency_augment_graph, - j, # Index for third edge if kopt_parameter is 3 - ac_tracker, + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + adjacency_base_graph::Matrix{<:Number}, + adjacency_augment_graph::Matrix{<:Number}, + j_idx::Int64, # Index for third edge if kopt_parameter is 3 + ac_tracker::Number, vertices_tracker, ) edge_list = collect(Graphs.edges(G)) - for k in j+1:(length(edge_list)-1) + for k in j_idx+1:(length(edge_list)-1) for l in k+1:length(edge_list) edges_to_check = [] - if j == 0 + if j_idx == 0 edges_to_check = [ (Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l])), ] else edges_to_check = [ - (Graphs.src(edge_list[j]), Graphs.dst(edge_list[j])), + (Graphs.src(edge_list[j_idx]), Graphs.dst(edge_list[j_idx])), (Graphs.src(edge_list[k]), Graphs.dst(edge_list[k])), (Graphs.src(edge_list[l]), Graphs.dst(edge_list[l])), ] @@ -619,11 +630,11 @@ function _vertices_tracker_update_two_edges!( end function _vertices_tracker_update_span_two_edges!( - G, - adjacency_augment_graph, - j, # Index for third edge src if kopt_parameter is 3 - k, # Index for third edge dst if kopt_parameter is 3 - ac_tracker, + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + adjacency_augment_graph::Matrix{<:Number}, + j_idx::Int64, # Index for third edge src if kopt_parameter is 3 + k_idx::Int64, # Index for third edge dst if kopt_parameter is 3 + ac_tracker::Number, vertices_tracker, ) cycle_basis_current = Graphs.cycle_basis(G) @@ -632,7 +643,7 @@ function _vertices_tracker_update_span_two_edges!( for p in 1:(length(cycle_basis_current[1])-1) for q in (p+1):length(cycle_basis_current[1]) cycle_basis_to_check = [] - if j == 0 && k == 0 + if j_idx == 0 && k_idx == 0 cycle_basis_to_check = [ (cycle_basis_current[2][l], cycle_basis_current[2][m]), (cycle_basis_current[1][p], cycle_basis_current[1][q]), @@ -642,14 +653,14 @@ function _vertices_tracker_update_span_two_edges!( (cycle_basis_current[2][l], cycle_basis_current[2][m]) else cycle_basis_to_check = [ - (cycle_basis_current[3][j], cycle_basis_current[3][k]), + (cycle_basis_current[3][j_idx], cycle_basis_current[3][k_idx]), (cycle_basis_current[2][l], cycle_basis_current[2][m]), (cycle_basis_current[1][p], cycle_basis_current[1][q]), ] cycle_basis_condition = - (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== + (cycle_basis_current[3][j_idx], cycle_basis_current[3][k_idx]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) && - (cycle_basis_current[3][j], cycle_basis_current[3][k]) !== + (cycle_basis_current[3][j_idx], cycle_basis_current[3][k_idx]) !== (cycle_basis_current[1][p], cycle_basis_current[1][q]) && (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) @@ -685,10 +696,10 @@ function _vertices_tracker_update_span_two_edges!( end function refinement_tree_1opt!( - G, - adjacency_base_graph, - adjacency_augment_graph, - edge_to_check, + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + adjacency_base_graph::Matrix{<:Number}, + adjacency_augment_graph::Matrix{<:Number}, + edge_to_check::Graphs.SimpleGraphs.SimpleEdge{<:Number}, ) if !(Graphs.has_edge(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check))) Graphs.add_edge!(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check)) @@ -716,11 +727,11 @@ function refinement_tree_1opt!( end function refinement_tree_2opt!( - G, - adjacency_base_graph, - adjacency_augment_graph, - edge_to_check_1, - edge_to_check_2, + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + adjacency_base_graph::Matrix{<:Number}, + adjacency_augment_graph::Matrix{<:Number}, + edge_to_check_1::Graphs.SimpleGraphs.SimpleEdge{<:Number}, + edge_to_check_2::Graphs.SimpleGraphs.SimpleEdge{<:Number}, ) if !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) && !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) @@ -769,12 +780,12 @@ function refinement_tree_2opt!( end function refinement_tree_3opt!( - G, - adjacency_base_graph, - adjacency_augment_graph, - edge_to_check_1, - edge_to_check_2, - edge_to_check_3, + G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, + adjacency_base_graph::Matrix{<:Number}, + adjacency_augment_graph::Matrix{<:Number}, + edge_to_check_1::Graphs.SimpleGraphs.SimpleEdge{<:Number}, + edge_to_check_2::Graphs.SimpleGraphs.SimpleEdge{<:Number}, + edge_to_check_3::Graphs.SimpleGraphs.SimpleEdge{<:Number}, ) con1 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) con2 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) diff --git a/src/utility.jl b/src/utility.jl index 92dba69..4b77dc4 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -338,7 +338,6 @@ end Returns a vector of order of central nodes as an input to construct the graph. """ - function priority_central_nodes( adjacency_augment_graph::Array{<:Number}, num_nodes::Int64, @@ -359,7 +358,6 @@ end Returns a weighted adjacency matrix for the connected part of graph. """ - function weighted_adjacency_matrix( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, @@ -398,7 +396,7 @@ Returns an updated adjacency_graph_ac_tuple after the kopt refinement. function update_kopt_adjacency!( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, - adjacency_graph_ac_tuple::Tuple{Array,Float64}; + adjacency_graph_ac_tuple::Tuple{Array, <:Number}; tol = 1E-6, ) if algebraic_connectivity( @@ -419,7 +417,10 @@ end Returns all combinations possible for given `n` and `k`. """ -function edge_combinations(num_edges::Int64, kopt_parameter::Int64) +function edge_combinations( + num_edges::Int64, + kopt_parameter::Int64 +) if kopt_parameter < 2 || kopt_parameter > 3 Memento.error(_LOGGER, "kopt_parameter must be either 2 or 3") elseif num_edges < kopt_parameter From 5cdf2a997f3d258e6d91fce617c1c35f4e74e972 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Fri, 18 Aug 2023 10:11:44 -0600 Subject: [PATCH 27/35] formatting --- src/heuristics.jl | 55 +++++++++++++++++++++++++---------------------- src/utility.jl | 7 ++---- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index 180a93e..8f1f2e2 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -1,9 +1,6 @@ # Heuristics to maximize algebraic connectivity of weighted graphs -function heuristic_kopt( - lom::LaplacianOptModel, - optimizer = nothing -) +function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) adjacency_star = [] ac_star = [] @@ -25,9 +22,7 @@ function heuristic_kopt( return merge(lom.result, result_dict) end -function heuristic_spanning_tree( - lom::LaplacianOptModel -) +function heuristic_spanning_tree(lom::LaplacianOptModel) num_nodes = lom.data["num_nodes"] adjacency_augment_graph = lom.data["adjacency_augment_graph"] num_central_nodes_kopt = lom.options.num_central_nodes_kopt @@ -36,11 +31,11 @@ function heuristic_spanning_tree( edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) # Making list of tuples of edges and edge weights - edge_wt_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}( + edge_wt_list = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}( undef, length(edge_augment_list), ) - edge_wt_sorted = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}( + edge_wt_sorted = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}( undef, length(edge_augment_list), ) @@ -58,7 +53,8 @@ function heuristic_spanning_tree( LOpt.priority_central_nodes(adjacency_augment_graph, num_nodes) # To keep track of adjacency matrix of highest algebraic connectivity and it's algebraic connectivity - adjacency_graph_list = Vector{Tuple{Matrix{<:Number}, <:Number}}(undef, num_central_nodes_kopt) + adjacency_graph_list = + Vector{Tuple{Matrix{<:Number},<:Number}}(undef, num_central_nodes_kopt) #JULIA_NUM_THREADS=auto #uncomment this line for multi threading (Requires atleast Julia 1.7) #Threads.@threads for index in 1:num_central_nodes_kopt #uncomment this line for multi threading @@ -85,9 +81,7 @@ function heuristic_spanning_tree( return adjacency_star, ac_star end -function heuristic_base_graph_connected( - lom::LaplacianOptModel -) +function heuristic_base_graph_connected(lom::LaplacianOptModel) adjacency_augment_graph = lom.data["adjacency_augment_graph"] adjacency_base_graph = lom.data["adjacency_base_graph"] @@ -96,11 +90,11 @@ function heuristic_base_graph_connected( # Making list of tuples of edges and fiedler weights (w_ij * (v_i - v_j)^2) edge_augment_list = collect(Graphs.edges(Graphs.SimpleGraph(adjacency_augment_graph))) - edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}( + edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}( undef, length(edge_augment_list), ) - sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}( + sorted_edges_fiedler_wt = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}( undef, length(edge_augment_list), ) @@ -153,10 +147,10 @@ function heuristic_base_graph_connected( end function build_span_tree( - num_nodes::Int64, - adjacency_augment_graph::Matrix{<:Number}, - edge_wt_sorted::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}, - central_node::Int64 + num_nodes::Int64, + adjacency_augment_graph::Matrix{<:Number}, + edge_wt_sorted::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}, + central_node::Int64, ) G = Graphs.SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph uncon_set = Int64[] # Set contains all unconnected nodes @@ -191,7 +185,7 @@ function build_span_tree( end while !Graphs.is_connected(G) # Connecting nodes until a spanning tree is formed - ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}(undef, 1) + ac_tracker = Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}(undef, 1) for j in 1:(size(dir_con_set)[1]+1) for k in eachindex(edge_wt_sorted) @@ -318,8 +312,8 @@ end function refinement_span_tree( adjacency_augment_graph::Matrix{<:Number}, - edge_wt_sorted::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}, - adjacency_graph_ac_tuple::Tuple{Matrix{<:Number}, <:Number}, + edge_wt_sorted::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}, + adjacency_graph_ac_tuple::Tuple{Matrix{<:Number},<:Number}, kopt_parameter::Int64, num_swaps_bound_kopt::Int64, ) @@ -502,7 +496,7 @@ function refinement_tree( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_base_graph::Matrix{<:Number}, adjacency_augment_graph::Matrix{<:Number}, - sorted_edges_fiedler_wt::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge, <:Number}}, + sorted_edges_fiedler_wt::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}, kopt_parameter::Int64, num_swaps_bound_kopt::Int64, augment_budget::Int64, @@ -653,14 +647,23 @@ function _vertices_tracker_update_span_two_edges!( (cycle_basis_current[2][l], cycle_basis_current[2][m]) else cycle_basis_to_check = [ - (cycle_basis_current[3][j_idx], cycle_basis_current[3][k_idx]), + ( + cycle_basis_current[3][j_idx], + cycle_basis_current[3][k_idx], + ), (cycle_basis_current[2][l], cycle_basis_current[2][m]), (cycle_basis_current[1][p], cycle_basis_current[1][q]), ] cycle_basis_condition = - (cycle_basis_current[3][j_idx], cycle_basis_current[3][k_idx]) !== + ( + cycle_basis_current[3][j_idx], + cycle_basis_current[3][k_idx], + ) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) && - (cycle_basis_current[3][j_idx], cycle_basis_current[3][k_idx]) !== + ( + cycle_basis_current[3][j_idx], + cycle_basis_current[3][k_idx], + ) !== (cycle_basis_current[1][p], cycle_basis_current[1][q]) && (cycle_basis_current[1][p], cycle_basis_current[1][q]) !== (cycle_basis_current[2][l], cycle_basis_current[2][m]) diff --git a/src/utility.jl b/src/utility.jl index 4b77dc4..56b27b1 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -396,7 +396,7 @@ Returns an updated adjacency_graph_ac_tuple after the kopt refinement. function update_kopt_adjacency!( G::Graphs.SimpleGraphs.SimpleGraph{<:Number}, adjacency_augment_graph::Array{<:Number}, - adjacency_graph_ac_tuple::Tuple{Array, <:Number}; + adjacency_graph_ac_tuple::Tuple{Array,<:Number}; tol = 1E-6, ) if algebraic_connectivity( @@ -417,10 +417,7 @@ end Returns all combinations possible for given `n` and `k`. """ -function edge_combinations( - num_edges::Int64, - kopt_parameter::Int64 -) +function edge_combinations(num_edges::Int64, kopt_parameter::Int64) if kopt_parameter < 2 || kopt_parameter > 3 Memento.error(_LOGGER, "kopt_parameter must be either 2 or 3") elseif num_edges < kopt_parameter From 348184ef2a9006622c438a52ae37ecee7031e129 Mon Sep 17 00:00:00 2001 From: Neelkamal18 Date: Fri, 18 Aug 2023 15:26:37 -0500 Subject: [PATCH 28/35] updated test cases --- src/heuristics.jl | 25 ++++++----- test/heuristics_tests.jl | 93 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 13 deletions(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index 8f1f2e2..fd0907c 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -3,7 +3,7 @@ function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) adjacency_star = [] ac_star = [] - + if (lom.data["num_edges_existing"] == 0) && (lom.data["augment_budget"] == (lom.data["num_nodes"] - 1)) _, heuristic_time, solve_bytes_alloc, sec_in_gc = @@ -151,7 +151,7 @@ function build_span_tree( adjacency_augment_graph::Matrix{<:Number}, edge_wt_sorted::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}, central_node::Int64, -) + ) G = Graphs.SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph uncon_set = Int64[] # Set contains all unconnected nodes uncon_set = collect(1:num_nodes) @@ -316,7 +316,7 @@ function refinement_span_tree( adjacency_graph_ac_tuple::Tuple{Matrix{<:Number},<:Number}, kopt_parameter::Int64, num_swaps_bound_kopt::Int64, -) + ) G = Graphs.SimpleGraph(adjacency_graph_ac_tuple[1]) # Refinement of Algebraic connectivity if kopt_parameter == 1 @@ -500,7 +500,7 @@ function refinement_tree( kopt_parameter::Int64, num_swaps_bound_kopt::Int64, augment_budget::Int64, -) + ) if kopt_parameter == 1 if length(sorted_edges_fiedler_wt) <= num_swaps_bound_kopt num_swaps = length(sorted_edges_fiedler_wt) @@ -576,7 +576,7 @@ function _vertices_tracker_update_two_edges!( j_idx::Int64, # Index for third edge if kopt_parameter is 3 ac_tracker::Number, vertices_tracker, -) + ) edge_list = collect(Graphs.edges(G)) for k in j_idx+1:(length(edge_list)-1) for l in k+1:length(edge_list) @@ -619,7 +619,6 @@ function _vertices_tracker_update_two_edges!( end end end - return ac_tracker, vertices_tracker end @@ -630,7 +629,7 @@ function _vertices_tracker_update_span_two_edges!( k_idx::Int64, # Index for third edge dst if kopt_parameter is 3 ac_tracker::Number, vertices_tracker, -) + ) cycle_basis_current = Graphs.cycle_basis(G) for l in 1:(length(cycle_basis_current[2])-1) for m in (l+1):length(cycle_basis_current[2]) @@ -703,7 +702,7 @@ function refinement_tree_1opt!( adjacency_base_graph::Matrix{<:Number}, adjacency_augment_graph::Matrix{<:Number}, edge_to_check::Graphs.SimpleGraphs.SimpleEdge{<:Number}, -) + ) if !(Graphs.has_edge(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check))) Graphs.add_edge!(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check)) ac_tracker = 0.0 @@ -735,7 +734,7 @@ function refinement_tree_2opt!( adjacency_augment_graph::Matrix{<:Number}, edge_to_check_1::Graphs.SimpleGraphs.SimpleEdge{<:Number}, edge_to_check_2::Graphs.SimpleGraphs.SimpleEdge{<:Number}, -) + ) if !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) && !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) G = LOpt.add_multiple_edges!( @@ -789,7 +788,7 @@ function refinement_tree_3opt!( edge_to_check_1::Graphs.SimpleGraphs.SimpleEdge{<:Number}, edge_to_check_2::Graphs.SimpleGraphs.SimpleEdge{<:Number}, edge_to_check_3::Graphs.SimpleGraphs.SimpleEdge{<:Number}, -) + ) con1 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) con2 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) con3 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_3), Graphs.dst(edge_to_check_3))) @@ -804,11 +803,11 @@ function refinement_tree_3opt!( ) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in eachindex(length(collect(Graphs.edges(G))) - 2) + for j in 1:(length(collect(Graphs.edges(G))) - 2) if ( adjacency_base_graph[ - Graphs.src(edge_list[j]), - Graphs.dst(edge_list[j]), + Graphs.src(collect(Graphs.edges(G))[j]), + Graphs.dst(collect(Graphs.edges(G))[j]), ] == 0 ) ac_tracker, vertices_tracker = LOpt._vertices_tracker_update_two_edges!( diff --git a/test/heuristics_tests.jl b/test/heuristics_tests.jl index 2ae18f7..07aacae 100644 --- a/test/heuristics_tests.jl +++ b/test/heuristics_tests.jl @@ -201,3 +201,96 @@ end @test isapprox(result["heuristic_solution"][4, 5], 1.0) @test isapprox(result["heuristic_solution"][5, 4], 1.0) end + +@testset "Heuristic test: zero augment budget" begin + num_nodes = 4 + adjacency_base_graph = [0 2 0 0; 2 0 3 0; 0 3 0 4; 0 0 4 0] + adjacency_augment_graph = [0 0 4 8; 0 0 0 7; 4 0 0 0; 8 7 0 0] + augment_budget = 0 + data_dict, augment_budget = data_II() + + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) + + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 1, + :time_limit => test_time_limit(), + ) + result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + + @test result["solution_type"] == "heuristic" + @test isapprox(result["heuristic_objective"], 1.610684749, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 3], 1.0) +end + +@testset "Heuristic test: refinement_tree_1opt_2opt!" begin + num_nodes = 4 + adjacency_base_graph = [0 2 0 0; 2 0 3 0; 0 3 0 4; 0 0 4 0] + adjacency_augment_graph = [0 0 4 8; 0 0 0 7; 4 0 0 0; 8 7 0 0] + augment_budget = 2 + data_dict, augment_budget = data_II() + + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) + + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 2, + :time_limit => test_time_limit(), + ) + result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + + @test result["solution_type"] == "heuristic" + @test isapprox(result["heuristic_objective"], 7.8551986404, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 3], 1.0) + @test isapprox(result["heuristic_solution"][1, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 2], 1.0) +end + +@testset "Heuristic test: refinement_tree_2opt_3opt!" begin + num_nodes = 5 + adjacency_base_graph = [0 2 0 0 0; 2 0 3 0 0; 0 3 0 4 0; 0 0 4 0 5; 0 0 0 5 0] + adjacency_augment_graph = [0 0 4 8 10; 0 0 0 7 9; 4 0 0 0 6; 8 7 0 0 0; 10 9 6 0 0] + augment_budget = 3 + data_dict, augment_budget = data_II() + + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) + + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 3, + :time_limit => test_time_limit(), + ) + result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) + + @test result["solution_type"] == "heuristic" + @test isapprox(result["heuristic_objective"], 9.276487957, atol = 1E-4) + @test isapprox(result["heuristic_solution"][1, 2], 1.0) + @test isapprox(result["heuristic_solution"][2, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 2], 1.0) + @test isapprox(result["heuristic_solution"][3, 4], 1.0) + @test isapprox(result["heuristic_solution"][4, 3], 1.0) + @test isapprox(result["heuristic_solution"][4, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 4], 1.0) + @test isapprox(result["heuristic_solution"][1, 3], 1.0) + @test isapprox(result["heuristic_solution"][3, 1], 1.0) + @test isapprox(result["heuristic_solution"][1, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 1], 1.0) + @test isapprox(result["heuristic_solution"][2, 5], 1.0) + @test isapprox(result["heuristic_solution"][5, 2], 1.0) +end From 8dac1bc146a45683e2c81a1f7dd6aa0c00849ea2 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Mon, 21 Aug 2023 13:25:33 -0600 Subject: [PATCH 29/35] minor formatting --- src/heuristics.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/heuristics.jl b/src/heuristics.jl index fd0907c..a0f3011 100644 --- a/src/heuristics.jl +++ b/src/heuristics.jl @@ -3,7 +3,7 @@ function heuristic_kopt(lom::LaplacianOptModel, optimizer = nothing) adjacency_star = [] ac_star = [] - + if (lom.data["num_edges_existing"] == 0) && (lom.data["augment_budget"] == (lom.data["num_nodes"] - 1)) _, heuristic_time, solve_bytes_alloc, sec_in_gc = @@ -151,7 +151,7 @@ function build_span_tree( adjacency_augment_graph::Matrix{<:Number}, edge_wt_sorted::Vector{Tuple{Graphs.SimpleGraphs.SimpleEdge,<:Number}}, central_node::Int64, - ) +) G = Graphs.SimpleGraph(zeros(num_nodes, num_nodes)) # Starting graph uncon_set = Int64[] # Set contains all unconnected nodes uncon_set = collect(1:num_nodes) @@ -316,7 +316,7 @@ function refinement_span_tree( adjacency_graph_ac_tuple::Tuple{Matrix{<:Number},<:Number}, kopt_parameter::Int64, num_swaps_bound_kopt::Int64, - ) +) G = Graphs.SimpleGraph(adjacency_graph_ac_tuple[1]) # Refinement of Algebraic connectivity if kopt_parameter == 1 @@ -500,7 +500,7 @@ function refinement_tree( kopt_parameter::Int64, num_swaps_bound_kopt::Int64, augment_budget::Int64, - ) +) if kopt_parameter == 1 if length(sorted_edges_fiedler_wt) <= num_swaps_bound_kopt num_swaps = length(sorted_edges_fiedler_wt) @@ -576,7 +576,7 @@ function _vertices_tracker_update_two_edges!( j_idx::Int64, # Index for third edge if kopt_parameter is 3 ac_tracker::Number, vertices_tracker, - ) +) edge_list = collect(Graphs.edges(G)) for k in j_idx+1:(length(edge_list)-1) for l in k+1:length(edge_list) @@ -629,7 +629,7 @@ function _vertices_tracker_update_span_two_edges!( k_idx::Int64, # Index for third edge dst if kopt_parameter is 3 ac_tracker::Number, vertices_tracker, - ) +) cycle_basis_current = Graphs.cycle_basis(G) for l in 1:(length(cycle_basis_current[2])-1) for m in (l+1):length(cycle_basis_current[2]) @@ -702,7 +702,7 @@ function refinement_tree_1opt!( adjacency_base_graph::Matrix{<:Number}, adjacency_augment_graph::Matrix{<:Number}, edge_to_check::Graphs.SimpleGraphs.SimpleEdge{<:Number}, - ) +) if !(Graphs.has_edge(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check))) Graphs.add_edge!(G, Graphs.src(edge_to_check), Graphs.dst(edge_to_check)) ac_tracker = 0.0 @@ -734,7 +734,7 @@ function refinement_tree_2opt!( adjacency_augment_graph::Matrix{<:Number}, edge_to_check_1::Graphs.SimpleGraphs.SimpleEdge{<:Number}, edge_to_check_2::Graphs.SimpleGraphs.SimpleEdge{<:Number}, - ) +) if !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) && !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) G = LOpt.add_multiple_edges!( @@ -788,7 +788,7 @@ function refinement_tree_3opt!( edge_to_check_1::Graphs.SimpleGraphs.SimpleEdge{<:Number}, edge_to_check_2::Graphs.SimpleGraphs.SimpleEdge{<:Number}, edge_to_check_3::Graphs.SimpleGraphs.SimpleEdge{<:Number}, - ) +) con1 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_1), Graphs.dst(edge_to_check_1))) con2 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_2), Graphs.dst(edge_to_check_2))) con3 = !(Graphs.has_edge(G, Graphs.src(edge_to_check_3), Graphs.dst(edge_to_check_3))) @@ -803,7 +803,7 @@ function refinement_tree_3opt!( ) ac_tracker = 0.0 vertices_tracker = [(undef, undef), (undef, undef), (undef, undef)] - for j in 1:(length(collect(Graphs.edges(G))) - 2) + for j in 1:(length(collect(Graphs.edges(G)))-2) if ( adjacency_base_graph[ Graphs.src(collect(Graphs.edges(G))[j]), From 19e443a4d657f486942006968697c385e49699d3 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Mon, 21 Aug 2023 13:54:25 -0600 Subject: [PATCH 30/35] heuristics test bug fix --- test/heuristics_tests.jl | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/test/heuristics_tests.jl b/test/heuristics_tests.jl index 07aacae..320c48c 100644 --- a/test/heuristics_tests.jl +++ b/test/heuristics_tests.jl @@ -203,10 +203,14 @@ end end @testset "Heuristic test: zero augment budget" begin - num_nodes = 4 - adjacency_base_graph = [0 2 0 0; 2 0 3 0; 0 3 0 4; 0 0 4 0] - adjacency_augment_graph = [0 0 4 8; 0 0 0 7; 4 0 0 0; 8 7 0 0] - augment_budget = 0 + function data_II() + data_dict = Dict{String,Any}() + data_dict["num_nodes"] = 4 + data_dict["adjacency_base_graph"] = [0 2 0 0; 2 0 3 0; 0 3 0 4; 0 0 4 0] + data_dict["adjacency_augment_graph"] = [0 0 4 8; 0 0 0 7; 4 0 0 0; 8 7 0 0] + augment_budget = 0 + return data_dict, augment_budget + end data_dict, augment_budget = data_II() params = @@ -230,10 +234,14 @@ end end @testset "Heuristic test: refinement_tree_1opt_2opt!" begin - num_nodes = 4 - adjacency_base_graph = [0 2 0 0; 2 0 3 0; 0 3 0 4; 0 0 4 0] - adjacency_augment_graph = [0 0 4 8; 0 0 0 7; 4 0 0 0; 8 7 0 0] - augment_budget = 2 + function data_II() + data_dict = Dict{String,Any}() + data_dict["num_nodes"] = 4 + data_dict["adjacency_base_graph"] = [0 2 0 0; 2 0 3 0; 0 3 0 4; 0 0 4 0] + data_dict["adjacency_augment_graph"] = [0 0 4 8; 0 0 0 7; 4 0 0 0; 8 7 0 0] + augment_budget = 2 + return data_dict, augment_budget + end data_dict, augment_budget = data_II() params = @@ -261,10 +269,16 @@ end end @testset "Heuristic test: refinement_tree_2opt_3opt!" begin - num_nodes = 5 - adjacency_base_graph = [0 2 0 0 0; 2 0 3 0 0; 0 3 0 4 0; 0 0 4 0 5; 0 0 0 5 0] - adjacency_augment_graph = [0 0 4 8 10; 0 0 0 7 9; 4 0 0 0 6; 8 7 0 0 0; 10 9 6 0 0] - augment_budget = 3 + function data_II() + data_dict = Dict{String,Any}() + data_dict["num_nodes"] = 5 + data_dict["adjacency_base_graph"] = + [0 2 0 0 0; 2 0 3 0 0; 0 3 0 4 0; 0 0 4 0 5; 0 0 0 5 0] + data_dict["adjacency_augment_graph"] = + [0 0 4 8 10; 0 0 0 7 9; 4 0 0 0 6; 8 7 0 0 0; 10 9 6 0 0] + augment_budget = 3 + return data_dict, augment_budget + end data_dict, augment_budget = data_II() params = From 3502304809644637f4a39683ab60a86d58e0e57d Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Mon, 21 Aug 2023 14:26:23 -0600 Subject: [PATCH 31/35] more tests for heuristic soln visualization --- examples/plots/plot_5.pdf | Bin 0 -> 3661 bytes test/log_tests.jl | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 examples/plots/plot_5.pdf diff --git a/examples/plots/plot_5.pdf b/examples/plots/plot_5.pdf new file mode 100644 index 0000000000000000000000000000000000000000..248d3fc8578e41b43b3621348f7c53c67a41b146 GIT binary patch literal 3661 zcma)(51nEQwy-1Z3NT`8?5ReuHsS1M9q*tj@R1mpR z1d-mGQUZcBDS}j$qA$Md9cSLVGxwf3f9%=W`JFxI_u1X=4`iT@l7dRhF@yStTSl2- zKnRdv@5-#80M@~IIFX%!Fa#3#=LJT&;K?`=7=_1>aq2h+0v4yJ$m~TX;V|yZzT=-B zc9jS~S;N0S zy>Dk~f&+bpKDJtSBq|5vOjLrMsz~zI;aNaf-+fj_Rj!b*_mGZnz-UMY^UXU=GZoHR zopAcRW;+G9nuI<}5#%68Z^tS`Aj(5Hfd8Hf-y5e*qUPOF=TJ+575_p*E}0c)J8V>I zN-`A{6;pRjoZre6GwaO#vOOG=z;ww_C|<;^3Lo-lhV|lnDo2a9))AB5-EZWvtm|Pa z@-5T-DcK~~dY?>`#$cxEi0F^(gz83qNiaa}vRiwTcw+K%Pu{*V^RtVM4)+w;Y!ip# z7Dnf)Hda*$;SUj%)J2U>Jxa#mbR3rUX_nfi{vzP1J zH;Hf9h4Ka}zm(@e-Q3PpzU}AF@RW+NaqQSdI6iHL2Ea@bM^6S=SZtYBj{ELDg)@}~ zl_I^LZ$IDNdJ1~hJINy~SASnqNgu z+nL4EJ<^{4K61?E{?>uD+kG3QDv_=Vi2I?nJPDeCsoIy{6uS()F<8hhK5{fIcQ znwC0>;6XN_*$}nADh&#S!2VUGQB2%*jGed}_|O4~68PblxaNr)Y0r@EEJaoZT;V$G z44wwmIT7=Z`BG&si0fqr{dCp#OQ)r9EX$(8M_n%;lbqhYdb_*G;u*;6&e(-6RbEH? z^bey`wbsv8d2dNiAUph&di|y@Ec(T(abDz6iF?#Q#VZ<^HazVYzFZ^xB60o?;V5T# z!V_3**XuU)CP=iN?vhA8=b)vL{RdG>-F7Lu{L$ZAL3(IFnx6`KtGkXpM$2?~!WX zjP}gW-q(MU{PHZ3yXmdM5fjs?2ESUi{_JZ_~68O^@xukZtZhbZ(LQc zmfkVLn(62kIRLVScRAsjN$GcI=W4ZF*b`gxL_KN*!1WF1mxrn`hA_1(+Feo>?+Xm!;{#|I7w~%SC@7U$Cv<++ z6RcQMSglw?EPvq%Vifa(AACosNX(GCshPnM+@?-};0qm0$b#-c@e@0-nW<+tVn$B` z$SOeJnXQzKGzc4j5F$BxlNj+wWLyu4q8d;e$1A3lr-$CUzIUc=)?37U>tp3i!ejHD z@uO4UwRauLhNZ8~;03#RZ|)EJp(VH~`3O_N>qTQda99D3&*>c-%r@o}t4(G}Xr9jY zIA39#9jD@s^6@I~`Vn(V&1;V7kcRC3c}ClQKlO$Imvoa-m z!uAB=o)3y~M#Qxg6U3F?Rx-5CV;9$?wY9~vm)!UjsWH7`+oG+Po*&g9c}>49DlV)RcVnPG&V25J^ohZ-5@AFZ2}YouWIXDZy!< zF|7Mo>-HDp(qTC3`g~0MpxSZz4bP7V<(WD8twNDt zp;toO%e~4&8=D1_1}xX=6Bj8t@pQ`GK^51)wnM&6H^P^~)(jAXH+%vY7?ZkJMb9D=gj$cl^H;|SC66h-z9#Z$;nBY*>mu@HS!j0+j8+r0q>ea znW8vMD>E*$a45l3dF~CW`A&jTUs;mSM}-h052c0d^*GCNKeOglXsyonT^oN;{~ay~ z#`o(?5&Apo&i=xl!ld(5E~W^P2UC!G=8unV(H%x^X8ujjy7aqe{n7!l2>8Eih4+k_ z(9O#1_3LKQ5vo_-;4N2j5`s)wjyxVjaS)c)s-!Q~Vk(i_>lu^ol949InEX=Uvg`Fd z(;sjdRCHw%Hn0T~R<=~yh;1l61LLO(42%>_a^gDjTkObJ{w_|Fbi5-JZ+PMi+O`q#ba?5KA#_}FAq|YNYse{Km*NaOyjIy-O z6_-$FVu^ZX^EoQ~4Cxx*ZjZVfGgT!{ol7$mRfrh7U7ntkq_nKy<6h0dnR6>+n`gtq zLg|9Qz}en53!#2QuA%xnj(Z}h>SVzIUG;c{7`ClCPQb>CTLTu_3(ZJ+x2c4|uN0Ge zE`eq>&8NR8)CKfMPn{7!#a1P5eRN(hza58coNM z2{b>=`RmDr;Gs^_dO$Jt%Q6rdSqMS~3cVyFcL^Z{fn9(=E{KD53D|##Fd<=xL>v~3 z!r;AdU;`5kb7_<}9j3m$$NAjY*7WijY|4jy{b(t9+5l$>dzb-D88mSJ6 zykl0A+q*iEewo&~1$iuWyR|RYM9+3XFsJ-Zw~Nm1OTEW>9FIZiuymWI_)He1k8%Ny zg_P!3JLXaR4($nVO)czy95$uzjP+9mp3P>Scv#l)4r_e9ccCVSFKuUa9ZZMsEG&?E zaX*uuh*I*_dz_|qJQ|Hl4_9pj=qF{2-{_QG-ZX{3t)hIp_UQxwo))p+rf-`tvXdd6 zystO5*mSp(lFx5e95=MI!S%3P`EFxKFjzPwmqR@`UUO>gJHs{JAYF?i2q^D+z96g*7CsO ze61irDAF2i?Bb838DKLjC{P9nwFVmz2xK7i=V{7_7U)O-LVuPZ%~rQkkU_#x8nQAl zSs93$1`3H#RfEaOLZGT}gbY$0B_pQ@{O1tsztD!s{vsRr563f})~=I55LWtuv1I+T zp*4m_5(hvGhK!|b`m&Ah+8r_R9qf#2uEiU}eRof`SidUD8u3v3mV5YtG37Qr^8f#d Y7a2n$`;u^u%(AkuOUxjUhCYh data_dict, - "augment_budget" => (num_nodes - 1), - "tol_zero" => 1E-4, - "tol_psd" => 1E-3, - "eigen_cuts_full" => true, - "topology_flow_cuts" => true, - "lazycuts_logging" => true, + params = + Dict{String,Any}("data_dict" => data_dict, "augment_budget" => (num_nodes - 1)) + + model_options = Dict{Symbol,Any}( + :tol_zero => 1E-4, + :tol_psd => 1E-3, + :eigen_cuts_full => true, + :topology_flow_cuts => true, + :lazycuts_logging => true, + :time_limit => test_time_limit(), ) data = LOpt.get_data(params) @@ -46,5 +48,23 @@ @test result_lopt["termination_status"] == MOI.OPTIMAL @test result_lopt["primal_status"] == MOI.FEASIBLE_POINT - # Nothing more to test for coverage since the visualizaiton depends on exterior packages. So, I believe the dot files generated are accurate. + # Visualizing based on heuristic solution + model_options = Dict{Symbol,Any}( + :solution_type => "heuristic", + :kopt_parameter => 2, + :time_limit => test_time_limit(), + ) + result_heuristic = LaplacianOpt.run_LOpt( + params, + glpk_optimizer; + options = model_options, + visualize_solution = true, # Make it true to plot the graph solution + visualizing_tool = "tikz", + ) + @test result_heuristic["solution_type"] == "heuristic" + @test isapprox( + result_heuristic["heuristic_objective"], + result_lopt["objective"], + atol = 1E-4, + ) end From 5b747d86dc5b40615ba0be923729d7c2f1abdda1 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Mon, 21 Aug 2023 16:13:15 -0600 Subject: [PATCH 32/35] test update --- examples/plots/plot_5.pdf | Bin 3661 -> 3661 bytes test/log_tests.jl | 3 --- 2 files changed, 3 deletions(-) diff --git a/examples/plots/plot_5.pdf b/examples/plots/plot_5.pdf index 248d3fc8578e41b43b3621348f7c53c67a41b146..5f4ddbdcf191e2e6b90dc8efaaeec9b815f974e9 100644 GIT binary patch delta 124 zcmX>rb5>@9A(y(Dp^=HPu7R1lfq}ZFzHfetOJYf?f`*Hgk%5u1frXKwAy~!c2rg-6 vXLC~{M?*7XGh=g0S0^(^GYc0BXJb=iBU3jQV^a$kCp!fjLP{oYrb5>@9A(y&|k*SG=u7R1lfq}ZFzHfetOJYf?f`*Hgk%5u1frXKwAy~!c2rg-6 vXCq5 1E-4, - :tol_psd => 1E-3, - :eigen_cuts_full => true, - :topology_flow_cuts => true, :lazycuts_logging => true, :time_limit => test_time_limit(), ) From 914b4523a0bf950829c4d3f38820151aa75f7f8b Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Mon, 21 Aug 2023 16:38:07 -0600 Subject: [PATCH 33/35] tikz -> graphviz test --- examples/plots/plot_5.dot | 9 ------- examples/plots/plot_5.pdf | Bin 3661 -> 0 bytes examples/plots/plot_5.tex | 52 -------------------------------------- test/log_tests.jl | 2 +- 4 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 examples/plots/plot_5.dot delete mode 100644 examples/plots/plot_5.pdf delete mode 100644 examples/plots/plot_5.tex diff --git a/examples/plots/plot_5.dot b/examples/plots/plot_5.dot deleted file mode 100644 index 3bc1453..0000000 --- a/examples/plots/plot_5.dot +++ /dev/null @@ -1,9 +0,0 @@ -graph G { -layout=neato; -size="10,5"; -node [fontname="Helvetica", fontsize=20, shape = circle, width=0.4, fixedsize=true, style="filled", fillcolor="0.650 0.200 1.000"]; -1 -- 4 [fontsize=9, fontname="Helvetica"]; -2 -- 4 [fontsize=9, fontname="Helvetica"]; -3 -- 4 [fontsize=9, fontname="Helvetica"]; -3 -- 5 [fontsize=9, fontname="Helvetica"]; -} \ No newline at end of file diff --git a/examples/plots/plot_5.pdf b/examples/plots/plot_5.pdf deleted file mode 100644 index 5f4ddbdcf191e2e6b90dc8efaaeec9b815f974e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3661 zcma)%h(p#~B{Kw1=}DhNuGUZqM=LF7si zM0yXPlz<>jiXc^`=!@@q$C>x;%)MvMAA5FoerM15eRlWz6Ej4jq#-i$%whw>t)t9P zAQ*^uaAQ_f1nFWuok=c0C>#O&^8%qmrm7*%)kFoGh-@IV|2`|D8dqredvNDBU<^2u`R1MGnM#-J zE*O1&i@ltfN)U0c#)UqbVZAt?#?h*yb;Pu9_Zw*}`+B&l zLhJNEY7Wt@!8Z$~LCI1Z5&e;qSkuHW1p>%lcJFAGNJ@F`#oJ$QadxrE@tz`^UD8ne z!suM}#;O`V;vt-zwy4piPtH8Nt{JoL!P8rRWkg<_+TC=vhzBL-Z|f=Dx2ZpT!q8BDK&WZb=*d7!%PotlalhTCFs8EL zGKA0b?dQ8&PsN_~P4WoKH{92hRw&3?(^i{Y4r#3&?L!g`=EiZ^9r2&jI*wkS_pp%` zbI2cIgSHjV`z)Ptq`YiPspi#SRw}a)1^Bf%a0c>Lxleo`Zr5;}FL00bwiW2t`>e<8 zerB<3kGL1Gj~H{kzja{ae&1HPTBN%Y>~UzLK!jwXtM?T)ISG{t3%GYuoKjDGutb&l z`L+60-h*-eAA@Zv`wo7mqmE;j=9l&AL(tE9BILpfSA;G* zgO?$7PQ>D4fpqx`!g{&k09}p4(rIZd%d)8OQMb#-q^5VT-tH;3d?x07XY4|^8n2T> z#)r|VI-6&!ytiZ~5S{+Yeg0Dy7X1^{IWO|4#y@JL;*<={8lMgbU#=B?ku?8@aI_07 z@d-4p`*pkarkH30-6fF%PKuSW!v|4v{dSpl#iQ8^E+g;xO!$_D=h{miuZfk3o!Bgz zvpD#)#IhaTg&NVUjLuviT4B;VtI8SR!1YHKRh9RF80Om#Z-Bjj^UQ)_1PxKMin``* z1)MJjw$K@KhEMFAEt2Zf(bE{Jcv~$FS3fZX#DRsYCeP)Zhpi*Kx~gQ} z3O_NMo8h(+9k)!hGjl`vd+H;@W37_8EHGeT2=;*uw?Iv*ZeF_+X$xwSX)K&_-N23B zgw?4-a>^OX%{#X?ZvL`c92(tCwzM4lKwxV7!M{l-o0 zYS|rgjJd8}u_GWyc$X8VnVfNVcCJp#l|88~U(~Z!0MyWUet8%}zt^@y(d%U=Me7bu z^-j}w6`T&X247K=Cg(h57?$HZM^U-VCb8?Y zzF_5=;%em@eEADcFr&CX?BF|GRdR;ZL(K|_M`zP|R>^}RFgvpymgTOX@t5+7Ua zj31r)uCwb{J}h%>1}E6Vdvl-SuPw<{#fP5?SuY;zg+U9ke9rIKpmwpRSZ%XP!}4{v z$N7rd?KzcpRE}5sG>lk~YhQCrhc@O6%rn{z0K!g{Ht_AG8VrvY?2c_5oFuvlnU^cm zWH;p3d?R8he9aI}x#1hMz?c#-Q~IUe zdF5p}fqm6b%7WhA#*xQRA$KmIyW9}Wy?RW0|6S7encUpunLS6}TO)5lcC8n$6!NY) zmMcj>wX))~iiQ%sROa5GTJ9t&_m?LNeN+rJ_EcWTS&z4>@HcN+h1BV8-?a@88@R(I z$@qSqDbipE=@KC9B}_a|ItV_Rp)-N3(2Z#N;R(Q|2 zS-VAry;a4P zZswb;9fhS+H?*_GZ?duK1Q{rGZGPGj@h{7u=!gt1W9O9;c-CH$v0Q8EI`g~3+aM)& zroIFqD_MQwVt&zm8?U~lme6ExlfVgSojA(5PrbOLRvhuQ`uSX3BXYQ@cZJrHF zOXUj!gJ=8NErkZ)c}B>09QQ=hkR-uDJ!FDnEZbH+Ct%~ntwBqjg%$+8`&1(3E7|m( zYmj+u%jqx5xmWvHb{PmJdwPGfov`2AX-LFld>pYvpt!CN+7xR+6MtZs3|IoBPSbHD zJk3vY{(5r7dm?FC4=9ejEDM&E1H)w@5GX|UlAJUcdI1c+AOX_DWBwt+l!zt}uow^u zjq}EW3{5pGWKcdhoEqBG^JfyKF0S4{+WluTQb02-(VO;K;GbFjHyNO=t1lKP3x>cY zBxr*3*R)3b{-mE{6lCDwWs7UnouQrlYmgzUz1fH0{@rnn&=r!(AeFQTsTqs zdUa2-CQNufRArcg(#qjUW)lbmOCoH$r%dPG-*_9M66g~6q`>kOdA&E?>%`HcQISB^ zHcrKxSEoaDYk8;rnKS|Q;IMP_Ms%`aV6-A@{+up~J^JJ7GBYePf>5IUx}-#Uq$V`# zj(Kff-|9rhWm@YN8`6L7;l=6uw-N zdZ%gSGAz78=Q_7k$tN@+nrp;}?eJ>;)$DKwAJh$rO3oE*E#%8{Co3WJXBZ=HXymHM z56QiKL#j+&?5n!To9>4LUfR)nolh>>4P|<;5tL+ll4U+ycPCQ88LxbTnj_K*^D?`y z$DjbKF9A8;I(3T%oXRVCI&pe8>GXKwIua|VUtoVQ7$u&%^Ec%K{~y*YjImB2El&*A z&l(JbAZ$P;t^rt@0W!CS0A+y?8;~&`PXa=Io~DdxflhcJ Date: Mon, 21 Aug 2023 17:51:42 -0600 Subject: [PATCH 34/35] upper bound data_II() issue fixed --- docs/src/quickguide.md | 3 +++ examples/run_examples.jl | 5 +---- src/constraints.jl | 4 ++-- src/solution.jl | 10 +++++++--- test/heuristics_tests.jl | 24 +++--------------------- test/log_tests.jl | 6 +----- 6 files changed, 17 insertions(+), 35 deletions(-) diff --git a/docs/src/quickguide.md b/docs/src/quickguide.md index d53608b..950f798 100644 --- a/docs/src/quickguide.md +++ b/docs/src/quickguide.md @@ -48,6 +48,9 @@ results = LOpt.run_LOpt(params, lopt_optimizer) !!! tip Run times of [LaplacianOpt](https://github.com/harshangrjn/LaplacianOpt.jl)'s mathematical optimization models are significantly faster using [Gurobi](https://www.gurobi.com) as the underlying mixed-integer programming (MIP) solver. Note that this solver's individual-usage license is available [free](https://www.gurobi.com/academia/academic-program-and-licenses/) for academic purposes. +!!! tip + Note that [LaplacianOpt](https://github.com/harshangrjn/LaplacianOpt.jl) tries to find the global solution of a combinatiorial optimization problem, that is known to be [NP-hard to compute](https://doi.org/10.1016/j.orl.2008.09.001) in the size of `num_nodes`. To obtain quick feasible solutions via "k-opt-based" heristics, just set `:solution_type` to `"heuristics"` and adjust the `num_swaps_bound_kopt` parameter to appropriate values, where larger value implies a better-quality feasible solution at the cost of the heuristic run time. + # Extracting results The run commands (for example, `run_LOpt`) in LaplacianOpt return detailed results in the form of a dictionary. This dictionary can be used for further processing of the results. For example, for the given instance of a complete graph, the algorithm's runtime and the optimal objective value (maximum algebraic connectivity) can be accessed with, diff --git a/examples/run_examples.jl b/examples/run_examples.jl index af7dce0..e49335f 100644 --- a/examples/run_examples.jl +++ b/examples/run_examples.jl @@ -57,12 +57,9 @@ params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_ # For more model options, check https://github.com/harshangrjn/LaplacianOpt.jl/blob/master/src/types.jl model_options = Dict{Symbol,Any}( - :eigen_cuts_sizes => [num_nodes, 2], + :eigen_cuts_sizes => [data_dict["num_nodes"], 2], :topology_flow_cuts => true, :solution_type => "optimal", - :kopt_parameter => 2, - :num_central_nodes_kopt => 5, - :num_swaps_bound_kopt => 1E4, ) result = LOpt.run_LOpt( diff --git a/src/constraints.jl b/src/constraints.jl index ab79b1d..1f13e69 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -93,8 +93,8 @@ function constraint_lazycallback_wrapper(lom::LaplacianOptModel; optimizer = not "Callback status: unknown - solution may not be integer feasible", ) - elseif status in - [MOI.CALLBACK_NODE_STATUS_INTEGER, MOI.CALLBACK_NODE_STATUS_FRACTIONAL] + elseif status in [MOI.CALLBACK_NODE_STATUS_INTEGER] + # [MOI.CALLBACK_NODE_STATUS_INTEGER, MOI.CALLBACK_NODE_STATUS_FRACTIONAL] if !(lom.options.formulation_type == "max_span_tree") if ( size(lom.options.eigen_cuts_sizes)[1] > 0 && diff --git a/src/solution.jl b/src/solution.jl index b1651a9..3a9acae 100644 --- a/src/solution.jl +++ b/src/solution.jl @@ -41,9 +41,13 @@ function build_LOModel_result(lom::LaplacianOptModel, solve_time::Number) "adjacency_augment_graph" => lom.data["adjacency_augment_graph"], ) - status = LOpt.optimality_certificate_MISDP(lom, result) - if status in [true, false] - result["optimality_certificate_MISDP"] = status + if lom.options.formulation_type == "max_λ2" + status = LOpt.optimality_certificate_MISDP(lom, result) + if status in [true, false] + result["optimality_certificate_MISDP"] = status + (status == false) && + (Memento.warn(_LOGGER, "Optimality certificate for MISDP failed!!!")) + end end return result diff --git a/test/heuristics_tests.jl b/test/heuristics_tests.jl index 320c48c..b93ff9d 100644 --- a/test/heuristics_tests.jl +++ b/test/heuristics_tests.jl @@ -10,7 +10,6 @@ :solution_type => "heuristic", :kopt_parameter => 1, :num_central_nodes_kopt => 5, - :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -44,7 +43,6 @@ end :solution_type => "heuristic", :kopt_parameter => 2, :num_central_nodes_kopt => 5, - :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -78,7 +76,6 @@ end :solution_type => "heuristic", :kopt_parameter => 3, :num_central_nodes_kopt => 5, - :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -106,7 +103,6 @@ end :solution_type => "heuristic", :kopt_parameter => 1, :num_central_nodes_kopt => 3, - :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -142,7 +138,6 @@ end :solution_type => "heuristic", :kopt_parameter => 2, :num_central_nodes_kopt => 3, - :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -178,7 +173,6 @@ end :solution_type => "heuristic", :kopt_parameter => 3, :num_central_nodes_kopt => 3, - :time_limit => test_time_limit(), ) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @@ -216,11 +210,7 @@ end params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) - model_options = Dict{Symbol,Any}( - :solution_type => "heuristic", - :kopt_parameter => 1, - :time_limit => test_time_limit(), - ) + model_options = Dict{Symbol,Any}(:solution_type => "heuristic", :kopt_parameter => 1) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" @@ -247,11 +237,7 @@ end params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) - model_options = Dict{Symbol,Any}( - :solution_type => "heuristic", - :kopt_parameter => 2, - :time_limit => test_time_limit(), - ) + model_options = Dict{Symbol,Any}(:solution_type => "heuristic", :kopt_parameter => 2) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" @@ -284,11 +270,7 @@ end params = Dict{String,Any}("data_dict" => data_dict, "augment_budget" => augment_budget) - model_options = Dict{Symbol,Any}( - :solution_type => "heuristic", - :kopt_parameter => 3, - :time_limit => test_time_limit(), - ) + model_options = Dict{Symbol,Any}(:solution_type => "heuristic", :kopt_parameter => 3) result = LaplacianOpt.run_LOpt(params, glpk_optimizer; options = model_options) @test result["solution_type"] == "heuristic" diff --git a/test/log_tests.jl b/test/log_tests.jl index 0cd2eb9..654c810 100644 --- a/test/log_tests.jl +++ b/test/log_tests.jl @@ -46,11 +46,7 @@ @test result_lopt["primal_status"] == MOI.FEASIBLE_POINT # Visualizing based on heuristic solution - model_options = Dict{Symbol,Any}( - :solution_type => "heuristic", - :kopt_parameter => 2, - :time_limit => test_time_limit(), - ) + model_options = Dict{Symbol,Any}(:solution_type => "heuristic", :kopt_parameter => 2) result_heuristic = LaplacianOpt.run_LOpt( params, glpk_optimizer; From 0765b23c8c820347b8c7e2a1a55aee25ddf11257 Mon Sep 17 00:00:00 2001 From: harshangrjn Date: Tue, 22 Aug 2023 07:32:27 -0600 Subject: [PATCH 35/35] some clean up for eigen cuts --- CHANGELOG.md | 3 ++- src/constraints.jl | 5 ++--- src/lopt_model.jl | 14 +++++--------- src/solution.jl | 2 +- src/utility.jl | 15 +++++++++++++-- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9365352..3cb52af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ LaplacianOpt.jl Change Log ### v0.6.0 - Added mutiple heuristics to handle both spanning trees and graphs with loops - Refactored `log.jl` to handle solutions from heuristics -- Included more used options for heuristic in `model_options` +- Included more user options for heuristic in `model_options` +- Cleaned up populating and logging of eigen cuts - Updated docs and unit tests to reflect above changes ### v0.5.0 diff --git a/src/constraints.jl b/src/constraints.jl index 1f13e69..37758d3 100644 --- a/src/constraints.jl +++ b/src/constraints.jl @@ -133,9 +133,8 @@ function constraint_lazycallback_wrapper(lom::LaplacianOptModel; optimizer = not end function constraint_eigen_cuts(W_val::Matrix{<:Number}, cb_cuts, lom::LaplacianOptModel) - if (size(lom.options.eigen_cuts_sizes)[1] > 0) && - (minimum(lom.options.eigen_cuts_sizes) >= 2) - for cut_size in sort(unique(lom.options.eigen_cuts_sizes), rev = true) + if (length(keys(lom.minor_idx_dict)) > 0) + for cut_size in sort(collect(keys(lom.minor_idx_dict)), rev = true) for i in lom.minor_idx_dict[cut_size] LOpt._add_eigen_cut_lazy(W_val, cb_cuts, lom, collect(i)) end diff --git a/src/lopt_model.jl b/src/lopt_model.jl index 56633c5..788aa8f 100644 --- a/src/lopt_model.jl +++ b/src/lopt_model.jl @@ -11,16 +11,16 @@ function build_LOModel(data::Dict{String,Any}; optimizer = nothing, options = no end end - LOpt._logging_info(lom) - if lom.options.formulation_type == "max_λ2" if lom.options.solution_type == "heuristic" + LOpt._logging_info(lom) return lom elseif lom.options.solution_type == "optimal" # Populate PMinor indices lom.minor_idx_dict = LOpt._PMinorIdx(lom.data["num_nodes"], lom.options.eigen_cuts_sizes) + LOpt._logging_info(lom) LOpt.variable_LOModel(lom) LOpt.constraint_LOModel(lom; optimizer = optimizer) @@ -192,12 +192,8 @@ end function _logging_info(lom::LaplacianOptModel) if lom.options.solution_type == "optimal" - if ( - size(lom.options.eigen_cuts_sizes)[1] > 0 && - minimum(lom.options.eigen_cuts_sizes) >= 2 && - maximum(lom.options.eigen_cuts_sizes) <= lom.data["num_nodes"] - ) - for k in lom.options.eigen_cuts_sizes + if length(keys(lom.minor_idx_dict)) > 0 + for k in keys(lom.minor_idx_dict) Memento.info(_LOGGER, "Applying eigen cuts ($(k)x$(k) matrix)") end end @@ -217,6 +213,6 @@ function _logging_info(lom::LaplacianOptModel) Memento.info(_LOGGER, "Applying topology flow cuts") end elseif lom.options.solution_type == "heuristic" - Memento.info(_LOGGER, "Applying heuristics to obtain a lower bound") + Memento.info(_LOGGER, "Applying heuristics to obtain a feasible solution") end end diff --git a/src/solution.jl b/src/solution.jl index 3a9acae..daf8ce3 100644 --- a/src/solution.jl +++ b/src/solution.jl @@ -46,7 +46,7 @@ function build_LOModel_result(lom::LaplacianOptModel, solve_time::Number) if status in [true, false] result["optimality_certificate_MISDP"] = status (status == false) && - (Memento.warn(_LOGGER, "Optimality certificate for MISDP failed!!!")) + (Memento.warn(_LOGGER, "Optimality certificate for MISDP failed!")) end end diff --git a/src/utility.jl b/src/utility.jl index 56b27b1..ec5dd93 100644 --- a/src/utility.jl +++ b/src/utility.jl @@ -325,8 +325,19 @@ end function _PMinorIdx(N::Int64, sizes::Vector{Int64}) minor_idx_dict = Dict{Int64,Vector{Tuple{Int64,Vararg{Int64}}}}() - for k in sizes - minor_idx_dict[k] = LOpt.get_minor_idx(N, k) + + if length(sizes) > 0 + if maximum(sizes) > N + Memento.warn( + _LOGGER, + "Detected maximum eigen-cut size ($(maximum(sizes))) > num_nodes", + ) + end + for k in unique(sizes) + if (k >= 2) && (k <= N) + minor_idx_dict[k] = LOpt.get_minor_idx(N, k) + end + end end return minor_idx_dict