Skip to content
Closed
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
10 changes: 9 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[weakdeps]
NautyGraphs = "7509a0a4-015a-4167-b44b-0799a1a2605e"

[extensions]
NautyGraphsExt = "NautyGraphs"

[compat]
Aqua = "0.6"
ArnoldiMethod = "0.4"
Expand All @@ -23,6 +29,7 @@ DataStructures = "0.17, 0.18"
Documenter = "0.27"
Inflate = "0.1.3"
JuliaFormatter = "1"
NautyGraphs = "0.5.0"
SimpleTraits = "0.9"
StableRNGs = "1"
Statistics = "1"
Expand All @@ -36,6 +43,7 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
NautyGraphs = "7509a0a4-015a-4167-b44b-0799a1a2605e"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Expand All @@ -45,4 +53,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[targets]
test = ["Aqua", "Base64", "DelimitedFiles", "Documenter", "JET", "JuliaFormatter", "LinearAlgebra", "Pkg", "Random", "SparseArrays", "StableRNGs", "Statistics", "Test", "Unitful"]
test = ["Aqua", "Base64", "DelimitedFiles", "Documenter", "JET", "JuliaFormatter", "LinearAlgebra", "NautyGraphs", "Pkg", "Random", "SparseArrays", "StableRNGs", "Statistics", "Test", "Unitful"]
137 changes: 137 additions & 0 deletions ext/NautyGraphsExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
module NautyGraphsExt

using Graphs, NautyGraphs
using Graphs.Experimental: AlgNautyGraphs

function Graphs.Experimental.has_induced_subgraphisomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Bool
error(
"Induced subgraph isomorphims are currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
return nothing
end
Comment on lines +6 to +17
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I do not think it is necessary to write these methods. This should just be a MethodError, no need to raise your own errors.


function Graphs.Experimental.has_subgraphisomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Bool
error(
"Subgraph isomorphims are currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
return nothing
end

function Graphs.Experimental.has_isomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Bool
if !isnothing(edge_relation)
error(
"Edge relations are currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
end
if !isnothing(vertex_relation)
error(
"Vertex relations are currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
end
return NautyGraph(g1) ≃ NautyGraph(g2)
end

function Graphs.Experimental.canonize!(g::AbstractGraph, ::AlgNautyGraphs)
ng = is_directed(g) ? NautyDiGraph(g) : NautyGraph(g)
perm = convert(Vector{eltype(g)}, NautyGraphs.canonical_permutation(ng))
permute!(g, perm)
return perm
end

function Graphs.Experimental.count_induced_subgraphisomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Int
error(
"Counting induced subgraph isomorphims is currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
return nothing
end

function Graphs.Experimental.count_subgraphisomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Int
error(
"Counting subgraph isomorphims is currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
return nothing
end

function Graphs.Experimental.count_isomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Int
error(
"Counting isomorphims is currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
return nothing
end

function Graphs.Experimental.all_induced_subgraphisomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}}
error(
"Generating all induced subgraph isomorphims is currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
return nothing
end

function Graphs.Experimental.all_subgraphisomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}}
error(
"Generating all subgraph isomorphims is currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
return nothing
end

function Graphs.Experimental.all_isomorph(
g1::AbstractGraph,
g2::AbstractGraph,
::AlgNautyGraphs;
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}}
error(
"Generating all isomorphims is currently not supported by `NautyGraphs`. Please use a different isomorphism algorithm.",
)
return nothing
end

end
3 changes: 3 additions & 0 deletions src/Experimental/Experimental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ export description,
# isomorphism
VF2,
vf2,
AlgNautyGraphs,
IsomorphismProblem,
SubGraphIsomorphismProblem,
InducedSubGraphIsomorphismProblem,
could_have_isomorph,
has_isomorph,
all_isomorph,
count_isomorph,
canonize!,
has_induced_subgraphisomorph,
count_induced_subgraphisomorph,
all_induced_subgraphisomorph,
Expand All @@ -25,6 +27,7 @@ description() = "This module contains experimental graph functions."

include("isomorphism.jl")
include("vf2.jl") # Julian implementation of VF2 algorithm
include("nautygraphs.jl")
include("Parallel/Parallel.jl")
include("Traversals/Traversals.jl")
include("ShortestPaths/ShortestPaths.jl")
Expand Down
83 changes: 48 additions & 35 deletions src/Experimental/isomorphism.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ function has_induced_subgraphisomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Bool
return has_induced_subgraphisomorph(
g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation
)
throw(MethodError(has_induced_subgraphisomorph, (g1, g2, alg)))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems like a breaking change to me. Why is it needed?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

also, generally, I try to avoid throwing my own MethodErrors. Structuring the dispatch such that julia is the one the throw MethodErrors usually leads to simpler code (and then error hints can be used for more messages).

end

"""
Expand Down Expand Up @@ -130,9 +128,7 @@ function has_subgraphisomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Bool
return has_subgraphisomorph(
g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation
)
throw(MethodError(has_subgraphisomorph, (g1, g2, alg)))
end

"""
Expand All @@ -141,12 +137,14 @@ end
Return `true` if the graph `g1` is isomorphic to `g2`.

### Optional Arguments
- `alg`: The algorithm that is used to find the induced subgraph isomorphism. Can be only
`VF2()` at the moment.
- `alg`: The algorithm that is used to find the induced subgraph isomorphism. Can be
`VF2()` or `AlgNautyGraphs()`, if `NautyGraphs` is installed and imported.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
`VF2()` or `AlgNautyGraphs()`, if `NautyGraphs` is installed and imported.
`VF2()` or, if `NautyGraphs` is imported, `AlgNautyGraphs()`.

- `vertex_relation`: A binary function that takes a vertex from `g1` and one from `g2`. An
isomorphism only exists if this function returns `true` for all matched vertices.
isomorphism only exists if this function returns `true` for all matched vertices. Only
works with `VF2()` at the moment.
- `edge_relation`: A binary function that takes an edge from `g1` and one from `g2`. An
isomorphism only exists if this function returns `true` for all matched edges.
isomorphism only exists if this function returns `true` for all matched edges. Only
works with `VF2()` at the moment.

### Examples
```doctest.jl
Expand All @@ -173,9 +171,40 @@ function has_isomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Bool
return has_isomorph(
g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation
)
throw(MethodError(has_isomorph, (g1, g2, alg)))
end

"""
canonize!(g, alg::IsomorphismAlgorithm=AlgNautyGraphs())

Permute the vertices of graph `g` into the canonical order defined by the algorithm `alg` and return the permutation.
If graphs `g1` and `g2` are isomorphic, the orders of their vertices will be equal after canonizing them with the same algorithm.

### Optional Arguments
- `alg`: The algorithm that is used to canonize the graph. Can be only be `AlgNautyGraphs()`
at this moment, which requires `NautyGraphs` to be installed and imported.

### Examples
```doctest.jl
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

huh... I did not know that you can also use doctest.jl as a specifier for doctest!

julia> canonize!(path_graph(3))
[1, 3, 2]

julia> g1 = path_digraph(4)
julia> g2 = path_digraph(4)[[2, 3, 1, 4]]
julia> g1 == g2
false
julia> canonize!(g1)
[4, 2, 3, 1]
julia> canonize!(g2)
[4, 1, 2, 3]
julia> g1 == g2
true
```
### See also
[`has_isomorph`](@ref)
"""
function canonize!(g::AbstractGraph, alg::IsomorphismAlgorithm=AlgNautyGraphs())
throw(MethodError(canonize!, (g, alg)))
end
Comment on lines +206 to 208
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this can be function canonize! end so that we do not manually throw MethodError. Then we can have a hint that detects the method error and warns that one needs to import NautyGraphs.


"""
Expand Down Expand Up @@ -214,9 +243,7 @@ function count_induced_subgraphisomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Int
return count_induced_subgraphisomorph(
g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation
)
throw(MethodError(count_induced_subgraphisomorph, (g1, g2, alg)))
end

"""
Expand Down Expand Up @@ -257,13 +284,7 @@ function count_subgraphisomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Int
return count_subgraphisomorph(
g1::AbstractGraph,
g2::AbstractGraph,
VF2();
vertex_relation=vertex_relation,
edge_relation=edge_relation,
)
throw(MethodError(count_subgraphisomorph, (g1, g2, alg)))
end

"""
Expand Down Expand Up @@ -304,9 +325,7 @@ function count_isomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Int
return count_isomorph(
g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation
)
throw(MethodError(count_isomorph, (g1, g2, alg)))
end

"""
Expand Down Expand Up @@ -353,9 +372,7 @@ function all_induced_subgraphisomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}}
return all_induced_subgraphisomorph(
g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation
)
throw(MethodError(all_induced_subgraphisomorph, (g1, g2, alg)))
end

"""
Expand Down Expand Up @@ -404,9 +421,7 @@ function all_subgraphisomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}}
return all_subgraphisomorph(
g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation
)
throw(MethodError(all_subgraphisomorph, (g1, g2, alg)))
end

"""
Expand Down Expand Up @@ -458,7 +473,5 @@ function all_isomorph(
vertex_relation::Union{Nothing,Function}=nothing,
edge_relation::Union{Nothing,Function}=nothing,
)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}}
return all_isomorph(
g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation
)
throw(MethodError(all_isomorph, (g1, g2, alg)))
end
8 changes: 8 additions & 0 deletions src/Experimental/nautygraphs.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
AlgNautyGraphs

An empty concrete type used to dispatch to [`NautyGraphs`](@ref) isomorphism functions.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
An empty concrete type used to dispatch to [`NautyGraphs`](@ref) isomorphism functions.
A "configuration" type used to dispatch to isomorphism algorithms from the `nauty_graphs` C library,
wrapped by the `NautyGraphs.jl` package.

"""
struct AlgNautyGraphs <: IsomorphismAlgorithm end

# The implementation of NautyGraph methods for graph isomorphism is done as a package extension in /ext/NautyGraphsExt.jl
Loading