From e005da830d9b0bb8ac3eb89892f1d4f1eea1905f Mon Sep 17 00:00:00 2001 From: Xuanzhao Gao Date: Mon, 25 Nov 2024 22:05:22 +0800 Subject: [PATCH] update xiao_2013 --- lib/OptimalBranchingMIS/Project.toml | 2 + .../src/OptimalBranchingMIS.jl | 3 + .../src/algorithms/xiao2013.jl | 45 ++--- .../src/algorithms/xiao2013_utils.jl | 78 -------- lib/OptimalBranchingMIS/src/graphs.jl | 6 +- lib/OptimalBranchingMIS/src/types.jl | 2 +- lib/OptimalBranchingMIS/test/graphs.jl | 1 + lib/OptimalBranchingMIS/test/mis.jl | 171 ++++++++++++++++++ lib/OptimalBranchingMIS/test/runtests.jl | 10 +- 9 files changed, 203 insertions(+), 115 deletions(-) create mode 100644 lib/OptimalBranchingMIS/test/mis.jl diff --git a/lib/OptimalBranchingMIS/Project.toml b/lib/OptimalBranchingMIS/Project.toml index a31af56..e3c73d9 100644 --- a/lib/OptimalBranchingMIS/Project.toml +++ b/lib/OptimalBranchingMIS/Project.toml @@ -4,11 +4,13 @@ authors = ["Xuanzhao Gao and contributors"] version = "1.0.0-DEV" [deps] +Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" EliminateGraphs = "b3ff564c-d3b6-11e9-0ef2-9b4ae9f9cbe1" GenericTensorNetworks = "3521c873-ad32-4bb4-b63d-f4f178f42b49" OptimalBranchingCore = "c76e7b22-e1d2-40e8-b0f1-f659837787b8" [compat] +Combinatorics = "1.0.2" EliminateGraphs = "0.2" GenericTensorNetworks = "2" julia = "1.6" diff --git a/lib/OptimalBranchingMIS/src/OptimalBranchingMIS.jl b/lib/OptimalBranchingMIS/src/OptimalBranchingMIS.jl index 0bf11b0..32af820 100644 --- a/lib/OptimalBranchingMIS/src/OptimalBranchingMIS.jl +++ b/lib/OptimalBranchingMIS/src/OptimalBranchingMIS.jl @@ -2,6 +2,7 @@ module OptimalBranchingMIS using OptimalBranchingCore using OptimalBranchingCore.BitBasis +using Combinatorics using EliminateGraphs, EliminateGraphs.Graphs using GenericTensorNetworks @@ -16,6 +17,8 @@ include("types.jl") include("graphs.jl") include("algorithms/mis1.jl") include("algorithms/mis2.jl") +include("algorithms/xiao2013.jl") +include("algorithms/xiao2013_utils.jl") include("reducer.jl") include("selector.jl") diff --git a/lib/OptimalBranchingMIS/src/algorithms/xiao2013.jl b/lib/OptimalBranchingMIS/src/algorithms/xiao2013.jl index 54e873f..e7a2767 100644 --- a/lib/OptimalBranchingMIS/src/algorithms/xiao2013.jl +++ b/lib/OptimalBranchingMIS/src/algorithms/xiao2013.jl @@ -1,46 +1,31 @@ -# the algorithm in Confining sets and avoiding bottleneck cases: A simple maximum independent set algorithm in degree-3 graphs -export mis_xiao2013, count_xiao2013 +export counting_xiao2013 """ - mis_xiao2013(g::SimpleGraph) + counting_xiao2013(g::SimpleGraph) -# Arguments -- `g::SimpleGraph`: The input graph for which the maximum independent set is to be calculated. - -# Returns -- `CountingMIS.mis`: The size of the MIS of a graph `g`. -""" -function mis_xiao2013(g::SimpleGraph) - gc = copy(g) - return _xiao2013(gc).mis -end - - -""" - count_xiao2013(g::SimpleGraph) - -Uses the branch and bound algorithm in xiao2013 to search for the size of the MIS of a graph `g`. +This function counts the maximum independent set (MIS) in a given simple graph using the Xiao 2013 algorithm. # Arguments -- `g::SimpleGraph`: The input graph for which the maximum independent set is to be calculated. +- `g::SimpleGraph`: A simple graph for which the maximum independent set is to be counted. # Returns -- `CountingMIS.count`: The number of branches generated during the entire process. +- `CountingMIS`: An object representing the size of the maximum independent set and the count of branches. + """ -function count_xiao2013(g::SimpleGraph) - gc = copy(g) - return _xiao2013(gc).count +function counting_xiao2013(g::SimpleGraph) + gc = copy(g) + return _xiao2013(gc) end function _xiao2013(g::SimpleGraph) if nv(g) == 0 - return CountingMIS(0) + return MISCount(0) elseif nv(g) == 1 - return CountingMIS(1) + return MISCount(1) elseif nv(g) == 2 - return CountingMIS(2 - has_edge(g, 1, 2)) + return MISCount(2 - has_edge(g, 1, 2)) else degrees = degree(g) degmin = minimum(degrees) @@ -48,11 +33,11 @@ function _xiao2013(g::SimpleGraph) if degmin == 0 all_zero_vertices = findall(==(0), degrees) - return length(all_zero_vertices) + _xiao2013(remove_vertex(g, all_zero_vertices)) + return length(all_zero_vertices) + _xiao2013(remove_vertices(g, all_zero_vertices)) elseif degmin == 1 - return 1 + _xiao2013(remove_vertex(g, neighbors(g, vmin) ∪ vmin)) + return 1 + _xiao2013(remove_vertices(g, neighbors(g, vmin) ∪ vmin)) elseif degmin == 2 - return 1 + _xiao2013(folding(g, vmin)) + return 1 + _xiao2013(folding(g, vmin)[1]) end # reduction rules diff --git a/lib/OptimalBranchingMIS/src/algorithms/xiao2013_utils.jl b/lib/OptimalBranchingMIS/src/algorithms/xiao2013_utils.jl index 5b14488..f727d0c 100644 --- a/lib/OptimalBranchingMIS/src/algorithms/xiao2013_utils.jl +++ b/lib/OptimalBranchingMIS/src/algorithms/xiao2013_utils.jl @@ -1,42 +1,3 @@ -""" - struct CountingMIS - -A struct representing a counting maximum independent set (MIS), where `mis` for size of the maximum independent set and `count` for the count of branches. - -# Fields -- `mis::Int`: The maximum independent set. -- `count::Int`: The count of the maximum independent set. - -# Constructors -- `CountingMIS(mis::Int)`: Constructs a CountingMIS object with the given maximum independent set and count set to 1. -- `CountingMIS(mis::Int, count::Int)`: Constructs a CountingMIS object with the given maximum independent set and count. - -# Examples -```jldoctest -julia> CountingMIS(2, 2) + 2 -CountingMIS(4, 2) - -julia> CountingMIS(1, 1) + CountingMIS(2, 2) -CountingMIS(3, 3) - -julia> max(CountingMIS(1, 1), CountingMIS(2, 2)) -CountingMIS(2, 3) -``` -""" -struct CountingMIS - mis::Int - count::Int - CountingMIS(mis::Int) = new(mis, 1) - CountingMIS(mis::Int, count::Int) = new(mis, count) -end - -Base.:+(x::CountingMIS, y::Int) = CountingMIS(x.mis + y, x.count) -Base.:+(x::Int, y::CountingMIS) = CountingMIS(x + y.mis, y.count) -Base.:+(x::CountingMIS, y::CountingMIS) = CountingMIS(x.mis + y.mis, x.count + y.count) - -Base.max(x::CountingMIS, y::CountingMIS) = CountingMIS(max(x.mis, y.mis), x.count + y.count) -Base.max(args...) = reduce(max, args) - struct Path vertices::Vector{Int} end @@ -80,45 +41,6 @@ function is_line_graph(g::SimpleGraph) return true end -function remove_vertex(g, v) - g, vs = induced_subgraph(g, setdiff(vertices(g), v)) - return g -end - -function remove_vertices(g, vertices) - gc = copy(g) - rem_vertices!(gc, vertices) - return gc -end - -function closed_neighbors(g::SimpleGraph, vertices::Vector{Int}) - return open_neighbors(g, vertices) ∪ vertices -end - -function open_neighbors(g::SimpleGraph, vertices::Vector{Int}) - ov = Vector{Int}() - for v in vertices - for n in neighbors(g, v) - push!(ov, n) - end - end - return unique!(setdiff(ov, vertices)) -end - -function folding(g::SimpleGraph, v) - @assert degree(g, v) == 2 - a, b = neighbors(g, v) - if !has_edge(g, a, b) - # apply the graph rewrite rule - add_vertex!(g) - nn = open_neighbors(g, [v, a, b]) - for n in nn - add_edge!(g, nv(g), n) - end - end - return remove_vertex(g, [v, a, b]) -end - function find_children(g::SimpleGraph, vertex_set::Vector{Int}) u_vertices = Int[] diff --git a/lib/OptimalBranchingMIS/src/graphs.jl b/lib/OptimalBranchingMIS/src/graphs.jl index c9b2ddc..7fd31d4 100644 --- a/lib/OptimalBranchingMIS/src/graphs.jl +++ b/lib/OptimalBranchingMIS/src/graphs.jl @@ -34,9 +34,9 @@ function removed_vertices(vertices::Vector{Int}, g::SimpleGraph, clause::Clause{ return unique!(rvs) end -function remove_vertices(g::SimpleGraph, vertices::Vector{Int}) - g_new, vmap = induced_subgraph(g, setdiff(1:nv(g), vertices)) - return g_new +function remove_vertices(g, v) + g, vs = induced_subgraph(g, setdiff(vertices(g), v)) + return g end """ diff --git a/lib/OptimalBranchingMIS/src/types.jl b/lib/OptimalBranchingMIS/src/types.jl index 505227c..e160365 100644 --- a/lib/OptimalBranchingMIS/src/types.jl +++ b/lib/OptimalBranchingMIS/src/types.jl @@ -14,7 +14,7 @@ Represents a Maximum Independent Set (MIS) problem. mutable struct MISProblem <: AbstractProblem g::SimpleGraph end -copy(p::MISProblem) = MISProblem(copy(p.g)) +Base.copy(p::MISProblem) = MISProblem(copy(p.g)) Base.show(io::IO, p::MISProblem) = print(io, "MISProblem($(nv(p.g)))") """ diff --git a/lib/OptimalBranchingMIS/test/graphs.jl b/lib/OptimalBranchingMIS/test/graphs.jl index d9a2298..879e540 100644 --- a/lib/OptimalBranchingMIS/test/graphs.jl +++ b/lib/OptimalBranchingMIS/test/graphs.jl @@ -1,3 +1,4 @@ +using OptimalBranchingCore using OptimalBranchingMIS, EliminateGraphs.Graphs using Test diff --git a/lib/OptimalBranchingMIS/test/mis.jl b/lib/OptimalBranchingMIS/test/mis.jl new file mode 100644 index 0000000..f669a5c --- /dev/null +++ b/lib/OptimalBranchingMIS/test/mis.jl @@ -0,0 +1,171 @@ +using EliminateGraphs, EliminateGraphs.Graphs +using Test + +using OptimalBranchingMIS: find_children, unconfined_vertices, is_line_graph, first_twin, twin_filter!, short_funnel_filter!, desk_filter!, effective_vertex, all_three_funnel, all_four_funnel, rho, optimal_four_cycle, optimal_vertex, has_fine_structure, count_o_path, closed_neighbors, is_complete_graph + +function graph_from_edges(edges) + return SimpleGraph(Graphs.SimpleEdge.(edges)) +end + +@testset "find_children" begin + g = graph_from_edges([(1,2),(2,3), (1,4), (2,5), (3,5)]) + @test find_children(g, [1]) == [2, 4] + @test find_children(g, [1,2,3]) == [4] +end + +@testset "line graph" begin + edges = [(1,2),(1,4),(1,5),(2,3),(2,4),(2,5),(3,4),(3,5)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test is_lg = is_line_graph(example_g) == true + + edges = [(1,2),(1,4),(1,5),(2,3),(2,4),(2,5),(3,4),(3,5),(4,5)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test is_lg = is_line_graph(example_g) == false +end + + +@testset "confined set and unconfined vertices" begin + # via dominated rule + g = graph_from_edges([(1,2),(1,3),(1, 4), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5)]) + @test unconfined_vertices(g) == [2] + + # via roof + g = graph_from_edges([(1, 2), (1, 5), (1, 6), (2, 5), (2, 3), (4, 5), (3, 4), (3, 7), (4, 7)]) + @test in(1, unconfined_vertices(g)) +end + + +@testset "twin" begin + # xiao2013 fig.2(a) + edges = [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (4, 5), (3, 6), (3, 7), (4, 8), (5, 9), (5, 10)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test first_twin(example_g) == (1, 2) + @test twin_filter!(example_g) + @test ne(example_g) == 0 + @test nv(example_g) == 5 + + #xiao2013 fig.2(b) + edges = [(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 6), (3, 7), (4, 8), (5, 9), (5, 10)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test first_twin(example_g) == (1, 2) + @test twin_filter!(example_g) + @test ne(example_g) == 5 + @test nv(example_g) == 6 +end + + + +@testset "short funnel" begin + edges = [(1, 2), (1, 4), (1, 5), (2, 3), (2, 6), (3, 6), (4, 6)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test short_funnel_filter!(example_g) + @test example_g == SimpleGraph{Int64}(5, [[2, 3, 4], [1, 3], [1, 2, 4], [1, 3]]) + + # xiao2013 fig.2(c) + edges = [(1, 2), (1, 3), (1, 4), (2, 5), (2, 6), (3, 4), (3, 7), (4, 5), (4, 8), (5, 10), (6, 9)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test short_funnel_filter!(example_g) + @test nv(example_g) == 8 + @test ne(example_g) == 9 +end + + +@testset "desk" begin + edges = [(1, 2), (1, 4), (1, 8), (2, 3), (2, 7), (3, 8), (5, 7), (6, 8), (7, 8)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test desk_filter!(example_g) + @test example_g == SimpleGraph{Int64}(4, [[2, 4], [1, 3], [2, 4], [1, 3]]) + + #xiao2013 fig.2(d) + edges = [(1, 2), (1, 4), (1, 5), (2, 3), (2, 6), (3, 4), (3, 5), (3, 7), (4, 8), (5, 9), (6, 10), (7, 11), (8, 12)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test desk_filter!(example_g) + @test nv(example_g) == 8 + @test ne(example_g) == 8 +end + + +@testset "effective vertex" begin + function is_effective_vertex(g::SimpleGraph, a::Int, S_a::Vector{Int}) + g_copy = copy(g) + rem_vertices!(g_copy, closed_neighbors(g, S_a)) + degree(g,a) == 3 && all(degree(g,n) == 3 for n in neighbors(g,a)) && rho(g) - rho(g_copy) >= 20 + end + + g = random_regular_graph(1000, 3) + a, S_a = effective_vertex(g) + @test is_effective_vertex(g, a, S_a) +end + +@testset "funnel" begin + function is_n_funnel(g::SimpleGraph, n::Int, a::Int, b::Int) + degree(g,a) == n && is_complete_graph(g, setdiff(neighbors(g,a), [b])) + end + + edges = [(1, 2), (1, 3), (1, 4), (3, 4)] + g = SimpleGraph(Graphs.SimpleEdge.(edges)) + three_funnels = all_three_funnel(g) + @test three_funnels == [(1, 2)] + @test is_n_funnel(g, 3, 1, 2) + + edges = [(1, 2), (1, 3), (1, 4), (1, 5), (3, 4), (3, 5), (4, 5)] + g = SimpleGraph(Graphs.SimpleEdge.(edges)) + four_funnels = all_four_funnel(g) + @test four_funnels == [(1, 2)] + @test is_n_funnel(g, 4, 1, 2) +end + +@testset "o_path" begin + edges = [(1, 2), (2, 3), (3, 4), (1, 5), (1, 6), (3, 7)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + o_path_num = count_o_path(example_g) + @test o_path_num == 1 + + edges = [(1, 2), (2, 3), (3, 4), (1, 5), (1, 6), (4, 7),(4, 8)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + o_path_num = count_o_path(example_g) + @test o_path_num == 0 +end + +@testset "fine_structure" begin + edges = [(1, 2), (2, 3), (3, 4), (1, 5), (1, 6), (3, 7)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test has_fine_structure(example_g) == true + + edges = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 7), (3, 8), (4, 5), (4, 6)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test has_fine_structure(example_g) == true + + edges = [(1, 2), (2, 3), (3, 4), (1, 5), (1, 6), (4, 7),(4, 8)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + @test has_fine_structure(example_g) == false +end + +@testset "four_cycle" begin + edges = [(1, 2), (2, 3), (3, 4), (4, 1), (1, 5)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + opt_quad = optimal_four_cycle(example_g) + @test opt_quad == [1, 2, 3, 4] + + edges = [(1, 2), (2, 3), (3, 4), (4, 1), (3, 5), (4, 6), (5, 6), (1, 7)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + opt_quad = optimal_four_cycle(example_g) + @test opt_quad == [1, 2, 3, 4] +end + +@testset "optimal vertex" begin + edges = [(1, 2), (2, 6), (1, 3), (3, 7), (1, 4), (4, 8), (1, 5), (5, 9)] + example_g = SimpleGraph(Graphs.SimpleEdge.(edges)) + v = optimal_vertex(example_g) + @test v == 1 +end + +@testset "xiao2013" begin + for seed in 10:2:100 + g = random_regular_graph(seed, 3) + eg = EliminateGraph(g) + mis_size_standard = mis2(eg) + mis_size = counting_xiao2013(g).mis_size + @test mis_size_standard == mis_size + end +end \ No newline at end of file diff --git a/lib/OptimalBranchingMIS/test/runtests.jl b/lib/OptimalBranchingMIS/test/runtests.jl index 144ee49..58c3b2a 100644 --- a/lib/OptimalBranchingMIS/test/runtests.jl +++ b/lib/OptimalBranchingMIS/test/runtests.jl @@ -1,10 +1,14 @@ using OptimalBranchingMIS using Test -@testset "branch" begin - include("branch.jl") -end +# @testset "branch" begin +# include("branch.jl") +# end @testset "graphs" begin include("graphs.jl") end + +@testset "mis" begin + include("mis.jl") +end