Skip to content

Commit

Permalink
added fractional_chromatic_number, fractional_clique_number (#13)
Browse files Browse the repository at this point in the history
* added fractional_chromatic_number, fractional_clique_number

* fractional_coloring: use HiGHS rather than GLPK

* fractional_coloring: various fixes

* Add quotes

* format

---------

Co-authored-by: Guillaume Dalle <[email protected]>
  • Loading branch information
dstahlke and gdalle authored Oct 27, 2023
1 parent 22cb219 commit d87b65d
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
Expand All @@ -38,4 +39,4 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Aqua", "Documenter", "Graphs", "HiGHS", "JET", "JuMP", "JuliaFormatter", "LinearAlgebra", "Test", "SparseArrays"]
test = ["Aqua", "Documenter", "Graphs", "HiGHS", "IterTools", "JET", "JuMP", "JuliaFormatter", "LinearAlgebra", "Test", "SparseArrays"]
8 changes: 8 additions & 0 deletions docs/src/algorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ graph_matching
GraphsOptim.graph_matching_step_size
```

## Coloring

```@docs
fractional_chromatic_number
fractional_clique_number
```

## Utils

```@docs
Expand All @@ -80,4 +87,5 @@ GraphsOptim.is_stochastic
GraphsOptim.is_doubly_stochastic
GraphsOptim.is_permutation_matrix
GraphsOptim.flat_doubly_stochastic
GraphsOptim.indvec
```
4 changes: 4 additions & 0 deletions src/GraphsOptim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ module GraphsOptim

using Graphs: AbstractGraph, is_directed
using Graphs: vertices, edges, nv, ne, src, dst, inneighbors, outneighbors
using Graphs: complement, maximal_cliques
using FillArrays: Zeros, Ones, Fill
using HiGHS: HiGHS
using JuMP: Model, AffExpr
using JuMP: objective_function, add_to_expression!
using JuMP: set_silent, optimize!, termination_status, value
using JuMP: set_optimizer, objective_value
using JuMP: @variable, @constraint, @objective
using LinearAlgebra: norm, tr, dot
using MathOptInterface: OPTIMAL
Expand All @@ -21,12 +23,14 @@ using OptimalTransport: sinkhorn
export min_cost_flow
export min_cost_assignment
export FAQ, GOAT, graph_matching
export fractional_chromatic_number, fractional_clique_number
export shortest_path

include("utils.jl")
include("flow.jl")
include("assignment.jl")
include("graph_matching.jl")
include("fractional_coloring.jl")
include("shortest_path.jl")

end
66 changes: 66 additions & 0 deletions src/fractional_coloring.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
fractional_chromatic_number(g; optimizer)
Compute the fractional chromatic number of a graph. Gives the same result as
`fractional_clique_number`, though one function may run faster than the other.
Beware: this can run very slowly for graphs of any substantial size.
# Keyword arguments
- `optimizer`: JuMP-compatible solver (default is `HiGHS.Optimizer`)
# References
- https://mathworld.wolfram.com/FractionalChromaticNumber.html
"""
function fractional_chromatic_number(
g::AbstractGraph{T}, optimizer=HiGHS.Optimizer
) where {T<:Integer}
if is_directed(g)
throw(ArgumentError("The graph must not be directed"))
end

ss = maximal_cliques(complement(g))
M = hcat(indvec.(ss, nv(g))...)

model = Model(optimizer)
set_silent(model)
@variable(model, x[1:length(ss)] >= 0)
@constraint(model, M * x .>= 1)
@objective(model, Min, sum(x))
optimize!(model)
return objective_value(model)
end

"""
fractional_clique_number(g; optimizer)
Compute the fractional clique number of a graph. Gives the same result as
`fractional_chromatic_number`, though one function may run faster than the other.
Beware: this can run very slowly for graphs of any substantial size.
# Keyword arguments
- `optimizer`: JuMP-compatible solver (default is `HiGHS.Optimizer`)
# References
- https://mathworld.wolfram.com/FractionalCliqueNumber.html
"""
function fractional_clique_number(
g::AbstractGraph{T}, optimizer=HiGHS.Optimizer
) where {T<:Integer}
if is_directed(g)
throw(ArgumentError("The graph must not be directed"))
end

model = Model(optimizer)
set_silent(model)
@variable(model, x[1:nv(g)] >= 0)
for clique in maximal_cliques(complement(g))
@constraint(model, sum(x[clique]) <= 1)
end
@objective(model, Max, sum(x))
optimize!(model)
return objective_value(model)
end
11 changes: 11 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,14 @@ end
Return the barycenter of doubly stochastic matrices `J = 𝟏 * 𝟏ᵀ / n`.
"""
flat_doubly_stochastic(n::Integer) = ones(n) * ones(n)' / n

"""
indvec(s, n)
Return a vector of length `n` with ones at indices specified by `s`.
"""
function indvec(s::AbstractVector, n::Integer)
x = zeros(n)
x[s] .= 1
return x
end
12 changes: 12 additions & 0 deletions test/fractional_coloring.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Graphs
using GraphsOptim
using IterTools
using Test

function kneser_graph(n::Integer, k::Integer)
ss = collect(subsets(1:n, k))
return SimpleGraph([isdisjoint(a, b) for a in ss, b in ss])
end

@test fractional_chromatic_number(kneser_graph(8, 3)) 8 / 3
@test fractional_clique_number(kneser_graph(8, 3)) 8 / 3
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ using Test
include("graph_matching.jl")
end

@testset verbose = true "Fractional coloring" begin
include("fractional_coloring.jl")
end

@testset verbose = true "Shortest path" begin
include("shortest_path.jl")
end
Expand Down

0 comments on commit d87b65d

Please sign in to comment.