-
Notifications
You must be signed in to change notification settings - Fork 98
Graph isomorphism and canonization via NautyGraphs.jl #418
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
|
||
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 |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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))) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like a breaking change to me. Why is it needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
|
||||||
""" | ||||||
|
@@ -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 | ||||||
|
||||||
""" | ||||||
|
@@ -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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
- `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 | ||||||
|
@@ -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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. huh... I did not know that you can also use |
||||||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this can be |
||||||
|
||||||
""" | ||||||
|
@@ -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 | ||||||
|
||||||
""" | ||||||
|
@@ -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 | ||||||
|
||||||
""" | ||||||
|
@@ -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 | ||||||
|
||||||
""" | ||||||
|
@@ -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 | ||||||
|
||||||
""" | ||||||
|
@@ -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 | ||||||
|
||||||
""" | ||||||
|
@@ -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 |
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. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
""" | ||||||||
struct AlgNautyGraphs <: IsomorphismAlgorithm end | ||||||||
|
||||||||
# The implementation of NautyGraph methods for graph isomorphism is done as a package extension in /ext/NautyGraphsExt.jl |
There was a problem hiding this comment.
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.