Skip to content

Commit eeb10f4

Browse files
committed
Stop allocating induced subgraphs
Originally mirroring the networkx implementation, we created a new AbstractGraph object every time we tested subsequent neighbors in the potential PEO with the induced_subgraph function. This commit makes our implementation more performant by simply taking the vertex (sub)set and checking to see if all pairs are adjacent via iteration. Additionally, we change inconsistent naming in certain parts ('node' vs 'vertex'), changing everything to 'vertex' where relevant.
1 parent 6c2fa92 commit eeb10f4

File tree

1 file changed

+18
-14
lines changed

1 file changed

+18
-14
lines changed

src/chordality.jl

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Check whether a graph is chordal.
55
66
A graph is said to be *chordal* if every cycle of length `≥ 4` has a chord
7-
(i.e., an edge between two nodes not adjacent in the cycle).
7+
(i.e., an edge between two vertices not adjacent in the cycle).
88
99
### Performance
1010
This algorithm is linear in the number of vertices and edges of the graph (i.e.,
@@ -47,32 +47,36 @@ function is_chordal(g::AbstractGraph)
4747
numbered = Set(start_vertex)
4848

4949
#= Searching by maximum cardinality ensures that in any possible perfect elimination
50-
ordering of `g`, `purported_clique_nodes` is precisely the set of neighbors of `v` that
51-
come later in the ordering. Hence, if the subgraph induced by `purported_clique_nodes`
50+
ordering of `g`, `subsequent_neighbors` is precisely the set of neighbors of `v` that
51+
come later in the ordering. Therefore, if the subgraph induced by `subsequent_neighbors`
5252
in any iteration is not complete, `g` cannot be chordal. =#
5353
while !isempty(unnumbered)
5454
# `v` is the vertex in `unnumbered` with the most neighbors in `numbered`
55-
v = _max_cardinality_node(g, unnumbered, numbered)
55+
v = _max_cardinality_vertex(g, unnumbered, numbered)
5656
delete!(unnumbered, v)
5757
push!(numbered, v)
58+
subsequent_neighbors = intersect(neighbors(g, v), numbered)
5859

59-
# A complete subgraph of a larger graph is called a "clique," hence the naming here
60-
purported_clique_nodes = intersect(neighbors(g, v), numbered)
61-
purported_clique = induced_subgraph(g, purported_clique_nodes)
62-
63-
_is_complete_graph(purported_clique) || return false
60+
# A complete subgraph is also called a "clique," hence the naming here
61+
_induces_clique(subsequent_neighbors, g) || return false
6462
end
6563

66-
#= That `g` admits a perfect elimination ordering is an "if and only if" condition for
67-
chordality, so if every `purported_clique` was indeed complete, `g` must be chordal. =#
64+
#= A perfect elimination ordering is an "if and only if" condition for chordality, so if
65+
every `subsequent_neighbors` set induced a complete subgraph, `g` must be chordal. =#
6866
return true
6967
end
7068

71-
function _max_cardinality_node(
72-
g::AbstractGraph, unnumbered::Set{T}, numbered::Set{T}
69+
function _max_cardinality_vertex(
70+
g::AbstractGraph{T}, unnumbered::Set{T}, numbered::Set{T}
7371
) where {T}
7472
cardinality(v::T) = count(in(numbered), neighbors(g, v))
7573
return argmax(cardinality, unnumbered)
7674
end
7775

78-
_is_complete_graph(g::AbstractGraph) = density(g) == 1
76+
function _induces_clique(vertex_subset::Vector{T}, g::AbstractGraph{T}) where {T}
77+
for (i, u) in enumerate(vertex_subset), v in Iterators.drop(vertex_subset, i)
78+
has_edge(g, u, v) || return false
79+
end
80+
81+
return true
82+
end

0 commit comments

Comments
 (0)