Skip to content

Commit d87b65d

Browse files
dstahlkegdalle
andauthored
added fractional_chromatic_number, fractional_clique_number (#13)
* 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 <22795598+gdalle@users.noreply.github.com>
1 parent 22cb219 commit d87b65d

File tree

7 files changed

+107
-1
lines changed

7 files changed

+107
-1
lines changed

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
3030
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
3131
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
3232
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
33+
IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
3334
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
3435
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
3536
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
@@ -38,4 +39,4 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
3839
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3940

4041
[targets]
41-
test = ["Aqua", "Documenter", "Graphs", "HiGHS", "JET", "JuMP", "JuliaFormatter", "LinearAlgebra", "Test", "SparseArrays"]
42+
test = ["Aqua", "Documenter", "Graphs", "HiGHS", "IterTools", "JET", "JuMP", "JuliaFormatter", "LinearAlgebra", "Test", "SparseArrays"]

docs/src/algorithms.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ graph_matching
7171
GraphsOptim.graph_matching_step_size
7272
```
7373

74+
## Coloring
75+
76+
```@docs
77+
fractional_chromatic_number
78+
fractional_clique_number
79+
```
80+
7481
## Utils
7582

7683
```@docs
@@ -80,4 +87,5 @@ GraphsOptim.is_stochastic
8087
GraphsOptim.is_doubly_stochastic
8188
GraphsOptim.is_permutation_matrix
8289
GraphsOptim.flat_doubly_stochastic
90+
GraphsOptim.indvec
8391
```

src/GraphsOptim.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ module GraphsOptim
77

88
using Graphs: AbstractGraph, is_directed
99
using Graphs: vertices, edges, nv, ne, src, dst, inneighbors, outneighbors
10+
using Graphs: complement, maximal_cliques
1011
using FillArrays: Zeros, Ones, Fill
1112
using HiGHS: HiGHS
1213
using JuMP: Model, AffExpr
1314
using JuMP: objective_function, add_to_expression!
1415
using JuMP: set_silent, optimize!, termination_status, value
16+
using JuMP: set_optimizer, objective_value
1517
using JuMP: @variable, @constraint, @objective
1618
using LinearAlgebra: norm, tr, dot
1719
using MathOptInterface: OPTIMAL
@@ -21,12 +23,14 @@ using OptimalTransport: sinkhorn
2123
export min_cost_flow
2224
export min_cost_assignment
2325
export FAQ, GOAT, graph_matching
26+
export fractional_chromatic_number, fractional_clique_number
2427
export shortest_path
2528

2629
include("utils.jl")
2730
include("flow.jl")
2831
include("assignment.jl")
2932
include("graph_matching.jl")
33+
include("fractional_coloring.jl")
3034
include("shortest_path.jl")
3135

3236
end

src/fractional_coloring.jl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
fractional_chromatic_number(g; optimizer)
3+
4+
Compute the fractional chromatic number of a graph. Gives the same result as
5+
`fractional_clique_number`, though one function may run faster than the other.
6+
Beware: this can run very slowly for graphs of any substantial size.
7+
8+
# Keyword arguments
9+
10+
- `optimizer`: JuMP-compatible solver (default is `HiGHS.Optimizer`)
11+
12+
# References
13+
14+
- https://mathworld.wolfram.com/FractionalChromaticNumber.html
15+
"""
16+
function fractional_chromatic_number(
17+
g::AbstractGraph{T}, optimizer=HiGHS.Optimizer
18+
) where {T<:Integer}
19+
if is_directed(g)
20+
throw(ArgumentError("The graph must not be directed"))
21+
end
22+
23+
ss = maximal_cliques(complement(g))
24+
M = hcat(indvec.(ss, nv(g))...)
25+
26+
model = Model(optimizer)
27+
set_silent(model)
28+
@variable(model, x[1:length(ss)] >= 0)
29+
@constraint(model, M * x .>= 1)
30+
@objective(model, Min, sum(x))
31+
optimize!(model)
32+
return objective_value(model)
33+
end
34+
35+
"""
36+
fractional_clique_number(g; optimizer)
37+
38+
Compute the fractional clique number of a graph. Gives the same result as
39+
`fractional_chromatic_number`, though one function may run faster than the other.
40+
Beware: this can run very slowly for graphs of any substantial size.
41+
42+
# Keyword arguments
43+
44+
- `optimizer`: JuMP-compatible solver (default is `HiGHS.Optimizer`)
45+
46+
# References
47+
48+
- https://mathworld.wolfram.com/FractionalCliqueNumber.html
49+
"""
50+
function fractional_clique_number(
51+
g::AbstractGraph{T}, optimizer=HiGHS.Optimizer
52+
) where {T<:Integer}
53+
if is_directed(g)
54+
throw(ArgumentError("The graph must not be directed"))
55+
end
56+
57+
model = Model(optimizer)
58+
set_silent(model)
59+
@variable(model, x[1:nv(g)] >= 0)
60+
for clique in maximal_cliques(complement(g))
61+
@constraint(model, sum(x[clique]) <= 1)
62+
end
63+
@objective(model, Max, sum(x))
64+
optimize!(model)
65+
return objective_value(model)
66+
end

src/utils.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,14 @@ end
4848
Return the barycenter of doubly stochastic matrices `J = 𝟏 * 𝟏ᵀ / n`.
4949
"""
5050
flat_doubly_stochastic(n::Integer) = ones(n) * ones(n)' / n
51+
52+
"""
53+
indvec(s, n)
54+
55+
Return a vector of length `n` with ones at indices specified by `s`.
56+
"""
57+
function indvec(s::AbstractVector, n::Integer)
58+
x = zeros(n)
59+
x[s] .= 1
60+
return x
61+
end

test/fractional_coloring.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Graphs
2+
using GraphsOptim
3+
using IterTools
4+
using Test
5+
6+
function kneser_graph(n::Integer, k::Integer)
7+
ss = collect(subsets(1:n, k))
8+
return SimpleGraph([isdisjoint(a, b) for a in ss, b in ss])
9+
end
10+
11+
@test fractional_chromatic_number(kneser_graph(8, 3)) 8 / 3
12+
@test fractional_clique_number(kneser_graph(8, 3)) 8 / 3

test/runtests.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ using Test
4040
include("graph_matching.jl")
4141
end
4242

43+
@testset verbose = true "Fractional coloring" begin
44+
include("fractional_coloring.jl")
45+
end
46+
4347
@testset verbose = true "Shortest path" begin
4448
include("shortest_path.jl")
4549
end

0 commit comments

Comments
 (0)