From 134d8f01837bfb7c70e4d811bc1fb64741509c50 Mon Sep 17 00:00:00 2001 From: Oleg Fafurin Date: Wed, 25 Oct 2023 17:54:12 +0200 Subject: [PATCH 1/6] add vertex cover --- src/GraphsOptim.jl | 2 ++ src/vertex_cover.jl | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/vertex_cover.jl diff --git a/src/GraphsOptim.jl b/src/GraphsOptim.jl index 62e58d4..ad7fc45 100644 --- a/src/GraphsOptim.jl +++ b/src/GraphsOptim.jl @@ -21,10 +21,12 @@ using OptimalTransport: sinkhorn export min_cost_flow export min_cost_assignment export FAQ, GOAT, graph_matching +export vertex_cover include("utils.jl") include("flow.jl") include("assignment.jl") include("graph_matching.jl") +include("vertex_cover.jl") end diff --git a/src/vertex_cover.jl b/src/vertex_cover.jl new file mode 100644 index 0000000..ddab856 --- /dev/null +++ b/src/vertex_cover.jl @@ -0,0 +1,56 @@ +function vertex_cover!( + model::Model, + g::AbstractGraph; + var_name, +) + if is_directed(g) + throw(ArgumentError("The graph must not be directed")) + end + + ver = collect(vertices(g)) + edge_tuples = [(src(ed), dst(ed)) for ed in edges(g)] + + f = @variable(model, [ver]; integer=true, base_name=String(var_name)) + model[Symbol(var_name)] = f + + for v in ver + @constraint(model, f[v] >= 0) + @constraint(model, f[v] <= 1) + end + + for (u,v) in edge_tuples + @constraint(model, f[u] + f[v] >= 1) + end + + obj = objective_function(model) + for v in ver + add_to_expression!(obj, f[v]) + end + @objective(model, Min, obj) + + return model +end + + + +function vertex_cover( + g::AbstractGraph, + optimizer=HiGHS.Optimizer, +) + model = Model(optimizer) + set_silent(model) + + vertex_cover!( + model, + g; + var_name=:cover, + ) + + optimize!(model) + @assert termination_status(model) == OPTIMAL + + cover_values = Vector(value.(model[:cover])) + cover_vertices = findall(==(1), cover_values) + + return cover_vertices +end From 71f1bf76adb82f01376616bfa20b13e406a69d69 Mon Sep 17 00:00:00 2001 From: Oleg Fafurin Date: Fri, 27 Oct 2023 16:57:10 +0200 Subject: [PATCH 2/6] add vertex cover --- src/vertex_cover.jl | 28 ++++++++++++++++++++++++++-- test/runtests.jl | 4 ++++ test/vertex_cover.jl | 19 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/vertex_cover.jl diff --git a/src/vertex_cover.jl b/src/vertex_cover.jl index ddab856..22442fe 100644 --- a/src/vertex_cover.jl +++ b/src/vertex_cover.jl @@ -1,3 +1,13 @@ +""" + vertex_cover!( + model, g; + var_name + ) + +Modify a JuMP model by adding the variable, constraints and objective necessary to compute a minimum vertex cove of an undirected graph. + +The vertex cover indicator variable will be named `var_name` +""" function vertex_cover!( model::Model, g::AbstractGraph; @@ -32,9 +42,23 @@ function vertex_cover!( end +""" + vertex_cover( + g, optimizer + ) + +Compute a minimum vertex cover of an undirected graph. +# Arguments + +- `g::Graphs.AbstractGraph`: an undirected graph `G = (V, E)` + +# Keyword arguments + +- `optimizer`: JuMP-compatible solver (default is `HiGHS.Optimizer`) +""" function vertex_cover( - g::AbstractGraph, + g::AbstractGraph; optimizer=HiGHS.Optimizer, ) model = Model(optimizer) @@ -53,4 +77,4 @@ function vertex_cover( cover_vertices = findall(==(1), cover_values) return cover_vertices -end +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 5e19bc2..929598b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -39,4 +39,8 @@ using Test @testset verbose = true "Graph matching" begin include("graph_matching.jl") end + + @testset verbose = true "Vertex cover" begin + include("vertex_cover.jl") + end end; diff --git a/test/vertex_cover.jl b/test/vertex_cover.jl new file mode 100644 index 0000000..5be847a --- /dev/null +++ b/test/vertex_cover.jl @@ -0,0 +1,19 @@ +using Graphs: SimpleGraph +using GraphsOptim +using Test + + +adj_matrix = [ + 0 1 1 0 0 0 + 1 0 1 1 1 1 + 1 1 0 0 0 0 + 0 1 0 0 0 0 + 0 1 0 0 0 0 + 0 1 0 0 0 0 +] + +expected_ans_options = Set([Set([1,2]), Set([2,3])]) + +graph = SimpleGraph(adj_matrix) + +@test Set(vertex_cover) in expected_ans_options \ No newline at end of file From 06b40e0f5cb1568254b3957b35dc1b9f286d92cb Mon Sep 17 00:00:00 2001 From: Oleg Fafurin Date: Fri, 27 Oct 2023 17:11:52 +0200 Subject: [PATCH 3/6] fix test and rename method --- src/GraphsOptim.jl | 4 ++-- src/{vertex_cover.jl => min_vertex_cover.jl} | 6 +++--- test/{vertex_cover.jl => min_vertex_cover.jl} | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/{vertex_cover.jl => min_vertex_cover.jl} (95%) rename test/{vertex_cover.jl => min_vertex_cover.jl} (81%) diff --git a/src/GraphsOptim.jl b/src/GraphsOptim.jl index ad7fc45..374548d 100644 --- a/src/GraphsOptim.jl +++ b/src/GraphsOptim.jl @@ -21,12 +21,12 @@ using OptimalTransport: sinkhorn export min_cost_flow export min_cost_assignment export FAQ, GOAT, graph_matching -export vertex_cover +export min_vertex_cover include("utils.jl") include("flow.jl") include("assignment.jl") include("graph_matching.jl") -include("vertex_cover.jl") +include("min_vertex_cover.jl") end diff --git a/src/vertex_cover.jl b/src/min_vertex_cover.jl similarity index 95% rename from src/vertex_cover.jl rename to src/min_vertex_cover.jl index 22442fe..b08101b 100644 --- a/src/vertex_cover.jl +++ b/src/min_vertex_cover.jl @@ -8,7 +8,7 @@ Modify a JuMP model by adding the variable, constraints and objective necessary The vertex cover indicator variable will be named `var_name` """ -function vertex_cover!( +function min_vertex_cover!( model::Model, g::AbstractGraph; var_name, @@ -57,14 +57,14 @@ Compute a minimum vertex cover of an undirected graph. - `optimizer`: JuMP-compatible solver (default is `HiGHS.Optimizer`) """ -function vertex_cover( +function min_vertex_cover( g::AbstractGraph; optimizer=HiGHS.Optimizer, ) model = Model(optimizer) set_silent(model) - vertex_cover!( + min_vertex_cover!( model, g; var_name=:cover, diff --git a/test/vertex_cover.jl b/test/min_vertex_cover.jl similarity index 81% rename from test/vertex_cover.jl rename to test/min_vertex_cover.jl index 5be847a..e5189ee 100644 --- a/test/vertex_cover.jl +++ b/test/min_vertex_cover.jl @@ -16,4 +16,4 @@ expected_ans_options = Set([Set([1,2]), Set([2,3])]) graph = SimpleGraph(adj_matrix) -@test Set(vertex_cover) in expected_ans_options \ No newline at end of file +@test Set(min_vertex_cover(graph)) in expected_ans_options \ No newline at end of file From c9df7bc3552d4ecea84eead8ee238104bcaa44c1 Mon Sep 17 00:00:00 2001 From: Oleg Fafurin Date: Fri, 27 Oct 2023 17:19:47 +0200 Subject: [PATCH 4/6] docs --- docs/src/algorithms.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/src/algorithms.md b/docs/src/algorithms.md index 15335df..2e35a1c 100644 --- a/docs/src/algorithms.md +++ b/docs/src/algorithms.md @@ -42,6 +42,14 @@ where the incoming flow $f^-(v)$ and outgoing flow $f^+(v)$ are defined as f^-(v) = \sum_{u \in N^-(v)} f(u, v) \quad \text{and} \quad f^+(v) = \sum_{w \in N^+(v)} f(v, w) ``` +## Minimum Vertex Cover + +```@docs +min_vertex_cover +``` +Minumum vertex cover algorithm finds a subset $S \subset V$ of vertices of an undirected graph $G = (V,E)$ such that $\forall (u,v) \in E: u \in S \lor v \in S$ + + ## Assignment !!! danger "Work in progress" From 645e3fc4dcb832752ef92febf68b064c0cc9ff2f Mon Sep 17 00:00:00 2001 From: Oleg Fafurin Date: Fri, 10 Nov 2023 15:05:00 +0100 Subject: [PATCH 5/6] fix docs + small fixes --- src/GraphsOptim.jl | 2 +- src/{vertex_cover.jl => min_vertex_cover.jl} | 17 ++++++++--------- test/{vertex_cover.jl => min_vertex_cover.jl} | 2 +- test/runtests.jl | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) rename src/{vertex_cover.jl => min_vertex_cover.jl} (72%) rename test/{vertex_cover.jl => min_vertex_cover.jl} (82%) diff --git a/src/GraphsOptim.jl b/src/GraphsOptim.jl index e1d0312..dc03c46 100644 --- a/src/GraphsOptim.jl +++ b/src/GraphsOptim.jl @@ -31,7 +31,7 @@ include("utils.jl") include("flow.jl") include("assignment.jl") include("graph_matching.jl") -include("vertex_cover.jl") +include("min_vertex_cover.jl") include("fractional_coloring.jl") include("shortest_path.jl") diff --git a/src/vertex_cover.jl b/src/min_vertex_cover.jl similarity index 72% rename from src/vertex_cover.jl rename to src/min_vertex_cover.jl index 587f8ec..a92a47e 100644 --- a/src/vertex_cover.jl +++ b/src/min_vertex_cover.jl @@ -1,5 +1,5 @@ """ - vertex_cover!( + min_vertex_cover!( model, g; var_name ) @@ -8,15 +8,14 @@ Modify a JuMP model by adding the variable, constraints and objective necessary The vertex cover indicator variable will be named `var_name` """ -function min_vertex_cover!(model::Model, g::AbstractGraph; var_name) +function min_vertex_cover!(model::Model, g::AbstractGraph; integer::Bool, var_name) if is_directed(g) throw(ArgumentError("The graph must not be directed")) end ver = collect(vertices(g)) - edge_tuples = [(src(ed), dst(ed)) for ed in edges(g)] - f = @variable(model, [ver]; integer=true, base_name=String(var_name)) + f = @variable(model, [ver]; integer=integer, base_name=String(var_name)) model[Symbol(var_name)] = f for v in ver @@ -24,8 +23,8 @@ function min_vertex_cover!(model::Model, g::AbstractGraph; var_name) @constraint(model, f[v] <= 1) end - for (u, v) in edge_tuples - @constraint(model, f[u] + f[v] >= 1) + for e in edges(g) + @constraint(model, f[src(e)] + f[dst(e)] >= 1) end obj = objective_function(model) @@ -38,7 +37,7 @@ function min_vertex_cover!(model::Model, g::AbstractGraph; var_name) end """ - vertex_cover( + min_vertex_cover( g, optimizer ) @@ -52,11 +51,11 @@ Compute a minimum vertex cover of an undirected graph. - `optimizer`: JuMP-compatible solver (default is `HiGHS.Optimizer`) """ -function min_vertex_cover(g::AbstractGraph; optimizer=HiGHS.Optimizer) +function min_vertex_cover(g::AbstractGraph; integer::Bool=true, optimizer=HiGHS.Optimizer) model = Model(optimizer) set_silent(model) - min_vertex_cover!(model, g; var_name=:cover) + min_vertex_cover!(model, g; integer=integer, var_name=:cover) optimize!(model) @assert termination_status(model) == OPTIMAL diff --git a/test/vertex_cover.jl b/test/min_vertex_cover.jl similarity index 82% rename from test/vertex_cover.jl rename to test/min_vertex_cover.jl index 6d1c68f..1a5b77e 100644 --- a/test/vertex_cover.jl +++ b/test/min_vertex_cover.jl @@ -11,7 +11,7 @@ adj_matrix = [ 0 1 0 0 0 0 ] -expected_ans_options = Set([Set([1, 2]), Set([2, 3])]) +expected_ans_options = [Set([1, 2]), Set([2, 3])] graph = SimpleGraph(adj_matrix) diff --git a/test/runtests.jl b/test/runtests.jl index eca05a0..8811777 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -41,7 +41,7 @@ using Test end @testset verbose = true "Vertex cover" begin - include("vertex_cover.jl") + include("min_vertex_cover.jl") end @testset verbose = true "Fractional coloring" begin From d3a88ef45af3809e425c85ebdad1c09dd8d2dfb6 Mon Sep 17 00:00:00 2001 From: Oleg Fafurin Date: Fri, 10 Nov 2023 15:30:12 +0100 Subject: [PATCH 6/6] fix missing docstring --- docs/src/algorithms.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/algorithms.md b/docs/src/algorithms.md index 3ffd096..ad088f9 100644 --- a/docs/src/algorithms.md +++ b/docs/src/algorithms.md @@ -65,6 +65,7 @@ GraphsOptim.min_cost_assignment! ```@docs min_vertex_cover +GraphsOptim.min_vertex_cover! ``` Finds a subset $S \subset V$ of vertices of an undirected graph $G = (V,E)$ such that $\forall (u,v) \in E: u \in S \lor v \in S$