Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ export
egonet,
merge_vertices!,
merge_vertices,
mycielski!,
mycielski,

# bfs
gdistances,
Expand Down
98 changes: 98 additions & 0 deletions src/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1105,3 +1105,101 @@ function merge_vertices!(g::Graph{T}, vs::Vector{U} where {U<:Integer}) where {T

return new_vertex_ids
end

"""
mycielski!(g; iterations = 1)

Performs the mycielski operation on an input graph, in place, for a given number of iterations.
See [`mycielski`](@ref) for the details of the operation.

### Implementation Notes
Operates in place and expects `g` to not have self loops.
"""
function mycielski!(g::SimpleGraph; iterations::Integer=1)
has_self_loops(g) && return g

for _ in Base.OneTo(iterations)
N = nv(g)

add_vertices!(g, N + 1)
w = nv(g)

for u in Base.OneTo(N)
for v in neighbors(g, u)
if v <= N && u < v
add_edge!(g, u, v + N)
add_edge!(g, v, u + N)
end
end
end

for v in 1:N
add_edge!(g, v + N, w)
end
end
return g
end

"""
mycielski(g; iterations = 1)

Returns a graph after applying the Mycielski operator to the input for a given number of
iterations. The Mycielski operator returns a new graph with `2n+1` vertices and `3e+n`
edges and will increase the chromatic number of the graph by 1. This operation assumes
that there are no self-loops.

The Mycielski operation can be repeated by using the `iterations` kwarg. Each time, it will
apply the operator to the previous iterations graph.

For each vertex in the original graph, that vertex and a copy of it are added to the new graph.
Then, for each edge `(u, v)` in the original graph, the edges `(u, v)`, `(u', v)`, and `(u, v')`
are added to the new graph, where `u'` and `v'` are the "copies" of `u` and `v`, respectively.
In other words, the original graph is present as a subgraph, and each vertex in the original graph
is connected to all of its neighbors' copies. Finally, one last vertex `w` is added to the graph
and an edge connecting each copy vertex `u'` to `w` is added.

See [Mycielskian](https://en.wikipedia.org/wiki/Mycielskian) for more information.

### Implementation Notes
Supports [`SimpleGraph`](@ref) only. Copies the input before calling [`mycielski!`](@ref).

If there is a self-loop, we just return the original graph unmodified.

# Examples
```jldoctest
julia> using Graphs

julia> g = cycle_graph(5)
{5, 5} undirected simple Int64 graph

julia> gm = mycielski(g)
{11, 20} undirected simple Int64 graph

julia> collect(edges(gm))
20-element Vector{Graphs.SimpleGraphs.SimpleEdge{Int64}}:
Edge 1 => 2
Edge 1 => 5
Edge 1 => 7
Edge 1 => 10
Edge 2 => 3
Edge 2 => 6
Edge 2 => 8
Edge 3 => 4
Edge 3 => 7
Edge 3 => 9
Edge 4 => 5
Edge 4 => 8
Edge 4 => 10
Edge 5 => 6
Edge 5 => 9
Edge 6 => 11
Edge 7 => 11
Edge 8 => 11
Edge 9 => 11
Edge 10 => 11
```
"""
function mycielski(g; iterations=1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function mycielski(g; iterations=1)
function mycielski(g::SimpleGraph; iterations=1)

has_self_loops(g) && return g
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "doing nothing" the right thing for the mycielski operator on graphs with self loops? Or is the operator not defined on graphs with self loops?

Suggested change
has_self_loops(g) && return g
@assert !has_self_loops(g) "The graph can not have self loops"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@assert 2^iterations * (nv(g)+1) - 1 < typemax(eltype(g)) "eltype too small (or a good error message)"

mycielski!(copy(g); iterations=iterations)
end
84 changes: 84 additions & 0 deletions test/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -369,4 +369,88 @@
@testset "Length: $(typeof(g))" for g in test_generic_graphs(SimpleGraph(100))
@test length(g) == 10000
end

@testset "Mycielski Operator" begin
@testset "out of place" begin
g = complete_graph(2)

m = mycielski(g; iterations=8)
@test nv(m) == 767
@test ne(m) == 22196

# ensure it is not done in-place
@test nv(g) == 2
@test ne(g) == 1

# check that mycielski preserves triangle-freeness
g = complete_bipartite_graph(10, 5)
m = mycielski(g)
@test nv(m) == 2*15 + 1
@test ne(m) == 3*50 + 15
@test all(iszero, triangles(m))

# ensure it is not done in-place
@test nv(g) == 15
@test ne(g) == 50
end

@testset "in place" begin
g = complete_graph(2)

m = mycielski!(g; iterations=8)
@test nv(m) == 767
@test ne(m) == 22196

# ensure it is done in-place
@test nv(g) == 767
@test ne(g) == 22196

# check that mycielski preserves triangle-freeness
g = complete_bipartite_graph(10, 5)
m = mycielski(g)
@test nv(m) == 2*15 + 1
@test ne(m) == 3*50 + 15
@test all(iszero, triangles(m))

# ensure it is not done in-place
@test nv(g) == 15
@test ne(g) == 50
end

@testset "empty" begin
g = Graph()

m = mycielski!(g)
@test nv(m) == 1
@test ne(m) == 0

# one more iteration
m = mycielski!(m)
@test nv(m) == 3
@test ne(m) == 1
end

@testset "isolated vertices" begin
g = Graph()
add_vertices!(g, 2)

m = mycielski!(g)
@test nv(m) == 5
@test ne(m) == 2
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simple test for insufficient eltype could look like:

@testset "insufficient eltype" begin
g = complete_graph(UInt8(2))
@test_throws AssertionError mycielski!(g, iterations=10)
end

@testset "self loop" begin
g = complete_graph(5)

# add a self loop
add_edge!(g, 1, 1)
n = nv(g)
e = ne(g)

# no modification
m = mycielski(g)
@test nv(m) == n
@test ne(m) == e
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are good, but I think we need to cover some additional test cases such as:

  • tests for graphs with self-loops
  • tests for graphs with zero vertices
  • tests for graphs with isolated vertices
  • tests for graphs with different eltypes - the testgraphs utility function can help here

end
Loading