petgraph/algo/
maximal_cliques.rs

1use crate::visit::{GetAdjacencyMatrix, IntoNeighbors, IntoNodeIdentifiers};
2use alloc::vec::Vec;
3use core::hash::Hash;
4use core::iter::FromIterator;
5use hashbrown::HashSet;
6
7/// Finds maximal cliques containing all the vertices in r, some of the
8/// vertices in p, and none of the vertices in x.
9///
10/// By default, only works on undirected graphs. It can be used on directed graphs
11/// if the graph is symmetric. I.e., if an edge (u, v) exists, then (v, u) also exists.
12///
13/// Uses the [Bron–Kerbosch algorithm][1] with pivoting.
14///
15/// [1]: https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm
16fn bron_kerbosch_pivot<G>(
17    g: G,
18    adj_mat: &G::AdjMatrix,
19    r: HashSet<G::NodeId>,
20    mut p: HashSet<G::NodeId>,
21    mut x: HashSet<G::NodeId>,
22) -> Vec<HashSet<G::NodeId>>
23where
24    G: GetAdjacencyMatrix + IntoNeighbors,
25    G::NodeId: Eq + Hash,
26{
27    let mut cliques = Vec::with_capacity(1);
28    if p.is_empty() {
29        if x.is_empty() {
30            cliques.push(r);
31        }
32        return cliques;
33    }
34    // pick the pivot u to be the vertex with max degree
35    let u = p.iter().max_by_key(|&v| g.neighbors(*v).count()).unwrap();
36    let mut todo = p
37        .iter()
38        .filter(|&v| *u == *v || !g.is_adjacent(adj_mat, *u, *v) || !g.is_adjacent(adj_mat, *v, *u)) //skip neighbors of pivot
39        .cloned()
40        .collect::<Vec<G::NodeId>>();
41    while let Some(v) = todo.pop() {
42        let neighbors = HashSet::from_iter(g.neighbors(v));
43        p.remove(&v);
44        let mut next_r = r.clone();
45        next_r.insert(v);
46
47        let next_p = p
48            .intersection(&neighbors)
49            .cloned()
50            .collect::<HashSet<G::NodeId>>();
51        let next_x = x
52            .intersection(&neighbors)
53            .cloned()
54            .collect::<HashSet<G::NodeId>>();
55
56        cliques.extend(bron_kerbosch_pivot(g, adj_mat, next_r, next_p, next_x));
57
58        x.insert(v);
59    }
60
61    cliques
62}
63
64/// Find all maximal cliques in an undirected graph using [Bron–Kerbosch algorithm][1]
65/// with pivoting. Also works on symmetric directed graphs, see the note below.
66///
67/// A clique is a set of nodes such that every node connects to
68/// every other. A maximal clique is a clique that cannot be extended
69/// by including one more adjacent vertex. A graph may have multiple
70/// maximal cliques.
71///
72/// This method may also be called on directed graphs, but one needs to ensure that
73/// if an edge (u, v) exists, then (v, u) also exists.
74///
75/// # Arguments
76/// * `g`: The graph to find maximal cliques in.
77///
78/// # Returns
79/// * `Vec<HashSet>`: A vector of [`struct@hashbrown::HashSet`] making up the maximal cliques in the graph.
80///
81/// # Complexity
82/// * Time complexity: **O(3^(|V|/3))**
83/// * Auxiliary space: **O(|V|²)**.
84///
85/// where **|V|** is the number of nodes.
86///
87/// [1]: https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm
88///
89/// # Example
90///
91/// ```
92/// use petgraph::algo::maximal_cliques;
93/// use petgraph::graph::UnGraph;
94///
95/// let mut g = UnGraph::<i32, ()>::from_edges([(0, 1), (0, 2), (1, 2), (2, 3)]);
96/// g.add_node(4);
97/// // The example graph:
98/// //
99/// // 0 --- 2 -- 3
100/// //  \   /
101/// //   \ /
102/// //    1       4
103/// //
104/// // maximal cliques: {4}, {2, 3}, {0, 1, 2}
105/// // Output the result
106/// let cliques = maximal_cliques(&g);
107/// println!("{:?}", cliques);
108/// // [
109/// //   {NodeIndex(4)},
110/// //   {NodeIndex(0), NodeIndex(1), NodeIndex(2)},
111/// //   {NodeIndex(2), NodeIndex(3)}
112/// // ]
113/// ```
114pub fn maximal_cliques<G>(g: G) -> Vec<HashSet<G::NodeId>>
115where
116    G: GetAdjacencyMatrix + IntoNodeIdentifiers + IntoNeighbors,
117    G::NodeId: Eq + Hash,
118{
119    let adj_mat = g.adjacency_matrix();
120    let r = HashSet::new();
121    let p = g.node_identifiers().collect::<HashSet<G::NodeId>>();
122    let x = HashSet::new();
123    bron_kerbosch_pivot(g, &adj_mat, r, p, x)
124}