Skip to content

Commit

Permalink
update xiao_2013
Browse files Browse the repository at this point in the history
  • Loading branch information
ArrogantGao committed Nov 25, 2024
1 parent 1852e02 commit e005da8
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 115 deletions.
2 changes: 2 additions & 0 deletions lib/OptimalBranchingMIS/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ authors = ["Xuanzhao Gao <[email protected]> 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"
Expand Down
3 changes: 3 additions & 0 deletions lib/OptimalBranchingMIS/src/OptimalBranchingMIS.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module OptimalBranchingMIS

using OptimalBranchingCore
using OptimalBranchingCore.BitBasis
using Combinatorics
using EliminateGraphs, EliminateGraphs.Graphs
using GenericTensorNetworks

Expand All @@ -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")
Expand Down
45 changes: 15 additions & 30 deletions lib/OptimalBranchingMIS/src/algorithms/xiao2013.jl
Original file line number Diff line number Diff line change
@@ -1,58 +1,43 @@
# 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)
vmin = findfirst(==(degmin), degrees)

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
Expand Down
78 changes: 0 additions & 78 deletions lib/OptimalBranchingMIS/src/algorithms/xiao2013_utils.jl
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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[]

Expand Down
6 changes: 3 additions & 3 deletions lib/OptimalBranchingMIS/src/graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

"""
Expand Down
2 changes: 1 addition & 1 deletion lib/OptimalBranchingMIS/src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)))")

"""
Expand Down
1 change: 1 addition & 0 deletions lib/OptimalBranchingMIS/test/graphs.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using OptimalBranchingCore
using OptimalBranchingMIS, EliminateGraphs.Graphs
using Test

Expand Down
171 changes: 171 additions & 0 deletions lib/OptimalBranchingMIS/test/mis.jl
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit e005da8

Please sign in to comment.