Skip to content

Commit

Permalink
Added maximum (weight) cliques (#21)
Browse files Browse the repository at this point in the history
* format

* include test

* Add to obj

---------

Co-authored-by: Guillaume Dalle <[email protected]>
  • Loading branch information
matbesancon and gdalle authored Apr 26, 2024
1 parent 2295e2e commit 1dd2f5e
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 0 deletions.
9 changes: 9 additions & 0 deletions docs/src/algorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ 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$

## Maximum weight clique

```@docs
maximum_weight_clique
GraphsOptim.maximum_weight_clique!
```

A *clique* is a subset $S \subset V$ of vertices of an undirected graph $G = (V,E)$ such that $\forall (u,v) \in S: (u, v) \in E$. We search for the clique maximizing the total weight of selected vertices.

## Maximum Weight Independent Set

```@docs
Expand Down
2 changes: 2 additions & 0 deletions src/GraphsOptim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export min_vertex_cover
export maximum_weight_independent_set
export fractional_chromatic_number, fractional_clique_number
export shortest_path
export maximum_weight_clique

include("utils.jl")
include("flow.jl")
Expand All @@ -35,6 +36,7 @@ include("graph_matching.jl")
include("min_vertex_cover.jl")
include("fractional_coloring.jl")
include("shortest_path.jl")
include("maximum_clique.jl")
include("independent_set.jl")

end
49 changes: 49 additions & 0 deletions src/maximum_clique.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

"""
maximum_weight_clique!(model, g; var_name)
Computes in-place in the JuMP model a maximum-weighted clique of `g`.
An optional `vertex_weights` vector can be passed to the graph, defaulting to uniform weights (computing a maximum size clique).
"""
function maximum_weight_clique!(
model::Model, g::AbstractGraph; binary::Bool=true, var_name, vertex_weights=ones(nv(g))
)
if is_directed(g)
throw(ArgumentError("The graph must not be directed"))
end
g_vertices = collect(vertices(g))
f = @variable(model, [g_vertices]; binary=binary, base_name=String(var_name))
model[Symbol(var_name)] = f
@constraint(
model,
packing_constraint[i=1:nv(g), j=1:nv(g); i j && !has_edge(g, i, j)],
f[i] + f[j] <= 1,
)
obj = objective_function(model)
add_to_expression!(obj, dot(f, vertex_weights))
@objective(model, Max, obj)
return model
end

"""
maximum_weight_clique(g; optimizer, binary, vertex_weights)
Computes a maximum-weighted clique of `g`.
"""
function maximum_weight_clique(
g::AbstractGraph;
binary::Bool=true,
vertex_weights=ones(nv(g)),
optimizer=HiGHS.Optimizer,
)
model = Model(optimizer)
set_silent(model)
maximum_weight_clique!(
model, g; binary=binary, vertex_weights=vertex_weights, var_name=:clique
)
optimize!(model)
@assert termination_status(model) == OPTIMAL
clique_variables = Vector(model[:clique])
clique_vertices = findall(v -> value(v) > 0.5, clique_variables)
return clique_vertices
end
21 changes: 21 additions & 0 deletions test/maximum_clique.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using GraphsOptim
using Graphs
using Test

g = Graphs.random_regular_graph(10, 5)

for _ in 1:10
vertex_weights = rand(nv(g))
clique = GraphsOptim.maximum_weight_clique(g; vertex_weights=vertex_weights)
if length(clique) > 1
for idx in 1:(length(clique) - 1)
@test Graphs.has_edge(g, clique[idx], clique[idx + 1])
end
end
end

g2 = complete_graph(3)
add_vertex!(g2)
add_edge!(g2, 3, 4)
clique = GraphsOptim.maximum_weight_clique(g2)
@test sort(clique) == 1:3
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ using Test
include("min_vertex_cover.jl")
end

@testset verbose = true "Cliques" begin
include("maximum_clique.jl")
end

@testset verbose = true "Independent set" begin
include("independent_set.jl")
end
Expand Down

0 comments on commit 1dd2f5e

Please sign in to comment.