rust_igraph/algorithms/properties/
is_clique.rs1use crate::core::{Graph, IgraphResult, VertexId};
10
11pub fn is_clique(graph: &Graph, vertices: &[VertexId], directed: bool) -> IgraphResult<bool> {
34 let directed_eff = directed && graph.is_directed();
35 check_subset(graph, vertices, directed_eff, false)
36}
37
38pub fn is_independent_vertex_set(graph: &Graph, vertices: &[VertexId]) -> IgraphResult<bool> {
58 check_subset(graph, vertices, false, true)
59}
60
61fn check_subset(
62 graph: &Graph,
63 vertices: &[VertexId],
64 directed: bool,
65 independent_set: bool,
66) -> IgraphResult<bool> {
67 let n = vertices.len();
68 if n <= 1 {
69 return Ok(true);
70 }
71
72 let vcount = graph.vcount();
73 for &v in vertices {
74 if v >= vcount {
75 return Err(crate::core::IgraphError::InvalidArgument(format!(
76 "vertex {v} out of range (vcount = {vcount})"
77 )));
78 }
79 }
80
81 let ecount = graph.ecount();
82 let mut out_adj: Vec<Vec<VertexId>> = vec![Vec::new(); vcount as usize];
83 let mut in_adj: Vec<Vec<VertexId>> = vec![Vec::new(); vcount as usize];
84
85 for eid in 0..ecount {
86 #[allow(clippy::cast_possible_truncation)]
87 let (from, to) = graph.edge(eid as u32)?;
88 out_adj[from as usize].push(to);
89 if graph.is_directed() {
90 in_adj[to as usize].push(from);
91 }
92 }
93
94 for adj in &mut out_adj {
95 adj.sort_unstable();
96 }
97 if graph.is_directed() {
98 for adj in &mut in_adj {
99 adj.sort_unstable();
100 }
101 }
102
103 for (i, &u) in vertices.iter().enumerate() {
104 let j_start = if directed { 0 } else { i + 1 };
105 for &v in &vertices[j_start..] {
106 if u == v {
107 continue;
108 }
109
110 let has_edge = if directed {
111 out_adj[u as usize].binary_search(&v).is_ok()
112 } else {
113 out_adj[u as usize].binary_search(&v).is_ok()
114 || out_adj[v as usize].binary_search(&u).is_ok()
115 || (graph.is_directed()
116 && (in_adj[u as usize].binary_search(&v).is_ok()
117 || in_adj[v as usize].binary_search(&u).is_ok()))
118 };
119
120 if independent_set {
121 if has_edge {
122 return Ok(false);
123 }
124 } else if !has_edge {
125 return Ok(false);
126 }
127 }
128 }
129
130 Ok(true)
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn clique_empty_set() {
139 let g = Graph::with_vertices(5);
140 assert!(is_clique(&g, &[], false).unwrap());
141 }
142
143 #[test]
144 fn clique_singleton() {
145 let g = Graph::with_vertices(5);
146 assert!(is_clique(&g, &[2], false).unwrap());
147 }
148
149 #[test]
150 fn clique_pair_connected() {
151 let mut g = Graph::with_vertices(3);
152 g.add_edge(0, 1).unwrap();
153 assert!(is_clique(&g, &[0, 1], false).unwrap());
154 }
155
156 #[test]
157 fn clique_pair_not_connected() {
158 let mut g = Graph::with_vertices(3);
159 g.add_edge(0, 1).unwrap();
160 assert!(!is_clique(&g, &[0, 2], false).unwrap());
161 }
162
163 #[test]
164 fn clique_triangle() {
165 let mut g = Graph::with_vertices(4);
166 g.add_edge(0, 1).unwrap();
167 g.add_edge(0, 2).unwrap();
168 g.add_edge(1, 2).unwrap();
169 g.add_edge(2, 3).unwrap();
170 assert!(is_clique(&g, &[0, 1, 2], false).unwrap());
171 assert!(!is_clique(&g, &[0, 1, 2, 3], false).unwrap());
172 }
173
174 #[test]
175 fn clique_directed_both_directions() {
176 let mut g = Graph::new(3, true).unwrap();
177 g.add_edge(0, 1).unwrap();
178 g.add_edge(1, 0).unwrap();
179 g.add_edge(0, 2).unwrap();
180 g.add_edge(1, 2).unwrap();
181 assert!(!is_clique(&g, &[0, 1, 2], true).unwrap());
183 assert!(is_clique(&g, &[0, 1, 2], false).unwrap());
185 }
186
187 #[test]
188 fn clique_directed_full() {
189 let mut g = Graph::new(3, true).unwrap();
190 g.add_edge(0, 1).unwrap();
191 g.add_edge(1, 0).unwrap();
192 g.add_edge(0, 2).unwrap();
193 g.add_edge(2, 0).unwrap();
194 g.add_edge(1, 2).unwrap();
195 g.add_edge(2, 1).unwrap();
196 assert!(is_clique(&g, &[0, 1, 2], true).unwrap());
197 }
198
199 #[test]
200 fn clique_with_self_loop() {
201 let mut g = Graph::with_vertices(3);
202 g.add_edge(0, 0).unwrap();
203 g.add_edge(0, 1).unwrap();
204 g.add_edge(1, 2).unwrap();
205 g.add_edge(0, 2).unwrap();
206 assert!(is_clique(&g, &[0, 1, 2], false).unwrap());
207 }
208
209 #[test]
210 fn clique_vertex_out_of_range() {
211 let g = Graph::with_vertices(3);
212 assert!(is_clique(&g, &[0, 5], false).is_err());
213 }
214
215 #[test]
216 fn independent_empty_set() {
217 let g = Graph::with_vertices(5);
218 assert!(is_independent_vertex_set(&g, &[]).unwrap());
219 }
220
221 #[test]
222 fn independent_singleton() {
223 let g = Graph::with_vertices(5);
224 assert!(is_independent_vertex_set(&g, &[3]).unwrap());
225 }
226
227 #[test]
228 fn independent_no_edges_between() {
229 let mut g = Graph::with_vertices(4);
230 g.add_edge(0, 1).unwrap();
231 g.add_edge(2, 3).unwrap();
232 assert!(is_independent_vertex_set(&g, &[0, 2]).unwrap());
233 assert!(is_independent_vertex_set(&g, &[1, 3]).unwrap());
234 assert!(is_independent_vertex_set(&g, &[0, 3]).unwrap());
235 }
236
237 #[test]
238 fn independent_has_edge() {
239 let mut g = Graph::with_vertices(4);
240 g.add_edge(0, 1).unwrap();
241 g.add_edge(2, 3).unwrap();
242 assert!(!is_independent_vertex_set(&g, &[0, 1]).unwrap());
243 assert!(!is_independent_vertex_set(&g, &[0, 1, 2]).unwrap());
244 }
245
246 #[test]
247 fn independent_directed_ignores_direction() {
248 let mut g = Graph::new(3, true).unwrap();
249 g.add_edge(0, 1).unwrap(); assert!(!is_independent_vertex_set(&g, &[0, 1]).unwrap());
251 }
252
253 #[test]
254 fn independent_all_isolated() {
255 let g = Graph::with_vertices(5);
256 assert!(is_independent_vertex_set(&g, &[0, 1, 2, 3, 4]).unwrap());
257 }
258}