igraph/cliques.rs
1//! Cliques, independent vertex sets and vertex/edge colorings
2//! (`igraph_cliques.h`, `igraph_coloring.h`).
3//!
4//! A *clique* is a set of vertices that are pairwise adjacent; an
5//! *independent vertex set* is a set of vertices no two of which are adjacent
6//! (i.e. a clique of the complementer graph). A *proper vertex coloring*
7//! assigns colors to vertices so that adjacent vertices always differ.
8//! The clique and independent-set functions ignore edge directions (igraph
9//! emits a warning for directed graphs), self-loops and multi-edges. The
10//! coloring checks ignore self-loops too, but
11//! [`is_bipartite_coloring`](Graph::is_bipartite_coloring) reports edge
12//! directions and [`is_edge_coloring`](Graph::is_edge_coloring) treats
13//! parallel edges as adjacent.
14//!
15//! Three families of sets are distinguished, both for cliques and for
16//! independent sets:
17//!
18//! - **all** sets (every clique, including every subset of a clique);
19//! - **maximal** sets, which cannot be extended by adding one more vertex;
20//! - **largest** (maximum) sets, those of the largest possible size. The size
21//! of the largest clique is the *clique number* ω(G), the size of the
22//! largest independent set is the *independence number* α(G).
23//!
24//! # Size ranges
25//!
26//! The enumeration functions take the admissible sizes as a Rust range of
27//! [`usize`] ([`RangeBounds`]): `..` means "any size", `3..` "at least 3",
28//! `2..=4` "between 2 and 4", `..5` "fewer than 5". An empty range (e.g.
29//! `4..=2` or `..1`) is rejected with an [`ErrorKind::InvalidValue`] error,
30//! since no set could ever satisfy it. A lower bound larger than the number
31//! of vertices is not an error: the result is simply empty. An optional
32//! `max_results` stops the search after that many sets were found (`None`
33//! means no limit, `Some(0)` returns nothing).
34//!
35//! # Nesting searches in callbacks
36//!
37//! The functions based on the Cliquer library
38//! ([`cliques`](Graph::cliques), [`cliques_callback`](Graph::cliques_callback),
39//! [`clique_size_hist`](Graph::clique_size_hist) and the weighted clique
40//! functions) share per-thread state inside igraph: in igraph 1.0.0 and 1.0.1
41//! the Cliquer wrapper keeps the options of the running search in a single
42//! thread-local global that every search overwrites. Calling one of them from
43//! inside a [`cliques_callback`](Graph::cliques_callback) closure is therefore
44//! refused with an [`ErrorKind::Failure`] error instead of corrupting the
45//! running search. All other functions (for instance
46//! [`maximal_cliques`](Graph::maximal_cliques) or
47//! [`clique_number`](Graph::clique_number)) may be used freely there, and
48//! [`maximal_cliques_callback`](Graph::maximal_cliques_callback) closures may
49//! call anything. The restriction is per thread: searches running on other
50//! threads are independent. A nested call that fails only returns its `Err`
51//! to the closure: the running search is not affected and can go on.
52//!
53//! # Example
54//!
55//! ```
56//! use igraph::{cliques::ColoringGreedy, prelude::*};
57//!
58//! // Two triangles {0, 1, 2} and {2, 3, 4} sharing vertex 2, plus the edge 4-5.
59//! let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3), (3, 4), (2, 4), (4, 5)], 6, false)
60//! .unwrap();
61//!
62//! assert_eq!(g.clique_number().unwrap(), 3);
63//! let mut largest = g.largest_cliques().unwrap();
64//! largest.iter_mut().for_each(|c| c.sort());
65//! largest.sort();
66//! assert_eq!(largest, vec![vec![0, 1, 2], vec![2, 3, 4]]);
67//!
68//! // Maximal cliques by size: none of size 1, one of size 2 (4-5), two triangles.
69//! assert_eq!(g.maximal_cliques_hist(..).unwrap(), vec![0, 1, 2]);
70//!
71//! // Vertices 0, 3 and 5 are pairwise non-adjacent.
72//! assert_eq!(g.independence_number().unwrap(), 3);
73//!
74//! // A proper coloring needs at least ω(G) = 3 colors.
75//! let colors = g.vertex_coloring_greedy(ColoringGreedy::DSatur).unwrap();
76//! assert!(g.is_vertex_coloring(&colors).unwrap());
77//! assert_eq!(colors.iter().max(), Some(&2));
78//!
79//! // Zachary's karate club: its 45 triangles are its cliques of size 3, and
80//! // its two largest cliques have 5 members.
81//! let karate = Graph::famous("Zachary").unwrap();
82//! assert_eq!(karate.clique_size_hist(3..=3).unwrap(), vec![0, 0, 45]);
83//! assert_eq!(karate.count_triangles().unwrap(), 45.0);
84//! assert_eq!(karate.clique_number().unwrap(), 5);
85//! ```
86//!
87//! # Provided functionality
88//!
89//! | Rust method | C function | Computes |
90//! |---|---|---|
91//! | [`cliques`](Graph::cliques) | `igraph_cliques` | all cliques in a size range |
92//! | [`cliques_callback`](Graph::cliques_callback) | `igraph_cliques_callback` | streams all cliques to a closure |
93//! | [`clique_size_hist`](Graph::clique_size_hist) | `igraph_clique_size_hist` | number of cliques of each size |
94//! | [`largest_cliques`](Graph::largest_cliques) | `igraph_largest_cliques` | the maximum cliques |
95//! | [`clique_number`](Graph::clique_number) | `igraph_clique_number` | ω(G) |
96//! | [`maximal_cliques`](Graph::maximal_cliques) | `igraph_maximal_cliques` | maximal cliques (Bron–Kerbosch) |
97//! | [`maximal_cliques_callback`](Graph::maximal_cliques_callback) | `igraph_maximal_cliques_callback` | streams maximal cliques to a closure |
98//! | [`maximal_cliques_count`](Graph::maximal_cliques_count) | `igraph_maximal_cliques_count` | number of maximal cliques |
99//! | [`maximal_cliques_hist`](Graph::maximal_cliques_hist) | `igraph_maximal_cliques_hist` | maximal cliques by size |
100//! | [`maximal_cliques_subset`](Graph::maximal_cliques_subset) | `igraph_maximal_cliques_subset` | maximal cliques started from some vertices |
101//! | [`write_maximal_cliques`](Graph::write_maximal_cliques) | `igraph_maximal_cliques_file` | writes maximal cliques to an [`io::Write`] |
102//! | [`weighted_cliques`](Graph::weighted_cliques) | `igraph_weighted_cliques` | (maximal) cliques in a weight range |
103//! | [`largest_weighted_cliques`](Graph::largest_weighted_cliques) | `igraph_largest_weighted_cliques` | heaviest cliques |
104//! | [`weighted_clique_number`](Graph::weighted_clique_number) | `igraph_weighted_clique_number` | weight of the heaviest clique |
105//! | [`independent_vertex_sets`](Graph::independent_vertex_sets) | `igraph_independent_vertex_sets` | all independent sets in a size range |
106//! | [`maximal_independent_vertex_sets`](Graph::maximal_independent_vertex_sets) | `igraph_maximal_independent_vertex_sets` | maximal independent sets |
107//! | [`largest_independent_vertex_sets`](Graph::largest_independent_vertex_sets) | `igraph_largest_independent_vertex_sets` | maximum independent sets |
108//! | [`independence_number`](Graph::independence_number) | `igraph_independence_number` | α(G) |
109//! | [`vertex_coloring_greedy`](Graph::vertex_coloring_greedy) | `igraph_vertex_coloring_greedy` | a proper vertex coloring |
110//! | [`is_vertex_coloring`](Graph::is_vertex_coloring) | `igraph_is_vertex_coloring` | checks a vertex coloring |
111//! | [`is_bipartite_coloring`](Graph::is_bipartite_coloring) | `igraph_is_bipartite_coloring` | checks a 2-coloring, and edge orientation |
112//! | [`is_edge_coloring`](Graph::is_edge_coloring) | `igraph_is_edge_coloring` | checks an edge coloring |
113//!
114//! Cliques and independent sets are returned as `Vec<Vec<VertexId>>`; the
115//! order of the sets, and of the vertices inside each set, is unspecified
116//! (sort them if you need a canonical form).
117//!
118//! # See also
119//!
120//! | Task | Elsewhere in the crate |
121//! |---|---|
122//! | test whether a *given* vertex set is a clique / independent set | [`Graph::is_clique`], [`Graph::is_independent_vertex_set`] (structural) |
123//! | triangles (the 3-cliques) only | [`Graph::count_triangles`], [`Graph::list_triangles`] (isomorphism), [`Graph::transitivity_undirected`] (mixing) |
124//! | switch between cliques and independent sets | [`Graph::complementer`] (operators): α(G) = ω(complement of G) |
125//! | degeneracy, which bounds the cost of [`maximal_cliques`](Graph::maximal_cliques) | [`Graph::coreness`]: the degeneracy is the largest coreness |
126//! | 2-colorings | [`Graph::is_bipartite`], [`Graph::bipartite_types`] (bipartite) |
127//! | graph classes where χ(G) = ω(G) | [`Graph::is_perfect`], [`Graph::is_chordal`] (structural) |
128//! | graphs with known ω, α and χ | [`Graph::full`], [`Graph::turan`], [`Graph::wheel`], [`Graph::famous`] (constructors), [`Graph::mycielskian`] (operators: triangle-free graphs of growing chromatic number) |
129
130use crate::{
131 constants::NeighborMode,
132 error::{Error, ErrorKind, Result, catch_panic},
133 ffi::*,
134 graph::{Graph, VertexId},
135 igraph_call,
136 list::VectorIntList,
137 vector::{Vector, VectorBool, VectorInt},
138};
139use std::{
140 cell::Cell,
141 ffi::{c_char, c_void},
142 io,
143 ops::{Bound, ControlFlow, RangeBounds},
144};
145
146crate::ffi_enum! {
147 /// Vertex ordering heuristic of [`Graph::vertex_coloring_greedy`]
148 /// (`igraph_coloring_greedy_t`).
149 pub enum ColoringGreedy: igraph_coloring_greedy_t {
150 /// Start from a vertex of maximum degree, then color next the vertex
151 /// with the largest number of already colored neighbors
152 /// (`IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS`). Both counts include
153 /// multi-edges with their multiplicity.
154 ColoredNeighbors = igraph_coloring_greedy_t_IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS,
155 /// Color next the vertex with the largest number of distinct colors
156 /// in its neighborhood (its *saturation degree*), breaking ties by the
157 /// number of uncolored neighbors (`IGRAPH_COLORING_GREEDY_DSATUR`).
158 /// This is Brélaz's DSatur heuristic (Commun. ACM 22(4), 1979,
159 /// <https://doi.org/10.1145/359094.359101>); it is exact on bipartite
160 /// graphs, cycles and wheels.
161 DSatur = igraph_coloring_greedy_t_IGRAPH_COLORING_GREEDY_DSATUR,
162 }
163}
164
165impl Default for ColoringGreedy {
166 /// [`ColoringGreedy::ColoredNeighbors`], igraph's historical default.
167 fn default() -> Self {
168 Self::ColoredNeighbors
169 }
170}
171
172/// Options of [`Graph::weighted_cliques`].
173///
174/// The default finds *all* cliques of any weight, without limits.
175///
176/// Cliquer only supports integer weights, so both bounds are truncated to
177/// their integer part. Unlike in C, where a non-positive maximum means "no
178/// bound", here `None` means "no bound" and a maximum below 1 (which no
179/// clique can meet, as vertex weights are at least 1) is rejected as an empty
180/// range.
181///
182/// ```
183/// use igraph::cliques::WeightedCliqueOptions;
184/// let opts = WeightedCliqueOptions::default().with_maximal(true).with_min_weight(7.0);
185/// assert!(opts.maximal && opts.max_weight.is_none());
186/// ```
187#[derive(Debug, Clone, Copy, PartialEq, Default)]
188pub struct WeightedCliqueOptions {
189 /// Only return *maximal* cliques (default `false`).
190 pub maximal: bool,
191 /// Minimum total weight of a returned clique (inclusive), `None` for no
192 /// lower bound. Truncated to its integer part; values of at most 1 are
193 /// the same as no bound.
194 pub min_weight: Option<f64>,
195 /// Maximum total weight of a returned clique (inclusive), `None` for no
196 /// upper bound. Truncated to its integer part.
197 pub max_weight: Option<f64>,
198 /// Stop after this many cliques were found, `None` for no limit
199 /// (`Some(0)` returns nothing).
200 pub max_results: Option<usize>,
201}
202
203impl WeightedCliqueOptions {
204 /// Sets the [`maximal`](#structfield.maximal) field.
205 pub fn with_maximal(mut self, maximal: bool) -> Self {
206 self.maximal = maximal;
207 self
208 }
209
210 /// Sets the [`min_weight`](#structfield.min_weight) field.
211 pub fn with_min_weight(mut self, weight: f64) -> Self {
212 self.min_weight = Some(weight);
213 self
214 }
215
216 /// Sets the [`max_weight`](#structfield.max_weight) field.
217 pub fn with_max_weight(mut self, weight: f64) -> Self {
218 self.max_weight = Some(weight);
219 self
220 }
221
222 /// Sets the [`max_results`](#structfield.max_results) field.
223 pub fn with_max_results(mut self, max_results: usize) -> Self {
224 self.max_results = Some(max_results);
225 self
226 }
227
228 /// Sets the [`maximal`](#structfield.maximal) field; use
229 /// [`with_maximal`](Self::with_maximal) instead.
230 #[deprecated(note = "use with_maximal")]
231 pub fn maximal(self, maximal: bool) -> Self {
232 self.with_maximal(maximal)
233 }
234
235 /// Sets the [`min_weight`](#structfield.min_weight) field; use
236 /// [`with_min_weight`](Self::with_min_weight) instead.
237 #[deprecated(note = "use with_min_weight")]
238 pub fn min_weight(self, weight: f64) -> Self {
239 self.with_min_weight(weight)
240 }
241
242 /// Sets the [`max_weight`](#structfield.max_weight) field; use
243 /// [`with_max_weight`](Self::with_max_weight) instead.
244 #[deprecated(note = "use with_max_weight")]
245 pub fn max_weight(self, weight: f64) -> Self {
246 self.with_max_weight(weight)
247 }
248
249 /// Sets the [`max_results`](#structfield.max_results) field; use
250 /// [`with_max_results`](Self::with_max_results) instead.
251 #[deprecated(note = "use with_max_results")]
252 pub fn max_results(self, max_results: usize) -> Self {
253 self.with_max_results(max_results)
254 }
255}
256
257/// Converts a size range into igraph's `(min_size, max_size)` convention,
258/// where a non-positive value means "no bound".
259///
260/// Returns `Ok(None)` when no vertex set of `graph` can satisfy the range
261/// (its lower bound exceeds the number of vertices): callers then return an
262/// empty result without calling igraph. This also keeps igraph away from
263/// absurd bounds: Cliquer allocates `min_size` integers up front and
264/// `igraph_clique_size_hist` allocates `max_size` doubles, and both cast the
265/// bounds to a C `int`.
266fn size_bounds(
267 graph: &Graph,
268 sizes: &impl RangeBounds<usize>,
269) -> Result<Option<(igraph_int_t, igraph_int_t)>> {
270 let min = match sizes.start_bound() {
271 Bound::Included(&n) => n,
272 Bound::Excluded(&n) => n.saturating_add(1),
273 Bound::Unbounded => 0,
274 };
275 let max = match sizes.end_bound() {
276 Bound::Included(&n) => Some(n),
277 Bound::Excluded(&n) => Some(n.checked_sub(1).ok_or_else(empty_range)?),
278 Bound::Unbounded => None,
279 };
280 if let Some(max) = max {
281 // igraph interprets 0 as "unbounded": reject it explicitly.
282 if max == 0 || max < min {
283 return Err(empty_range());
284 }
285 }
286 let n = graph.vcount();
287 if min > n {
288 return Ok(None);
289 }
290 // No set is larger than the vertex set: a larger bound is no bound.
291 let max = max.filter(|&m| m < n).unwrap_or(0);
292 Ok(Some((min as igraph_int_t, max as igraph_int_t)))
293}
294
295fn empty_range() -> Error {
296 Error::invalid("the range of admissible sizes is empty")
297}
298
299fn limit(max_results: Option<usize>) -> igraph_int_t {
300 max_results.map_or(IGRAPH_UNLIMITED as igraph_int_t, |n| {
301 igraph_int_t::try_from(n).unwrap_or(igraph_int_t::MAX)
302 })
303}
304
305/// Largest value Cliquer can represent (it stores weights as C `int`s).
306const CLIQUER_MAX: f64 = i32::MAX as f64;
307
308/// Validates vertex weights for Cliquer: finite, positive after truncation,
309/// and small enough that no clique weight overflows Cliquer's `int` sums.
310/// (igraph 1.0.0 and 1.0.1 convert them to `int` without checks, which is
311/// undefined behavior in C for NaN or out-of-range values.)
312fn check_weights(graph: &Graph, weights: Option<&[f64]>) -> Result<()> {
313 let Some(w) = weights else { return Ok(()) };
314 if w.len() != graph.vcount() {
315 return Err(Error::invalid(format!(
316 "the vertex weight vector has length {}, but the graph has {} vertices",
317 w.len(),
318 graph.vcount()
319 )));
320 }
321 if let Some((v, x)) = w
322 .iter()
323 .enumerate()
324 .find(|&(_, &x)| !(x.is_finite() && x.trunc() >= 1.0 && x <= CLIQUER_MAX))
325 {
326 return Err(Error::invalid(format!(
327 "vertex weights must be positive integers, but vertex {v} has weight {x}"
328 )));
329 }
330 let total: f64 = w.iter().map(|x| x.trunc()).sum();
331 if total > CLIQUER_MAX {
332 return Err(Error::invalid(format!(
333 "the total vertex weight {total} exceeds the limit {CLIQUER_MAX} of Cliquer"
334 )));
335 }
336 Ok(())
337}
338
339/// Converts the weight range of [`WeightedCliqueOptions`] into igraph's
340/// `(min_weight, max_weight)` convention (0 meaning "no bound").
341///
342/// `heaviest` is an upper bound on the weight of any clique (the total
343/// vertex weight, or the vertex count without weights). Returns `Ok(None)`
344/// when no clique can reach `min_weight`, so that callers return an empty
345/// result without calling igraph; a maximum of at least `heaviest` is no
346/// bound. This also keeps both bounds within Cliquer's C `int` range, and
347/// keeps absurd sizes away from the unweighted fallbacks.
348fn weight_bounds(options: &WeightedCliqueOptions, heaviest: f64) -> Result<Option<(f64, f64)>> {
349 let finite = |x: f64, what: &str| {
350 if x.is_finite() {
351 Ok(x.trunc())
352 } else {
353 Err(Error::invalid(format!(
354 "the {what} weight must be finite, got {x}"
355 )))
356 }
357 };
358 let min = match options.min_weight {
359 Some(m) => finite(m, "minimum")?.max(0.0),
360 None => 0.0,
361 };
362 let max = match options.max_weight {
363 Some(m) => {
364 let m = finite(m, "maximum")?;
365 // igraph reads a non-positive maximum as "no bound": since
366 // weights are at least 1, such a range is really empty.
367 if m < 1.0 || m < min {
368 return Err(Error::invalid(
369 "the range of admissible clique weights is empty",
370 ));
371 }
372 m
373 }
374 None => 0.0,
375 };
376 if min > heaviest {
377 return Ok(None);
378 }
379 Ok(Some((min, if max >= heaviest { 0.0 } else { max })))
380}
381
382thread_local! {
383 /// Whether a Cliquer-based search is running on this thread.
384 static CLIQUER_ACTIVE: Cell<bool> = const { Cell::new(false) };
385}
386
387/// Marks a Cliquer-based search as running on this thread.
388///
389/// igraph's Cliquer wrapper (`src/cliques/cliquer_wrapper.c`, unchanged in
390/// igraph 1.0.0 and 1.0.1) keeps its search options (the result sink and
391/// the per-clique callback) in a *thread-local global* that every search
392/// overwrites and never restores. Starting a second Cliquer search from
393/// inside the closure of [`Graph::cliques_callback`] would therefore leave
394/// the outer search writing through dangling pointers. This guard turns
395/// such a nested call into an error instead.
396struct CliquerGuard;
397
398impl CliquerGuard {
399 fn enter() -> Result<Self> {
400 if CLIQUER_ACTIVE.with(|a| a.replace(true)) {
401 return Err(Error::new(
402 ErrorKind::Failure,
403 "Cliquer-based clique searches (cliques, clique_size_hist, cliques_callback \
404 and the weighted clique functions) cannot be nested inside a \
405 cliques_callback closure",
406 ));
407 }
408 Ok(CliquerGuard)
409 }
410}
411
412impl Drop for CliquerGuard {
413 fn drop(&mut self) {
414 CLIQUER_ACTIVE.with(|a| a.set(false));
415 }
416}
417
418/// Runs `f`, an igraph call on `graph`, with the graph's property cache
419/// dropped before and after it when `graph` is directed.
420///
421/// igraph bug (1.0.0 and 1.0.1): many functions that ignore edge directions
422/// (triangles, cliques, independent sets, greedy coloring, ...) build an
423/// adjacency list with `igraph_adjlist_init(.., IGRAPH_ALL, ..,
424/// IGRAPH_NO_MULTIPLE)` (or the lazy variant), which trusts and updates the
425/// cached "has multi-edges" flag. In mode ALL, a mutual pair `u -> v`,
426/// `v -> u` of a *directed* graph looks like a multi-edge, so:
427/// - if "no multi-edges" was already cached (a correct value, e.g. written by
428/// [`Graph::has_multiple`]), the deduplication is skipped and mutual
429/// neighbors are counted twice (wrong triangle counts, duplicated cliques);
430/// - otherwise "has multi-edges" is cached, which is wrong for the directed
431/// graph (igraph only calls same-direction edges multi-edges), and it
432/// survives edge additions and clones.
433///
434/// Dropping the cache on both sides avoids both; it only costs the
435/// recomputation of cached properties. `Graph` is `Send` but not `Sync`, so
436/// no other thread can observe the cache through `&Graph` meanwhile. The
437/// cache is also dropped when `f` panics (e.g. resuming a callback panic).
438pub(crate) fn directed_cache_guard<T>(graph: &Graph, f: impl FnOnce() -> T) -> T {
439 graph.with_fresh_multi_cache(f)
440}
441
442fn counts(hist: Vector) -> Vec<usize> {
443 hist.iter().map(|&c| c as usize).collect()
444}
445
446/// C trampoline running a Rust closure for each clique.
447///
448/// The closure runs in a fresh level of igraph's "finally" stack
449/// (`IGRAPH_FINALLY_ENTER` / `IGRAPH_FINALLY_EXIT`). The running clique
450/// search keeps its temporary objects on that stack, and igraph's error
451/// handler frees the current level when a call fails: without the new level,
452/// an igraph call made by the closure that fails (and returns an `Err` to the
453/// closure, which may well ignore it) would free the objects of the search
454/// that is still running, a use-after-free in C.
455///
456/// [`catch_panic`] does not open a level itself (as of this writing), so this
457/// is the protection for the clique callbacks. Should it start doing so, this
458/// level becomes redundant but stays harmless: finally levels nest, and each
459/// one is closed by its own opener.
460unsafe extern "C" fn clique_trampoline<F>(
461 clique: *const igraph_vector_int_t,
462 arg: *mut c_void,
463) -> igraph_error_t
464where
465 F: FnMut(&[VertexId]) -> ControlFlow<()>,
466{
467 // SAFETY: plain bookkeeping on igraph's thread-local finally stack; the
468 // matching EXIT runs below, as `catch_panic` never unwinds.
469 unsafe { IGRAPH_FINALLY_ENTER() };
470 let code = catch_panic(|| {
471 // SAFETY: `arg` is the `&mut F` handed to igraph by the wrapper that
472 // started the search, and `clique` is a valid vector owned by igraph
473 // for the duration of this call.
474 let f = unsafe { &mut *(arg as *mut F) };
475 let clique = unsafe { &*clique };
476 match f(clique.as_slice()) {
477 ControlFlow::Continue(()) => igraph_error_type_t_IGRAPH_SUCCESS,
478 ControlFlow::Break(()) => igraph_error_type_t_IGRAPH_STOP,
479 }
480 });
481 // SAFETY: closes the level opened above. Every igraph call made by the
482 // closure has returned, and a failed one has already freed its own
483 // objects, so the level is empty again.
484 unsafe { IGRAPH_FINALLY_EXIT() };
485 code
486}
487
488/// Opens an in-memory C stream, runs `f` on it and returns what was written.
489fn with_memstream(f: impl FnOnce(*mut FILE) -> Result<()>) -> Result<Vec<u8>> {
490 let mut buf: *mut c_char = std::ptr::null_mut();
491 let mut size: usize = 0;
492 // SAFETY: `buf` and `size` outlive the stream, which is closed below.
493 let file = unsafe { open_memstream(&mut buf, &mut size) };
494 if file.is_null() {
495 return Err(Error::new(
496 ErrorKind::File,
497 "cannot open an in-memory stream",
498 ));
499 }
500 let outcome = f(file);
501 // SAFETY: closing the stream finalizes `buf`/`size`; `buf` is then ours to free.
502 let closed = unsafe { fclose(file) } == 0;
503 let bytes = if buf.is_null() {
504 Vec::new()
505 } else {
506 let bytes = unsafe { std::slice::from_raw_parts(buf as *const u8, size) }.to_vec();
507 unsafe { free(buf as *mut c_void) };
508 bytes
509 };
510 outcome?;
511 if !closed {
512 return Err(Error::new(
513 ErrorKind::File,
514 "cannot flush an in-memory stream",
515 ));
516 }
517 Ok(bytes)
518}
519
520impl igraph_t {
521 // ------------------------------------------------------------------
522 // All cliques (Cliquer)
523 // ------------------------------------------------------------------
524
525 /// Finds all cliques whose size lies in `sizes`.
526 ///
527 /// Every clique is reported, not only the maximal ones: a triangle
528 /// contributes three cliques of size 1, three of size 2 and one of size 3.
529 /// The search stops after `max_results` cliques when that is `Some`.
530 /// If you only need the size of the largest clique, use
531 /// [`clique_number`](Self::clique_number) instead; to only list the
532 /// maximal ones, use [`maximal_cliques`](Self::maximal_cliques). Edge
533 /// directions, self-loops and multi-edges are ignored.
534 ///
535 /// See also [`is_clique`](Self::is_clique) to test a given vertex set,
536 /// and [`list_triangles`](Self::list_triangles) for cliques of size 3
537 /// only.
538 ///
539 /// The implementation uses the Cliquer library (version 1.21) by Sampo
540 /// Niskanen and Patric R. J. Östergård. Time complexity: exponential.
541 ///
542 /// Binds [`igraph_cliques`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_cliques).
543 ///
544 /// # Errors
545 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
546 /// [`ErrorKind::Failure`] if called from inside a
547 /// [`cliques_callback`](Self::cliques_callback) closure (see the
548 /// [module docs](self#nesting-searches-in-callbacks)).
549 ///
550 /// # Examples
551 ///
552 /// ```
553 /// use igraph::prelude::*;
554 /// // K4 has C(4, 3) = 4 triangles.
555 /// let k4 = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)], 4, false)
556 /// .unwrap();
557 /// assert_eq!(k4.cliques(3..=3, None).unwrap().len(), 4);
558 /// // 15 = 2^4 - 1 non-empty cliques in total, but we only want two of them.
559 /// assert_eq!(k4.cliques(.., None).unwrap().len(), 15);
560 /// assert_eq!(k4.cliques(.., Some(2)).unwrap().len(), 2);
561 /// ```
562 pub fn cliques(
563 &self,
564 sizes: impl RangeBounds<usize>,
565 max_results: Option<usize>,
566 ) -> Result<Vec<Vec<VertexId>>> {
567 let Some((min, max)) = size_bounds(self, &sizes)? else {
568 return Ok(Vec::new());
569 };
570 let _guard = CliquerGuard::enter()?;
571 let mut res = VectorIntList::new();
572 igraph_call!(igraph_cliques(self, &mut res, min, max, limit(max_results)))?;
573 Ok(res.to_vecs())
574 }
575
576 /// Calls `f` for each clique whose size lies in `sizes`, without storing
577 /// them.
578 ///
579 /// The closure receives the vertex ids of the clique (a borrowed slice,
580 /// copy it if you want to keep it) and returns
581 /// [`ControlFlow::Continue`] to go on or [`ControlFlow::Break`] to stop the
582 /// search early; stopping is not an error. Cliques are produced in the
583 /// same order as by [`cliques`](Self::cliques). A panic inside `f` stops
584 /// the search and is propagated to the caller.
585 ///
586 /// The closure must not start another Cliquer-based search
587 /// ([`cliques`](Self::cliques), [`clique_size_hist`](Self::clique_size_hist),
588 /// `cliques_callback` itself or the weighted clique functions): such
589 /// nested calls return an [`ErrorKind::Failure`] error, because igraph
590 /// keeps the state of the running search in per-thread globals. Other
591 /// functions, e.g. [`maximal_cliques`](Self::maximal_cliques), are fine.
592 ///
593 /// Binds [`igraph_cliques_callback`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_cliques_callback).
594 ///
595 /// # Errors
596 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
597 /// [`ErrorKind::Failure`] if called from inside another
598 /// `cliques_callback` closure.
599 ///
600 /// # Examples
601 ///
602 /// ```
603 /// use igraph::prelude::*;
604 /// use std::ops::ControlFlow;
605 /// let k5 = Graph::from_edges(
606 /// &[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)],
607 /// 5,
608 /// false,
609 /// )
610 /// .unwrap();
611 /// // Find the first triangle containing vertex 4, then stop.
612 /// let mut found = None;
613 /// k5.cliques_callback(3..=3, |c| {
614 /// if c.contains(&4) {
615 /// found = Some(c.to_vec());
616 /// ControlFlow::Break(())
617 /// } else {
618 /// ControlFlow::Continue(())
619 /// }
620 /// })
621 /// .unwrap();
622 /// assert!(found.unwrap().contains(&4));
623 /// ```
624 pub fn cliques_callback<F>(&self, sizes: impl RangeBounds<usize>, mut f: F) -> Result<()>
625 where
626 F: FnMut(&[VertexId]) -> ControlFlow<()>,
627 {
628 let Some((min, max)) = size_bounds(self, &sizes)? else {
629 return Ok(());
630 };
631 let _guard = CliquerGuard::enter()?;
632 igraph_call!(igraph_cliques_callback(
633 self,
634 min,
635 max,
636 Some(clique_trampoline::<F>),
637 &mut f as *mut F as *mut c_void
638 ))
639 }
640
641 /// Counts the cliques of each size.
642 ///
643 /// Element `i` of the result is the number of cliques of size `i + 1`;
644 /// sizes below the lower bound of `sizes` get a zero count, and the vector
645 /// ends at the largest size found (so it is empty for the null graph).
646 /// The counts are those of [`cliques`](Self::cliques), without storing the
647 /// cliques: `hist[0]` is the number of vertices, `hist[1]` the number of
648 /// adjacent vertex pairs and `hist[2]` the number of triangles (see
649 /// [`count_triangles`](Self::count_triangles)). Uses Cliquer; time
650 /// complexity: exponential.
651 ///
652 /// Binds [`igraph_clique_size_hist`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_clique_size_hist).
653 ///
654 /// # Errors
655 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
656 /// [`ErrorKind::Failure`] if called from inside a
657 /// [`cliques_callback`](Self::cliques_callback) closure.
658 ///
659 /// # Examples
660 ///
661 /// ```
662 /// use igraph::prelude::*;
663 /// // A triangle: 3 vertices, 3 edges, 1 triangle.
664 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
665 /// assert_eq!(g.clique_size_hist(..).unwrap(), vec![3, 3, 1]);
666 /// assert_eq!(g.clique_size_hist(2..).unwrap(), vec![0, 3, 1]);
667 /// ```
668 pub fn clique_size_hist(&self, sizes: impl RangeBounds<usize>) -> Result<Vec<usize>> {
669 let Some((min, max)) = size_bounds(self, &sizes)? else {
670 return Ok(Vec::new());
671 };
672 let _guard = CliquerGuard::enter()?;
673 let mut hist = Vector::new();
674 igraph_call!(igraph_clique_size_hist(self, &mut hist, min, max))?;
675 Ok(counts(hist))
676 }
677
678 /// Finds all the largest (maximum) cliques.
679 ///
680 /// A clique is *largest* if no other clique has more vertices. Largest
681 /// cliques are always maximal, but a maximal clique need not be largest.
682 /// The null graph has no cliques at all. The maximal cliques are
683 /// enumerated with the same algorithm as
684 /// [`maximal_cliques`](Self::maximal_cliques), keeping only the largest.
685 /// All returned cliques have [`clique_number`](Self::clique_number)
686 /// vertices.
687 ///
688 /// Time complexity: O(3^(|V|/3)) in the worst case.
689 ///
690 /// Binds [`igraph_largest_cliques`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_largest_cliques).
691 ///
692 /// # Examples
693 ///
694 /// ```
695 /// use igraph::prelude::*;
696 /// // The two 5-cliques of Zachary's karate club share the four leaders
697 /// // 0, 1, 2, 3.
698 /// let karate = Graph::famous("Zachary").unwrap();
699 /// let mut largest = karate.largest_cliques().unwrap();
700 /// largest.iter_mut().for_each(|c| c.sort());
701 /// largest.sort();
702 /// assert_eq!(largest, vec![vec![0, 1, 2, 3, 7], vec![0, 1, 2, 3, 13]]);
703 /// ```
704 pub fn largest_cliques(&self) -> Result<Vec<Vec<VertexId>>> {
705 let mut res = VectorIntList::new();
706 directed_cache_guard(self, || {
707 igraph_call!(igraph_largest_cliques(self, &mut res))
708 })?;
709 Ok(res.to_vecs())
710 }
711
712 /// The clique number ω(G): the size of the largest clique.
713 ///
714 /// It is 0 for the null graph and 1 for a graph without edges. It is a
715 /// lower bound for the chromatic number, i.e. the number of colors of
716 /// any proper coloring (see
717 /// [`vertex_coloring_greedy`](Self::vertex_coloring_greedy)); the two
718 /// are equal for [perfect](Self::is_perfect) graphs.
719 /// Time complexity: O(3^(|V|/3)) in the worst case.
720 ///
721 /// Binds [`igraph_clique_number`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_clique_number).
722 ///
723 /// # Examples
724 ///
725 /// ```
726 /// use igraph::prelude::*;
727 /// let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
728 /// assert_eq!(path.clique_number().unwrap(), 2);
729 /// assert_eq!(Graph::new(0, false).clique_number().unwrap(), 0);
730 /// ```
731 pub fn clique_number(&self) -> Result<usize> {
732 let mut no: igraph_int_t = 0;
733 directed_cache_guard(self, || igraph_call!(igraph_clique_number(self, &mut no)))?;
734 Ok(no as usize)
735 }
736
737 // ------------------------------------------------------------------
738 // Maximal cliques (Eppstein–Löffler–Strash)
739 // ------------------------------------------------------------------
740
741 /// Finds the maximal cliques whose size lies in `sizes`.
742 ///
743 /// A *maximal* clique is not a proper subset of any other clique.
744 /// Isolated vertices are maximal cliques of size 1. No guarantees are given
745 /// about the order of the cliques or of the vertices inside them.
746 ///
747 /// The implementation is the Bron–Kerbosch variant with degeneracy
748 /// ordering by Eppstein, Löffler and Strash (2010,
749 /// <https://arxiv.org/abs/1006.5440>). Time complexity: O(d (n − d)
750 /// 3^(d/3)) in the worst case, where d is the degeneracy of the graph
751 /// (typically small for sparse graphs; it is the largest value of
752 /// [`coreness`](Self::coreness)).
753 ///
754 /// See also [`maximal_cliques_count`](Self::maximal_cliques_count),
755 /// [`maximal_cliques_hist`](Self::maximal_cliques_hist) and
756 /// [`maximal_cliques_callback`](Self::maximal_cliques_callback) to avoid
757 /// storing the cliques, and
758 /// [`maximal_independent_vertex_sets`](Self::maximal_independent_vertex_sets)
759 /// for the maximal cliques of the complementer graph.
760 ///
761 /// Binds [`igraph_maximal_cliques`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_maximal_cliques).
762 ///
763 /// # Errors
764 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
765 ///
766 /// # Examples
767 ///
768 /// ```
769 /// use igraph::prelude::*;
770 /// // A "bowtie": triangles {0,1,2} and {2,3,4}.
771 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)
772 /// .unwrap();
773 /// let mut cliques = g.maximal_cliques(.., None).unwrap();
774 /// cliques.iter_mut().for_each(|c| c.sort());
775 /// cliques.sort();
776 /// assert_eq!(cliques, vec![vec![0, 1, 2], vec![2, 3, 4]]);
777 /// ```
778 pub fn maximal_cliques(
779 &self,
780 sizes: impl RangeBounds<usize>,
781 max_results: Option<usize>,
782 ) -> Result<Vec<Vec<VertexId>>> {
783 let Some((min, max)) = size_bounds(self, &sizes)? else {
784 return Ok(Vec::new());
785 };
786 let mut res = VectorIntList::new();
787 directed_cache_guard(self, || {
788 igraph_call!(igraph_maximal_cliques(
789 self,
790 &mut res,
791 min,
792 max,
793 limit(max_results)
794 ))
795 })?;
796 Ok(res.to_vecs())
797 }
798
799 /// Calls `f` for each maximal clique whose size lies in `sizes`.
800 ///
801 /// This is the streaming version of [`maximal_cliques`](Self::maximal_cliques):
802 /// the cliques are not stored, which is useful for graphs with a huge
803 /// number of them. The closure receives a borrowed slice (copy it to keep
804 /// it) and returns [`ControlFlow::Break`] to stop the search early, which
805 /// is not an error. A panic inside `f` is propagated to the caller.
806 ///
807 /// Binds [`igraph_maximal_cliques_callback`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_maximal_cliques_callback).
808 ///
809 /// # Errors
810 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
811 ///
812 /// # Examples
813 ///
814 /// ```
815 /// use igraph::prelude::*;
816 /// use std::ops::ControlFlow;
817 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
818 /// let mut sizes = vec![];
819 /// g.maximal_cliques_callback(.., |c| {
820 /// sizes.push(c.len());
821 /// ControlFlow::Continue(())
822 /// })
823 /// .unwrap();
824 /// sizes.sort();
825 /// assert_eq!(sizes, vec![2, 3]);
826 /// ```
827 pub fn maximal_cliques_callback<F>(
828 &self,
829 sizes: impl RangeBounds<usize>,
830 mut f: F,
831 ) -> Result<()>
832 where
833 F: FnMut(&[VertexId]) -> ControlFlow<()>,
834 {
835 let Some((min, max)) = size_bounds(self, &sizes)? else {
836 return Ok(());
837 };
838 directed_cache_guard(self, || {
839 igraph_call!(igraph_maximal_cliques_callback(
840 self,
841 min,
842 max,
843 Some(clique_trampoline::<F>),
844 &mut f as *mut F as *mut c_void
845 ))
846 })
847 }
848
849 /// Counts the maximal cliques whose size lies in `sizes`, without storing
850 /// them.
851 ///
852 /// Same algorithm and complexity as [`maximal_cliques`](Self::maximal_cliques).
853 ///
854 /// Binds [`igraph_maximal_cliques_count`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_maximal_cliques_count).
855 ///
856 /// # Errors
857 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
858 ///
859 /// # Examples
860 ///
861 /// ```
862 /// use igraph::prelude::*;
863 /// // Each edge of a 5-cycle is a maximal clique.
864 /// let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
865 /// assert_eq!(c5.maximal_cliques_count(..).unwrap(), 5);
866 /// assert_eq!(c5.maximal_cliques_count(3..).unwrap(), 0);
867 /// ```
868 pub fn maximal_cliques_count(&self, sizes: impl RangeBounds<usize>) -> Result<usize> {
869 let Some((min, max)) = size_bounds(self, &sizes)? else {
870 return Ok(0);
871 };
872 let mut no: igraph_int_t = 0;
873 directed_cache_guard(self, || {
874 igraph_call!(igraph_maximal_cliques_count(self, &mut no, min, max))
875 })?;
876 Ok(no as usize)
877 }
878
879 /// Counts the maximal cliques of each size.
880 ///
881 /// Element `i` of the result is the number of maximal cliques of size
882 /// `i + 1` (size-1 maximal cliques are the isolated vertices); sizes below
883 /// the lower bound of `sizes` get a zero count, and the vector ends at the
884 /// largest size found. The counts sum to
885 /// [`maximal_cliques_count`](Self::maximal_cliques_count), and the length
886 /// of the unrestricted histogram is the
887 /// [`clique_number`](Self::clique_number).
888 ///
889 /// Binds [`igraph_maximal_cliques_hist`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_maximal_cliques_hist).
890 ///
891 /// # Errors
892 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
893 ///
894 /// # Examples
895 ///
896 /// ```
897 /// use igraph::prelude::*;
898 /// // A triangle with a pendant edge, plus an isolated vertex.
899 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 5, false).unwrap();
900 /// assert_eq!(g.maximal_cliques_hist(..).unwrap(), vec![1, 1, 1]);
901 /// assert_eq!(g.maximal_cliques_hist(2..).unwrap(), vec![0, 1, 1]);
902 /// ```
903 pub fn maximal_cliques_hist(&self, sizes: impl RangeBounds<usize>) -> Result<Vec<usize>> {
904 let Some((min, max)) = size_bounds(self, &sizes)? else {
905 return Ok(Vec::new());
906 };
907 let mut hist = Vector::new();
908 directed_cache_guard(self, || {
909 igraph_call!(igraph_maximal_cliques_hist(self, &mut hist, min, max))
910 })?;
911 Ok(counts(hist))
912 }
913
914 /// Finds the maximal cliques reached from a subset of initial vertices.
915 ///
916 /// The Eppstein–Löffler–Strash algorithm processes the vertices one by one
917 /// in degeneracy order, each time listing the maximal cliques that contain
918 /// that vertex and none of the vertices processed before it. This function
919 /// only runs the outer loop over the vertices in `subset`: running it on
920 /// the parts of a partition of the vertex set and concatenating the results
921 /// yields every maximal clique exactly once, which makes it a building
922 /// block for parallel enumeration.
923 ///
924 /// Binds [`igraph_maximal_cliques_subset`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_maximal_cliques_subset)
925 /// (without its optional file output, see
926 /// [`write_maximal_cliques`](Self::write_maximal_cliques)).
927 ///
928 /// # Errors
929 /// [`ErrorKind::InvalidValue`] if `sizes` is empty,
930 /// [`ErrorKind::InvalidVertexId`] if `subset` contains an invalid id.
931 ///
932 /// # Examples
933 ///
934 /// ```
935 /// use igraph::prelude::*;
936 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)
937 /// .unwrap();
938 /// let all = g.maximal_cliques(.., None).unwrap().len();
939 /// let evens = g.maximal_cliques_subset(&[0, 2, 4], .., None).unwrap();
940 /// let odds = g.maximal_cliques_subset(&[1, 3], .., None).unwrap();
941 /// assert_eq!(evens.len() + odds.len(), all);
942 /// ```
943 pub fn maximal_cliques_subset(
944 &self,
945 subset: &[VertexId],
946 sizes: impl RangeBounds<usize>,
947 max_results: Option<usize>,
948 ) -> Result<Vec<Vec<VertexId>>> {
949 let bounds = size_bounds(self, &sizes)?;
950 let n = self.vcount() as VertexId;
951 if let Some(&v) = subset.iter().find(|&&v| !(0..n).contains(&v)) {
952 return Err(Error::new(
953 ErrorKind::InvalidVertexId,
954 format!("vertex id {v} is out of range in the subset of initial vertices"),
955 ));
956 }
957 let Some((min, max)) = bounds else {
958 return Ok(Vec::new());
959 };
960 let subset = VectorInt::view(subset);
961 let mut res = VectorIntList::new();
962 let mut no: igraph_int_t = 0;
963 directed_cache_guard(self, || {
964 igraph_call!(igraph_maximal_cliques_subset(
965 self,
966 subset.as_ptr(),
967 &mut res,
968 &mut no,
969 std::ptr::null_mut(),
970 min,
971 max,
972 limit(max_results)
973 ))
974 })?;
975 Ok(res.to_vecs())
976 }
977
978 /// Writes the maximal cliques whose size lies in `sizes` to `out`, one
979 /// clique per line as space-separated vertex ids.
980 ///
981 /// This mirrors the C function, which streams the cliques to a `FILE *`
982 /// instead of storing them: igraph writes into an in-memory stream that is
983 /// then copied into `out`.
984 ///
985 /// Binds [`igraph_maximal_cliques_file`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_maximal_cliques_file).
986 ///
987 /// # Errors
988 /// [`ErrorKind::InvalidValue`] if `sizes` is empty, [`ErrorKind::File`]
989 /// if writing to `out` (or to the intermediate stream) fails.
990 ///
991 /// # Examples
992 ///
993 /// ```
994 /// use igraph::prelude::*;
995 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 4, false).unwrap();
996 /// let mut out = Vec::new();
997 /// g.write_maximal_cliques(&mut out, 2.., None).unwrap();
998 /// let text = String::from_utf8(out).unwrap();
999 /// assert_eq!(text.lines().count(), 1); // the triangle; vertex 3 is too small
1000 /// ```
1001 pub fn write_maximal_cliques<W: io::Write + ?Sized>(
1002 &self,
1003 out: &mut W,
1004 sizes: impl RangeBounds<usize>,
1005 max_results: Option<usize>,
1006 ) -> Result<()> {
1007 let Some((min, max)) = size_bounds(self, &sizes)? else {
1008 return Ok(());
1009 };
1010 let bytes = with_memstream(|file| {
1011 directed_cache_guard(self, || {
1012 igraph_call!(igraph_maximal_cliques_file(
1013 self,
1014 file,
1015 min,
1016 max,
1017 limit(max_results)
1018 ))
1019 })
1020 })?;
1021 out.write_all(&bytes)
1022 .map_err(|e| Error::new(ErrorKind::File, e.to_string()))
1023 }
1024
1025 // ------------------------------------------------------------------
1026 // Weighted cliques (Cliquer)
1027 // ------------------------------------------------------------------
1028
1029 /// Finds the cliques whose total vertex weight lies in a range.
1030 ///
1031 /// The weight of a clique is the sum of the weights of its vertices.
1032 /// Only positive integer weights are supported: fractional weights (and
1033 /// weight bounds) are truncated to their integer part, with a warning.
1034 /// Weights must be finite, at least 1, and sum to at most `i32::MAX`
1035 /// (Cliquer computes with C `int`s).
1036 /// With `weights = None` every vertex weighs 1, so weights are sizes and
1037 /// this is [`cliques`](Self::cliques) or [`maximal_cliques`](Self::maximal_cliques).
1038 /// See [`WeightedCliqueOptions`] for the range, maximality and limit.
1039 ///
1040 /// Uses the Cliquer library. Time complexity: exponential.
1041 ///
1042 /// Binds [`igraph_weighted_cliques`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_weighted_cliques).
1043 ///
1044 /// # Errors
1045 /// [`ErrorKind::InvalidValue`] if `weights` has the wrong length or an
1046 /// invalid weight (see above), if a bound is not finite, or if the weight
1047 /// range is empty (`max_weight < 1` or `max_weight < min_weight`).
1048 /// [`ErrorKind::Failure`] if called from inside a
1049 /// [`cliques_callback`](Self::cliques_callback) closure (see the
1050 /// [module docs](self#nesting-searches-in-callbacks)).
1051 ///
1052 /// # Examples
1053 ///
1054 /// ```
1055 /// use igraph::{cliques::WeightedCliqueOptions, prelude::*};
1056 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
1057 /// let w = [1.0, 1.0, 1.0, 10.0];
1058 /// // Maximal cliques weighing at least 5: only the edge {2, 3} (weight 11).
1059 /// let opts = WeightedCliqueOptions::default().with_maximal(true).with_min_weight(5.0);
1060 /// let mut heavy = g.weighted_cliques(Some(&w), &opts).unwrap();
1061 /// heavy[0].sort();
1062 /// assert_eq!(heavy, vec![vec![2, 3]]);
1063 /// ```
1064 pub fn weighted_cliques(
1065 &self,
1066 weights: Option<&[f64]>,
1067 options: &WeightedCliqueOptions,
1068 ) -> Result<Vec<Vec<VertexId>>> {
1069 check_weights(self, weights)?;
1070 // Validated above: the sum is exact and at most `i32::MAX`.
1071 let heaviest = weights.map_or(self.vcount() as f64, |w| w.iter().map(|x| x.trunc()).sum());
1072 let Some((min, max)) = weight_bounds(options, heaviest)? else {
1073 return Ok(Vec::new());
1074 };
1075 // Without weights, maximal cliques use the Eppstein–Löffler–Strash
1076 // algorithm; every other case goes through Cliquer.
1077 let _guard = if weights.is_some() || !options.maximal {
1078 Some(CliquerGuard::enter()?)
1079 } else {
1080 None
1081 };
1082 let w = weights.map(Vector::view);
1083 let wp = w.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
1084 let mut res = VectorIntList::new();
1085 igraph_call!(igraph_weighted_cliques(
1086 self,
1087 wp,
1088 &mut res,
1089 options.maximal,
1090 min,
1091 max,
1092 limit(options.max_results)
1093 ))?;
1094 Ok(res.to_vecs())
1095 }
1096
1097 /// Finds the cliques of largest total vertex weight.
1098 ///
1099 /// Only positive integer weights are supported (fractional weights are
1100 /// truncated). With `weights = None` this is
1101 /// [`largest_cliques`](Self::largest_cliques). The total weight of every
1102 /// returned clique is the
1103 /// [`weighted_clique_number`](Self::weighted_clique_number). A heaviest
1104 /// clique is always maximal, but it need not be a largest one.
1105 ///
1106 /// Uses the Cliquer library when `weights` is given. Time complexity:
1107 /// exponential.
1108 ///
1109 /// Binds [`igraph_largest_weighted_cliques`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_largest_weighted_cliques).
1110 ///
1111 /// # Errors
1112 /// [`ErrorKind::InvalidValue`] if `weights` has the wrong length or a
1113 /// weight that is not finite, below 1, or makes the total exceed
1114 /// `i32::MAX`. [`ErrorKind::Failure`] if `weights` is given and this is
1115 /// called from inside a [`cliques_callback`](Self::cliques_callback)
1116 /// closure.
1117 ///
1118 /// # Examples
1119 ///
1120 /// ```
1121 /// use igraph::prelude::*;
1122 /// // A triangle {0, 1, 2} of light vertices and a heavy edge {2, 3}.
1123 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
1124 /// let mut heaviest = g.largest_weighted_cliques(Some(&[1.0, 1.0, 1.0, 10.0])).unwrap();
1125 /// heaviest[0].sort();
1126 /// assert_eq!(heaviest, vec![vec![2, 3]]);
1127 /// // Unweighted, the triangle wins.
1128 /// assert_eq!(g.largest_weighted_cliques(None).unwrap().len(), 1);
1129 /// assert_eq!(g.largest_weighted_cliques(None).unwrap()[0].len(), 3);
1130 /// ```
1131 pub fn largest_weighted_cliques(&self, weights: Option<&[f64]>) -> Result<Vec<Vec<VertexId>>> {
1132 check_weights(self, weights)?;
1133 let _guard = weights.map(|_| CliquerGuard::enter()).transpose()?;
1134 let w = weights.map(Vector::view);
1135 let wp = w.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
1136 let mut res = VectorIntList::new();
1137 igraph_call!(igraph_largest_weighted_cliques(self, wp, &mut res))?;
1138 Ok(res.to_vecs())
1139 }
1140
1141 /// The weighted clique number: the largest total vertex weight of a
1142 /// clique.
1143 ///
1144 /// Only positive integer weights are supported (fractional weights are
1145 /// truncated). With `weights = None` this is the
1146 /// [`clique_number`](Self::clique_number). It is 0 for the null graph.
1147 /// Uses the Cliquer library when `weights` is given. Time complexity:
1148 /// exponential.
1149 ///
1150 /// Binds [`igraph_weighted_clique_number`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_weighted_clique_number).
1151 ///
1152 /// # Errors
1153 /// [`ErrorKind::InvalidValue`] if `weights` has the wrong length or a
1154 /// weight that is not finite, below 1, or makes the total exceed
1155 /// `i32::MAX`. [`ErrorKind::Failure`] if `weights` is given and this is
1156 /// called from inside a [`cliques_callback`](Self::cliques_callback)
1157 /// closure.
1158 ///
1159 /// # Examples
1160 ///
1161 /// ```
1162 /// use igraph::prelude::*;
1163 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
1164 /// assert_eq!(g.weighted_clique_number(Some(&[1.0, 1.0, 1.0, 10.0])).unwrap(), 11.0);
1165 /// assert_eq!(g.weighted_clique_number(None).unwrap(), 3.0);
1166 /// ```
1167 pub fn weighted_clique_number(&self, weights: Option<&[f64]>) -> Result<f64> {
1168 check_weights(self, weights)?;
1169 let _guard = weights.map(|_| CliquerGuard::enter()).transpose()?;
1170 let w = weights.map(Vector::view);
1171 let wp = w.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
1172 let mut res: igraph_real_t = 0.0;
1173 igraph_call!(igraph_weighted_clique_number(self, wp, &mut res))?;
1174 Ok(res)
1175 }
1176
1177 // ------------------------------------------------------------------
1178 // Independent vertex sets
1179 // ------------------------------------------------------------------
1180
1181 /// Finds all independent vertex sets whose size lies in `sizes`.
1182 ///
1183 /// A vertex set is *independent* if no two of its vertices are adjacent.
1184 /// As with [`cliques`](Self::cliques), every such set is reported, not only
1185 /// the maximal ones. If you only need the size of the largest one, use
1186 /// [`independence_number`](Self::independence_number). The independent
1187 /// sets of G are exactly the [`cliques`](Self::cliques) of its
1188 /// [complementer](Self::complementer). Edge directions are ignored.
1189 ///
1190 /// See also [`is_independent_vertex_set`](Self::is_independent_vertex_set)
1191 /// to test a given vertex set.
1192 ///
1193 /// The implementation was ported from the Very Nauty Graph Library by
1194 /// Keith Briggs and uses the algorithm of Tsukiyama, Ide, Ariyoshi and
1195 /// Shirakawa (SIAM J. Computing 6:505–517, 1977).
1196 ///
1197 /// Binds [`igraph_independent_vertex_sets`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_independent_vertex_sets).
1198 ///
1199 /// # Errors
1200 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
1201 ///
1202 /// # Examples
1203 ///
1204 /// ```
1205 /// use igraph::prelude::*;
1206 /// // In the path 0-1-2-3 the independent pairs are {0,2}, {0,3} and {1,3}.
1207 /// let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1208 /// let mut pairs = path.independent_vertex_sets(2..=2, None).unwrap();
1209 /// pairs.iter_mut().for_each(|s| s.sort());
1210 /// pairs.sort();
1211 /// assert_eq!(pairs, vec![vec![0, 2], vec![0, 3], vec![1, 3]]);
1212 /// ```
1213 pub fn independent_vertex_sets(
1214 &self,
1215 sizes: impl RangeBounds<usize>,
1216 max_results: Option<usize>,
1217 ) -> Result<Vec<Vec<VertexId>>> {
1218 let Some((min, max)) = size_bounds(self, &sizes)? else {
1219 return Ok(Vec::new());
1220 };
1221 let mut res = VectorIntList::new();
1222 directed_cache_guard(self, || {
1223 igraph_call!(igraph_independent_vertex_sets(
1224 self,
1225 &mut res,
1226 min,
1227 max,
1228 limit(max_results)
1229 ))
1230 })?;
1231 Ok(res.to_vecs())
1232 }
1233
1234 /// Finds all the largest (maximum) independent vertex sets.
1235 ///
1236 /// An independent set is *largest* if no other independent set has more
1237 /// vertices; its size is the [independence number](Self::independence_number).
1238 /// Largest independent sets are always maximal (see
1239 /// [`maximal_independent_vertex_sets`](Self::maximal_independent_vertex_sets)),
1240 /// but not conversely. Edge directions are ignored (with a warning).
1241 ///
1242 /// Uses the algorithm of Tsukiyama et al. (1977), ported from the Very
1243 /// Nauty Graph Library.
1244 ///
1245 /// Binds [`igraph_largest_independent_vertex_sets`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_largest_independent_vertex_sets).
1246 ///
1247 /// # Examples
1248 ///
1249 /// ```
1250 /// use igraph::prelude::*;
1251 /// // The Petersen graph has exactly five independent sets of size 4.
1252 /// let petersen = Graph::famous("Petersen").unwrap();
1253 /// let largest = petersen.largest_independent_vertex_sets().unwrap();
1254 /// assert_eq!(largest.len(), 5);
1255 /// assert!(largest.iter().all(|s| s.len() == 4));
1256 /// assert!(petersen.is_independent_vertex_set(&largest[0]).unwrap());
1257 /// ```
1258 pub fn largest_independent_vertex_sets(&self) -> Result<Vec<Vec<VertexId>>> {
1259 let mut res = VectorIntList::new();
1260 directed_cache_guard(self, || {
1261 igraph_call!(igraph_largest_independent_vertex_sets(self, &mut res))
1262 })?;
1263 Ok(res.to_vecs())
1264 }
1265
1266 /// Finds the maximal independent vertex sets whose size lies in `sizes`.
1267 ///
1268 /// A *maximal* independent set cannot be extended by adding any other
1269 /// vertex; equivalently it is an independent *dominating* set. Maximal
1270 /// independent sets of G are the maximal cliques of its complementer.
1271 ///
1272 /// Uses the algorithm of Tsukiyama et al. (1977), as implemented by Kevin
1273 /// O'Neill and K. M. Briggs in the Very Nauty Graph Library. Edge
1274 /// directions are ignored (with a warning).
1275 ///
1276 /// Binds [`igraph_maximal_independent_vertex_sets`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_maximal_independent_vertex_sets).
1277 ///
1278 /// # Errors
1279 /// [`ErrorKind::InvalidValue`] if `sizes` is empty.
1280 ///
1281 /// # Examples
1282 ///
1283 /// ```
1284 /// use igraph::prelude::*;
1285 /// // In the star with center 0, the maximal independent sets are {0}
1286 /// // and the set of all leaves.
1287 /// let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false).unwrap();
1288 /// let mut sets = star.maximal_independent_vertex_sets(.., None).unwrap();
1289 /// sets.iter_mut().for_each(|s| s.sort());
1290 /// sets.sort();
1291 /// assert_eq!(sets, vec![vec![0], vec![1, 2, 3]]);
1292 /// // They are the maximal cliques of the complementer graph.
1293 /// let comp = star.complementer(false).unwrap();
1294 /// assert_eq!(comp.maximal_cliques_count(..).unwrap(), 2);
1295 /// ```
1296 pub fn maximal_independent_vertex_sets(
1297 &self,
1298 sizes: impl RangeBounds<usize>,
1299 max_results: Option<usize>,
1300 ) -> Result<Vec<Vec<VertexId>>> {
1301 let Some((min, max)) = size_bounds(self, &sizes)? else {
1302 return Ok(Vec::new());
1303 };
1304 let mut res = VectorIntList::new();
1305 directed_cache_guard(self, || {
1306 igraph_call!(igraph_maximal_independent_vertex_sets(
1307 self,
1308 &mut res,
1309 min,
1310 max,
1311 limit(max_results)
1312 ))
1313 })?;
1314 Ok(res.to_vecs())
1315 }
1316
1317 /// The independence number α(G): the size of the largest independent
1318 /// vertex set.
1319 ///
1320 /// By definition α(G) = ω(complement of G) (see
1321 /// [`complementer`](Self::complementer)). It is 0 for the null graph.
1322 /// Every color class of a proper vertex coloring is an independent set,
1323 /// so α(G) · χ(G) ≥ |V|. Edge directions are ignored (with a warning).
1324 ///
1325 /// Binds [`igraph_independence_number`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_independence_number).
1326 ///
1327 /// # Examples
1328 ///
1329 /// ```
1330 /// use igraph::prelude::*;
1331 /// // α(C5) = 2: any three vertices of a pentagon contain an edge.
1332 /// let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
1333 /// assert_eq!(c5.independence_number().unwrap(), 2);
1334 /// ```
1335 pub fn independence_number(&self) -> Result<usize> {
1336 let mut no: igraph_int_t = 0;
1337 directed_cache_guard(self, || {
1338 igraph_call!(igraph_independence_number(self, &mut no))
1339 })?;
1340 Ok(no as usize)
1341 }
1342
1343 // ------------------------------------------------------------------
1344 // Coloring (igraph_coloring.h)
1345 // ------------------------------------------------------------------
1346
1347 /// Computes a proper vertex coloring greedily.
1348 ///
1349 /// Colors are the integers `0, 1, 2, ...`; element `v` of the result is
1350 /// the color of vertex `v`, and adjacent vertices always get different
1351 /// colors. Vertices are colored one at a time, each receiving the smallest
1352 /// color not used by its already colored neighbors, in an order chosen by
1353 /// `heuristic` (see [`ColoringGreedy`]). The number of colors used is an
1354 /// upper bound on the chromatic number, not necessarily optimal; the
1355 /// [`clique_number`](Self::clique_number) is a lower bound. Edge
1356 /// directions and self-loops are ignored. Multi-edges never affect the
1357 /// validity of the coloring, but [`ColoringGreedy::ColoredNeighbors`]
1358 /// counts them with their multiplicity when choosing the next vertex
1359 /// (so the colors can differ from those of the simplified graph), while
1360 /// [`ColoringGreedy::DSatur`] ignores them. The algorithm is
1361 /// deterministic (it does not use the random number generator).
1362 ///
1363 /// See also [`bipartite_types`](Self::bipartite_types), which finds a
1364 /// 2-coloring whenever one exists.
1365 ///
1366 /// Binds [`igraph_vertex_coloring_greedy`](https://igraph.org/c/html/latest/igraph-Coloring.html#igraph_vertex_coloring_greedy).
1367 ///
1368 /// # Examples
1369 ///
1370 /// ```
1371 /// use igraph::{cliques::ColoringGreedy, prelude::*};
1372 /// // An even cycle is bipartite: DSatur finds a 2-coloring.
1373 /// let c6 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)], 6, false)
1374 /// .unwrap();
1375 /// let colors = c6.vertex_coloring_greedy(ColoringGreedy::DSatur).unwrap();
1376 /// assert!(c6.is_vertex_coloring(&colors).unwrap());
1377 /// assert_eq!(*colors.iter().max().unwrap(), 1);
1378 /// ```
1379 pub fn vertex_coloring_greedy(&self, heuristic: ColoringGreedy) -> Result<Vec<i64>> {
1380 let mut colors = VectorInt::new();
1381 directed_cache_guard(self, || {
1382 igraph_call!(igraph_vertex_coloring_greedy(
1383 self,
1384 &mut colors,
1385 heuristic.into()
1386 ))
1387 })?;
1388 Ok(colors.into())
1389 }
1390
1391 /// Checks whether `colors` (one integer per vertex) is a proper vertex
1392 /// coloring, i.e. no edge joins two vertices of the same color.
1393 ///
1394 /// Colors may be any integers (they need not be consecutive). Edge
1395 /// directions are ignored, and so are self-loops. Time complexity: O(|E|).
1396 ///
1397 /// Binds [`igraph_is_vertex_coloring`](https://igraph.org/c/html/latest/igraph-Coloring.html#igraph_is_vertex_coloring).
1398 ///
1399 /// # Errors
1400 /// [`ErrorKind::InvalidValue`] if `colors.len()` differs from the number
1401 /// of vertices.
1402 ///
1403 /// # Examples
1404 ///
1405 /// ```
1406 /// use igraph::prelude::*;
1407 /// let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
1408 /// assert!(triangle.is_vertex_coloring(&[7, -1, 3]).unwrap());
1409 /// assert!(!triangle.is_vertex_coloring(&[0, 1, 0]).unwrap());
1410 /// ```
1411 pub fn is_vertex_coloring(&self, colors: &[i64]) -> Result<bool> {
1412 let types = VectorInt::view(colors);
1413 let mut res = false;
1414 igraph_call!(igraph_is_vertex_coloring(self, types.as_ptr(), &mut res))?;
1415 Ok(res)
1416 }
1417
1418 /// Checks whether `types` (one boolean per vertex) is a valid bipartite
1419 /// coloring, and if so reports the orientation of the edges.
1420 ///
1421 /// Returns `None` if some edge joins two vertices of the same type
1422 /// (self-loops are ignored), otherwise `Some(mode)` where, for a directed
1423 /// graph, `mode` is [`NeighborMode::Out`] when all edges go from `false`
1424 /// to `true` vertices, [`NeighborMode::In`] when they all go from `true`
1425 /// to `false`, and [`NeighborMode::All`] when both directions occur. It is
1426 /// always [`NeighborMode::All`] for undirected graphs, and for directed
1427 /// graphs without non-loop edges. Time complexity:
1428 /// O(|E|).
1429 ///
1430 /// See also [`bipartite_types`](Self::bipartite_types) to find such a
1431 /// coloring, and [`is_bipartite`](Self::is_bipartite).
1432 ///
1433 /// Binds [`igraph_is_bipartite_coloring`](https://igraph.org/c/html/latest/igraph-Coloring.html#igraph_is_bipartite_coloring).
1434 ///
1435 /// # Errors
1436 /// [`ErrorKind::InvalidValue`] if `types.len()` differs from the number of
1437 /// vertices.
1438 ///
1439 /// # Examples
1440 ///
1441 /// ```
1442 /// use igraph::prelude::*;
1443 /// // Directed edges from "users" (false) to "items" (true).
1444 /// let g = Graph::from_edges(&[(0, 2), (1, 2), (1, 3)], 4, true).unwrap();
1445 /// let types = [false, false, true, true];
1446 /// assert_eq!(g.is_bipartite_coloring(&types).unwrap(), Some(NeighborMode::Out));
1447 /// assert_eq!(g.is_bipartite_coloring(&[false, true, true, false]).unwrap(), None);
1448 /// ```
1449 pub fn is_bipartite_coloring(&self, types: &[bool]) -> Result<Option<NeighborMode>> {
1450 let types = VectorBool::view(types);
1451 let mut res = false;
1452 let mut mode: igraph_neimode_t = igraph_neimode_t_IGRAPH_ALL;
1453 igraph_call!(igraph_is_bipartite_coloring(
1454 self,
1455 types.as_ptr(),
1456 &mut res,
1457 &mut mode
1458 ))?;
1459 if res {
1460 Ok(Some(NeighborMode::try_from(mode)?))
1461 } else {
1462 Ok(None)
1463 }
1464 }
1465
1466 /// Checks whether `colors` (one integer per edge) is a proper edge
1467 /// coloring, i.e. no two edges sharing an endpoint have the same color.
1468 ///
1469 /// A self-loop is not considered adjacent to itself, so graphs with
1470 /// self-loops can still be properly edge-colored. Time complexity:
1471 /// O(|V| d log d), where d is the maximum degree.
1472 ///
1473 /// Binds [`igraph_is_edge_coloring`](https://igraph.org/c/html/latest/igraph-Coloring.html#igraph_is_edge_coloring).
1474 ///
1475 /// # Errors
1476 /// [`ErrorKind::InvalidValue`] if `colors.len()` differs from the number
1477 /// of edges.
1478 ///
1479 /// # Examples
1480 ///
1481 /// ```
1482 /// use igraph::prelude::*;
1483 /// // The perfect matchings {01, 23}, {02, 13}, {03, 12} 3-edge-color K4.
1484 /// let k4 = Graph::from_edges(&[(0, 1), (2, 3), (0, 2), (1, 3), (0, 3), (1, 2)], 4, false)
1485 /// .unwrap();
1486 /// assert!(k4.is_edge_coloring(&[0, 0, 1, 1, 2, 2]).unwrap());
1487 /// assert!(!k4.is_edge_coloring(&[0, 1, 0, 1, 2, 2]).unwrap());
1488 /// ```
1489 pub fn is_edge_coloring(&self, colors: &[i64]) -> Result<bool> {
1490 let types = VectorInt::view(colors);
1491 let mut res = false;
1492 igraph_call!(igraph_is_edge_coloring(self, types.as_ptr(), &mut res))?;
1493 Ok(res)
1494 }
1495}