Skip to main content

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}