Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added strong_product, disjunctive_product, lexicographical_product, h… #154

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ export
join,
tensor_product,
cartesian_product,
strong_product,
disjunctive_product,
lexicographic_product,
homomorphic_product,
crosspath,
induced_subgraph,
egonet,
Expand Down
211 changes: 211 additions & 0 deletions src/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,217 @@ function tensor_product(g::G, h::G) where {G<:AbstractGraph}
return z
end

"""
strong_product(g, h)

Return the [strong product](https://en.wikipedia.org/wiki/Strong_product_of_graphs)
of `g` and `h`.
dstahlke marked this conversation as resolved.
Show resolved Hide resolved

### Implementation Notes
Preserves the eltype of the input graph. Will error if the number of vertices
in the generated graph exceeds the eltype.

# Examples
```jldoctest
julia> using Graphs

julia> a = star_graph(3)

julia> b = path_graph(3)

julia> g = strong_product(a, b)
{9, 8} undirected simple Int64 graph

julia> g == union(cartesian_product(a, b), tensor_product(a, b))
true
```
"""
function strong_product(g::G, h::G) where {G<:AbstractGraph}
Copy link
Member

Choose a reason for hiding this comment

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

Does this operator also work for directed graphs? And does it really make sense to define it for all graph types? Maybe we should restrict it to SimpleGraph and maybe also SimpleDiGraph.

Otherwise it might be a bit confusing if one has, for example, a graph with metadata on its vertices.

Copy link
Author

Choose a reason for hiding this comment

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

Though I don't see mention in wikipedia of directed graphs in relation to any of these products, I think most of them would make sense. The only exception would be the homomorphic product which has (h_1 \nsim h_2) in its definition and there could be controversy over how that should be interpreted for directed graphs. Also this one is pretty obscure and I suspect nobody would ever want to use it on a directed graph.

Good point on not supporting graphs that have metadata. What would be the appropriate way to support both SimpleGraph and SimpleDiGraph? Should I use the common parent class AbstractSimpleGraph? (Note that the existing functions tensor_product and cartesian_product also have this issue.)

z = G(nv(g) * nv(h))
id(i, j) = (i - 1) * nv(h) + j
undirected = !is_directed(g)
for e1 in edges(g)
i1, i2 = Tuple(e1)
for e2 in edges(h)
j1, j2 = Tuple(e2)
add_edge!(z, id(i1, j1), id(i2, j2))
if undirected
add_edge!(z, id(i1, j2), id(i2, j1))
end
end
end
for e in edges(g)
i1, i2 = Tuple(e)
for j in vertices(h)
add_edge!(z, id(i1, j), id(i2, j))
end
end
for e in edges(h)
j1, j2 = Tuple(e)
for i in vertices(g)
add_edge!(z, id(i, j1), id(i, j2))
end
end
return z
end

"""
disjunctive_product(g, h)

Return the [disjunctive product](https://en.wikipedia.org/wiki/Graph_product)
of `g` and `h`.

### Implementation Notes
Preserves the eltype of the input graph. Will error if the number of vertices
in the generated graph exceeds the eltype.

# Examples
```jldoctest
julia> using Graphs

julia> a = star_graph(3)

julia> b = path_graph(3)

julia> g = disjunctive_product(a, b)
{9, 8} undirected simple Int64 graph

julia> complement(g) == strong_product(complement(a), complement(b))
true
```
"""
function disjunctive_product(g::G, h::G) where {G<:AbstractGraph}
z = G(nv(g) * nv(h))
id(i, j) = (i - 1) * nv(h) + j
for e in edges(g)
i1, i2 = Tuple(e)
for j in vertices(h)
for k in vertices(h)
add_edge!(z, id(i1, j), id(i2, k))
end
end
end
for e in edges(h)
j1, j2 = Tuple(e)
for i in vertices(g)
for k in vertices(g)
add_edge!(z, id(i, j1), id(k, j2))
end
end
end
return z
end

"""
lexicographic_product(g, h)

Return the [lexicographic product](https://en.wikipedia.org/wiki/Lexicographic_product_of_graphs)
of `g` and `h`.

### Implementation Notes
Preserves the eltype of the input graph. Will error if the number of vertices
in the generated graph exceeds the eltype.

# Examples
```jldoctest
julia> using Graphs

julia> g = lexicographic_product(star_graph(3), path_graph(3))
{9, 8} undirected simple Int64 graph

julia> adjacency_matrix(g)
9×9 SparseArrays.SparseMatrixCSC{Int64, Int64} with 48 stored entries:
⋅ 1 ⋅ 1 1 1 1 1 1
1 ⋅ 1 1 1 1 1 1 1
⋅ 1 ⋅ 1 1 1 1 1 1
1 1 1 ⋅ 1 ⋅ ⋅ ⋅ ⋅
1 1 1 1 ⋅ 1 ⋅ ⋅ ⋅
1 1 1 ⋅ 1 ⋅ ⋅ ⋅ ⋅
1 1 1 ⋅ ⋅ ⋅ ⋅ 1 ⋅
1 1 1 ⋅ ⋅ ⋅ 1 ⋅ 1
1 1 1 ⋅ ⋅ ⋅ ⋅ 1 ⋅
```
"""
function lexicographic_product(g::G, h::G) where {G<:AbstractGraph}
z = G(nv(g) * nv(h))
id(i, j) = (i - 1) * nv(h) + j
for e in edges(g)
i1, i2 = Tuple(e)
for j in vertices(h)
for k in vertices(h)
add_edge!(z, id(i1, j), id(i2, k))
end
end
end
for e in edges(h)
j1, j2 = Tuple(e)
for i in vertices(g)
add_edge!(z, id(i, j1), id(i, j2))
end
end
return z
end

"""
homomorphic_product(g, h)

Return the [homomorphic product](https://en.wikipedia.org/wiki/Graph_product)
of `g` and `h`.

### Implementation Notes
Preserves the eltype of the input graph. Will error if the number of vertices
in the generated graph exceeds the eltype.

# Examples
```jldoctest
julia> using Graphs

julia> g = homomorphic_product(star_graph(3), path_graph(3))
{9, 8} undirected simple Int64 graph

julia> adjacency_matrix(g)
9×9 SparseArrays.SparseMatrixCSC{Int64, Int64} with 38 stored entries:
⋅ 1 1 1 ⋅ 1 1 ⋅ 1
1 ⋅ 1 ⋅ 1 ⋅ ⋅ 1 ⋅
1 1 ⋅ 1 ⋅ 1 1 ⋅ 1
1 ⋅ 1 ⋅ 1 1 ⋅ ⋅ ⋅
⋅ 1 ⋅ 1 ⋅ 1 ⋅ ⋅ ⋅
1 ⋅ 1 1 1 ⋅ ⋅ ⋅ ⋅
1 ⋅ 1 ⋅ ⋅ ⋅ ⋅ 1 1
⋅ 1 ⋅ ⋅ ⋅ ⋅ 1 ⋅ 1
1 ⋅ 1 ⋅ ⋅ ⋅ 1 1 ⋅
```
"""
function homomorphic_product(g::G, h::G) where {G<:AbstractGraph}
z = G(nv(g) * nv(h))
id(i, j) = (i - 1) * nv(h) + j
undirected = !is_directed(g)
for i in vertices(g)
for j in vertices(h)
for k in vertices(h)
if k != j
add_edge!(z, id(i, j), id(i, k))
end
end
end
end
cmpl_h = complement(h)
for e in edges(g)
i1, i2 = Tuple(e)
for f in edges(cmpl_h)
j1, j2 = Tuple(f)
add_edge!(z, id(i1, j1), id(i2, j2))
if undirected
add_edge!(z, id(i1, j2), id(i2, j1))
end
end
for j in vertices(h)
add_edge!(z, id(i1, j), id(i2, j))
end
end
return z
end

## subgraphs ###

"""
Expand Down
15 changes: 15 additions & 0 deletions test/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,21 @@
end
end

gx = SimpleGraph(10, 20)
gy = SimpleGraph(15, 34)
@testset "Graph product edge counts" for (g1, g2) in zip(testgraphs(gx), testgraphs(gy))
v1 = nv(g1)
v2 = nv(g2)
e1 = ne(g1)
e2 = ne(g2)
# Edge counts from https://en.wikipedia.org/wiki/Graph_product
@test ne(cartesian_product(g1, g2)) == v1 * e2 + v2 * e1
@test ne(tensor_product(g1, g2)) == 2 * e1 * e2
@test ne(lexicographic_product(g1, g2)) == v1 * e2 + e1 * v2^2
@test ne(strong_product(g1, g2)) == v1 * e2 + v2 * e1 + 2 * e1 * e2
@test ne(disjunctive_product(g1, g2)) == v1^2 * e2 + e1 * v2^2 - 2 * e1 * e2
end

## test subgraphs ##

gb = smallgraph(:bull)
Expand Down