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}