|
4 | 4 | Check whether a graph is chordal. |
5 | 5 |
|
6 | 6 | 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). |
8 | 8 |
|
9 | 9 | ### Performance |
10 | 10 | 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) |
47 | 47 | numbered = Set(start_vertex) |
48 | 48 |
|
49 | 49 | #= 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` |
52 | 52 | in any iteration is not complete, `g` cannot be chordal. =# |
53 | 53 | while !isempty(unnumbered) |
54 | 54 | # `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) |
56 | 56 | delete!(unnumbered, v) |
57 | 57 | push!(numbered, v) |
| 58 | + subsequent_neighbors = intersect(neighbors(g, v), numbered) |
58 | 59 |
|
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 |
64 | 62 | end |
65 | 63 |
|
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. =# |
68 | 66 | return true |
69 | 67 | end |
70 | 68 |
|
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} |
73 | 71 | ) where {T} |
74 | 72 | cardinality(v::T) = count(in(numbered), neighbors(g, v)) |
75 | 73 | return argmax(cardinality, unnumbered) |
76 | 74 | end |
77 | 75 |
|
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