Skip to main content

igraph/
bipartite.rs

1//! Bipartite (two-mode) graphs and matchings (`igraph_bipartite.h`, `igraph_matching.h`).
2//!
3//! A graph is *bipartite* if its vertices can be split into two classes so
4//! that every edge connects vertices of different classes: actors and
5//! movies, authors and papers, workers and jobs. igraph has no dedicated
6//! bipartite graph type: an ordinary [`Graph`] is paired with a *types*
7//! vector, one `bool` per vertex, telling which class each vertex belongs
8//! to. In this crate the types are plain `&[bool]` inputs and `Vec<bool>`
9//! outputs; by igraph's convention vertices of type `false` form the *first*
10//! (or "bottom") class and vertices of type `true` the *second* ("top") one.
11//!
12//! Functions that *create* bipartite graphs return a [`BipartiteGraph`],
13//! which bundles the graph with its types and offers shortcuts to the
14//! analysis methods below.
15//!
16//! # Example
17//!
18//! ```
19//! use igraph::{bipartite::BipartiteGraph, prelude::*};
20//!
21//! // Three workers (vertices 0..3, type false) and three jobs (3..6, type true).
22//! let types = vec![false, false, false, true, true, true];
23//! let edges = [(0, 3), (0, 4), (1, 3), (2, 4), (2, 5)];
24//! let staff = BipartiteGraph::new(types, &edges, false).unwrap();
25//!
26//! // Everyone can get a job: the maximum matching is perfect.
27//! let m = staff.maximum_matching(None).unwrap();
28//! assert_eq!(m.size, 3);
29//! assert_eq!(m.mate(1), Some(3)); // worker 1 can only take job 3
30//! assert!(staff.graph.is_matching(Some(&staff.types), &m.matching).unwrap());
31//!
32//! // Workers sharing a job skill are connected in the projection.
33//! let p = staff.projection().unwrap();
34//! assert_eq!(p.proj1.edge_list(), vec![(0, 1), (0, 2)]);
35//! ```
36//!
37//! # Provided functionality
38//!
39//! | Rust | C function | What it does |
40//! |------|------------|--------------|
41//! | [`Graph::is_bipartite`], [`Graph::bipartite_types`], [`BipartiteGraph::from_graph`] | `igraph_is_bipartite` | test bipartiteness, find a 2-coloring |
42//! | [`Graph::create_bipartite`], [`BipartiteGraph::new`] | `igraph_create_bipartite` | build from types + edges, checking bipartiteness |
43//! | [`Graph::full_bipartite`] | `igraph_full_bipartite` | complete bipartite graph *K(n1, n2)* |
44//! | [`Graph::biadjacency`] | `igraph_biadjacency` | graph from a bipartite adjacency matrix |
45//! | [`Graph::weighted_biadjacency`] | `igraph_weighted_biadjacency` | weighted graph from a bipartite adjacency matrix |
46//! | [`Graph::get_biadjacency`], [`BipartiteGraph::biadjacency`] | `igraph_get_biadjacency` | bipartite adjacency matrix of a graph |
47//! | [`Graph::bipartite_projection_size`] | `igraph_bipartite_projection_size` | sizes of the two projections |
48//! | [`Graph::bipartite_projection`], [`Graph::bipartite_projection_of`], [`BipartiteGraph::projection`] | `igraph_bipartite_projection` | the one-mode projections |
49//! | [`Graph::bipartite_game_gnp`] | `igraph_bipartite_game_gnp` | random *G(n1, n2, p)* graph |
50//! | [`Graph::bipartite_game_gnm`] | `igraph_bipartite_game_gnm` | random *G(n1, n2, m)* graph |
51//! | [`Graph::bipartite_iea_game`] | `igraph_bipartite_iea_game` (implemented with `igraph_bipartite_game_gnm`, working around an igraph 1.0.0 and 1.0.1 bug) | random multigraph by independent edge assignment |
52//! | [`Graph::is_matching`] | `igraph_is_matching` | validity of a matching |
53//! | [`Graph::is_maximal_matching`] | `igraph_is_maximal_matching` | maximality of a matching |
54//! | [`Graph::maximum_bipartite_matching`], [`Graph::maximum_bipartite_matching_eps`], [`BipartiteGraph::maximum_matching`] | `igraph_maximum_bipartite_matching` | maximum (weighted) bipartite matching |
55//!
56//! # Related functionality in other modules
57//!
58//! | Rust | What it does |
59//! |------|--------------|
60//! | [`Graph::layout_bipartite`] ([`layout`](crate::layout)) | two-row drawing of a bipartite graph, from its types |
61//! | [`Graph::realize_bipartite_degree_sequence`] ([`constructors`](crate::constructors)) | deterministic bipartite graph with given degrees |
62//! | [`is_bigraphical`](crate::mixing::is_bigraphical) ([`mixing`](crate::mixing)) | whether two degree sequences can be realized by a bipartite graph |
63//! | [`Graph::full_multipartite`], [`Graph::turan`] ([`constructors`](crate::constructors)) | complete *k*-partite graphs, generalizing [`Graph::full_bipartite`] |
64//! | [`Graph::erdos_renyi_game_gnp`], [`Graph::erdos_renyi_game_gnm`] ([`games`](crate::games)) | one-mode versions of the random bipartite games |
65//! | [`Graph::maxflow_value`] ([`flow`](crate::flow)) | maximum flow: a bipartite matching is a unit-capacity flow from one class to the other |
66//! | [`Graph::girth`] ([`structural`](crate::structural)) | shortest cycle; a graph is bipartite iff it has no odd cycle |
67//! | [`Graph::is_bipartite_coloring`] ([`cliques`](crate::cliques)) | whether a *given* types vector is a proper 2-coloring, and how the edges are oriented |
68//! | [`Graph::read_graph_pajek`] ([`foreign`](crate::foreign)) | reads two-mode Pajek networks, storing the types in the `type` vertex attribute when [`attributes`](crate::attributes) are enabled |
69//!
70//! # Matchings
71//!
72//! A matching is represented, as in igraph, by a vector with one entry per
73//! vertex: entry `i` is the vertex matched to `i`, or [`UNMATCHED`] (`-1`)
74//! if `i` is unmatched. [`BipartiteMatching`] offers [`mate`](BipartiteMatching::mate)
75//! and [`pairs`](BipartiteMatching::pairs) to read it in a friendlier way.
76//!
77//! See the igraph C documentation chapter on
78//! [bipartite graphs](https://igraph.org/c/html/latest/igraph-Bipartite.html)
79//! and the section on
80//! [matchings](https://igraph.org/c/html/latest/igraph-Structural.html#matchings).
81
82use crate::{
83    constants::{EdgeTypeSw, NeighborMode},
84    error::{Error, Result},
85    ffi::*,
86    graph::{Graph, VertexId},
87    igraph_call,
88    matrix::Matrix,
89    vector::{Vector, VectorBool, VectorInt},
90};
91use std::ptr;
92
93/// Marker used in matching vectors for unmatched vertices (`-1`).
94pub const UNMATCHED: VertexId = -1;
95
96/// Largest matrix entry accepted as an edge multiplicity by
97/// [`Graph::biadjacency`] (2^53, beyond which not every integer is an `f64`).
98const MAX_MULTIPLICITY: f64 = 9_007_199_254_740_992.0;
99
100/// Converts a count into an `igraph_int_t`, failing on overflow.
101fn to_int(n: usize, what: &str) -> Result<igraph_int_t> {
102    igraph_int_t::try_from(n).map_err(|_| Error::invalid(format!("{what} is too large: {n}")))
103}
104
105/// Checks that `types` has one entry per vertex of `graph`.
106fn check_types(graph: &Graph, types: &[bool]) -> Result<()> {
107    if types.len() != graph.vcount() {
108        return Err(Error::invalid(format!(
109            "the types vector has length {}, but the graph has {} vertices",
110            types.len(),
111            graph.vcount()
112        )));
113    }
114    Ok(())
115}
116
117/// Checks that `weights`, if given, has one entry per edge of `graph`.
118fn check_weights(graph: &Graph, weights: Option<&[f64]>) -> Result<()> {
119    match weights {
120        Some(w) if w.len() != graph.ecount() => Err(Error::invalid(format!(
121            "the weights vector has length {}, but the graph has {} edges",
122            w.len(),
123            graph.ecount()
124        ))),
125        _ => Ok(()),
126    }
127}
128
129/// Checks that every edge of `graph` joins vertices of different types
130/// (`types` must already have the right length).
131///
132/// igraph's matching code (`misc/matching.c`, unchanged in igraph 1.0.0 and
133/// 1.0.1) only partially performs this check: with an undetected same-type
134/// edge it silently returns wrong results.
135fn check_bipartite_edges(graph: &Graph, types: &[bool]) -> Result<()> {
136    for (e, (u, v)) in graph.edge_list().into_iter().enumerate() {
137        if types[u as usize] == types[v as usize] {
138            return Err(Error::invalid(format!(
139                "edge {e} ({u}, {v}) joins two vertices of the same type"
140            )));
141        }
142    }
143    Ok(())
144}
145
146/// Checks that all `weights`, if given, are finite.
147fn check_finite_weights(weights: Option<&[f64]>) -> Result<()> {
148    match weights
149        .into_iter()
150        .flatten()
151        .enumerate()
152        .find(|(_, x)| !x.is_finite())
153    {
154        Some((i, x)) => Err(Error::invalid(format!(
155            "the weight of edge {i} is not finite: {x}"
156        ))),
157        None => Ok(()),
158    }
159}
160
161/// A graph together with its vertex types: the output of the bipartite
162/// constructors and generators of this module.
163///
164/// Vertices with type `false` form the first class, vertices with type
165/// `true` the second one. The fields are public: take them apart freely
166/// (or use [`into_parts`](Self::into_parts)).
167#[derive(Debug, Clone, PartialEq)]
168pub struct BipartiteGraph {
169    /// The graph.
170    pub graph: Graph,
171    /// The type of each vertex, indexed by vertex id.
172    pub types: Vec<bool>,
173}
174
175impl BipartiteGraph {
176    /// Creates a bipartite graph from vertex types and a list of edges,
177    /// checking that every edge connects vertices of different types
178    /// (`igraph_create_bipartite`).
179    ///
180    /// The number of vertices is `types.len()`. See [`Graph::create_bipartite`].
181    ///
182    /// # Errors
183    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if an edge
184    /// joins two vertices of the same type, or
185    /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if an
186    /// endpoint is out of range.
187    ///
188    /// # Examples
189    /// ```
190    /// use igraph::bipartite::BipartiteGraph;
191    /// let b = BipartiteGraph::new(vec![false, true, false], &[(0, 1), (2, 1)], false).unwrap();
192    /// assert_eq!(b.part(true), vec![1]);
193    /// assert!(BipartiteGraph::new(vec![false, false], &[(0, 1)], false).is_err());
194    /// ```
195    pub fn new(types: Vec<bool>, edges: &[(VertexId, VertexId)], directed: bool) -> Result<Self> {
196        let graph = Graph::create_bipartite(&types, edges, directed)?;
197        Ok(Self { graph, types })
198    }
199
200    /// Wraps `graph` with a 2-coloring found by [`Graph::bipartite_types`],
201    /// or returns `Ok(None)` if the graph is not bipartite.
202    ///
203    /// # Examples
204    /// ```
205    /// use igraph::{bipartite::BipartiteGraph, prelude::*};
206    /// let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
207    /// let b = BipartiteGraph::from_graph(square).unwrap().unwrap();
208    /// assert_eq!(b.types, vec![false, true, false, true]);
209    /// let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
210    /// assert!(BipartiteGraph::from_graph(triangle).unwrap().is_none());
211    /// ```
212    pub fn from_graph(graph: Graph) -> Result<Option<Self>> {
213        Ok(graph.bipartite_types()?.map(|types| Self { graph, types }))
214    }
215
216    /// Splits `self` into the graph and its types.
217    pub fn into_parts(self) -> (Graph, Vec<bool>) {
218        (self.graph, self.types)
219    }
220
221    /// The ids of the vertices having type `kind`, in increasing order.
222    pub fn part(&self, kind: bool) -> Vec<VertexId> {
223        (0..)
224            .zip(&self.types)
225            .filter(|&(_, &t)| t == kind)
226            .map(|(v, _)| v)
227            .collect()
228    }
229
230    /// The number of vertices of type `false` and of type `true`.
231    pub fn part_sizes(&self) -> (usize, usize) {
232        let n2 = self.types.iter().filter(|&&t| t).count();
233        (self.types.len() - n2, n2)
234    }
235
236    /// Both one-mode projections, see [`Graph::bipartite_projection`].
237    pub fn projection(&self) -> Result<BipartiteProjection> {
238        self.graph.bipartite_projection(&self.types, None)
239    }
240
241    /// The bipartite adjacency matrix, see [`Graph::get_biadjacency`].
242    pub fn biadjacency(&self, weights: Option<&[f64]>) -> Result<Biadjacency> {
243        self.graph.get_biadjacency(&self.types, weights)
244    }
245
246    /// A maximum (weighted) matching, see [`Graph::maximum_bipartite_matching`].
247    pub fn maximum_matching(&self, weights: Option<&[f64]>) -> Result<BipartiteMatching> {
248        self.graph.maximum_bipartite_matching(&self.types, weights)
249    }
250}
251
252/// A bipartite graph with edge weights, the output of [`Graph::weighted_biadjacency`].
253#[derive(Debug, Clone, PartialEq)]
254pub struct WeightedBipartiteGraph {
255    /// The graph.
256    pub graph: Graph,
257    /// The type of each vertex, indexed by vertex id.
258    pub types: Vec<bool>,
259    /// The weight of each edge, indexed by edge id.
260    pub weights: Vec<f64>,
261}
262
263/// A bipartite adjacency matrix, the output of [`Graph::get_biadjacency`].
264#[derive(Debug, Clone, PartialEq)]
265pub struct Biadjacency {
266    /// The matrix: rows correspond to vertices of type `false`, columns to
267    /// vertices of type `true`; an element is the number of edges (or the
268    /// total weight of the edges) between the two vertices.
269    pub matrix: Matrix,
270    /// The vertex id of each row.
271    pub row_ids: Vec<VertexId>,
272    /// The vertex id of each column.
273    pub col_ids: Vec<VertexId>,
274}
275
276/// Vertex and edge counts of the two projections of a bipartite graph,
277/// the output of [`Graph::bipartite_projection_size`].
278#[derive(Debug, Clone, Copy, PartialEq, Eq)]
279pub struct ProjectionSize {
280    /// Number of vertices of the first projection (type `false`).
281    pub vcount1: usize,
282    /// Number of edges of the first projection.
283    pub ecount1: usize,
284    /// Number of vertices of the second projection (type `true`).
285    pub vcount2: usize,
286    /// Number of edges of the second projection.
287    pub ecount2: usize,
288}
289
290/// The two one-mode projections of a bipartite graph, the output of
291/// [`Graph::bipartite_projection`].
292///
293/// Each projection has one vertex per vertex of the corresponding type,
294/// numbered in increasing order of the original ids; two vertices are
295/// connected if they share at least one neighbor in the other class.
296#[derive(Debug, Clone, PartialEq)]
297pub struct BipartiteProjection {
298    /// The first projection.
299    pub proj1: Graph,
300    /// The second projection.
301    pub proj2: Graph,
302    /// For each edge of `proj1`, the number of common neighbors of its
303    /// endpoints in the original graph (in a multigraph: the number of
304    /// paths of length two between them).
305    pub multiplicity1: Vec<i64>,
306    /// For each edge of `proj2`, the number of common neighbors of its
307    /// endpoints in the original graph (in a multigraph: the number of
308    /// paths of length two between them).
309    pub multiplicity2: Vec<i64>,
310}
311
312/// A maximum matching of a bipartite graph, the output of
313/// [`Graph::maximum_bipartite_matching`].
314#[derive(Debug, Clone, PartialEq)]
315pub struct BipartiteMatching {
316    /// The number of matched pairs (the cardinality of the matching).
317    pub size: usize,
318    /// The total weight of the matched edges; equal to `size` for
319    /// unweighted graphs.
320    pub weight: f64,
321    /// For each vertex, the vertex it is matched to, or [`UNMATCHED`].
322    pub matching: Vec<VertexId>,
323}
324
325impl BipartiteMatching {
326    /// The vertex matched to `v`, or `None` if `v` is unmatched or out of range.
327    pub fn mate(&self, v: VertexId) -> Option<VertexId> {
328        usize::try_from(v)
329            .ok()
330            .and_then(|i| self.matching.get(i))
331            .copied()
332            .filter(|&m| m >= 0)
333    }
334
335    /// The matched pairs `(u, v)` with `u < v`, sorted by `u`.
336    pub fn pairs(&self) -> Vec<(VertexId, VertexId)> {
337        (0..)
338            .zip(&self.matching)
339            .filter(|&(u, &v)| v > u)
340            .map(|(u, &v)| (u, v))
341            .collect()
342    }
343
344    /// Whether vertex `v` is matched.
345    pub fn is_matched(&self, v: VertexId) -> bool {
346        self.mate(v).is_some()
347    }
348}
349
350/// Parameters shared by the random bipartite generators
351/// [`Graph::bipartite_game_gnp`] and [`Graph::bipartite_game_gnm`].
352///
353/// The default is an undirected simple graph from the classic
354/// (edge-unlabeled) Erdős–Rényi model.
355#[derive(Debug, Clone, Copy, PartialEq, Eq)]
356pub struct BipartiteGameOptions {
357    /// Whether to generate a directed graph (default `false`).
358    pub directed: bool,
359    /// Direction of the edges in directed graphs (default [`NeighborMode::Out`]):
360    /// `Out` points from bottom (type `false`) to top (type `true`) vertices,
361    /// `In` the other way around; with `All` each edge direction is sampled
362    /// independently, so mutual edges may appear. Ignored if undirected.
363    pub mode: NeighborMode,
364    /// Allowed edge types (default [`EdgeTypeSw::Simple`]): use
365    /// [`EdgeTypeSw::Multi`] to allow multi-edges. Self-loops are never
366    /// possible in a bipartite graph, so [`EdgeTypeSw::Loops`] is the same
367    /// as [`EdgeTypeSw::Simple`].
368    pub allowed_edge_types: EdgeTypeSw,
369    /// If `true`, sample uniformly from ordered edge lists (edge-labeled
370    /// graphs) instead of the classic model (default `false`). Only
371    /// relevant when multi-edges are allowed: with simple graphs,
372    /// [`Graph::bipartite_game_gnm`] only shuffles the edge order and
373    /// [`Graph::bipartite_game_gnp`] fails with
374    /// [`ErrorKind::Unimplemented`](crate::ErrorKind::Unimplemented).
375    pub edge_labeled: bool,
376}
377
378impl Default for BipartiteGameOptions {
379    fn default() -> Self {
380        Self {
381            directed: false,
382            mode: NeighborMode::Out,
383            allowed_edge_types: EdgeTypeSw::Simple,
384            edge_labeled: false,
385        }
386    }
387}
388
389impl BipartiteGameOptions {
390    /// Sets [`directed`](Self::directed) and [`mode`](Self::mode).
391    pub fn with_directed(mut self, directed: bool, mode: NeighborMode) -> Self {
392        self.directed = directed;
393        self.mode = mode;
394        self
395    }
396
397    /// Sets [`allowed_edge_types`](Self::allowed_edge_types).
398    pub fn with_allowed_edge_types(mut self, allowed: EdgeTypeSw) -> Self {
399        self.allowed_edge_types = allowed;
400        self
401    }
402
403    /// Sets [`edge_labeled`](Self::edge_labeled).
404    pub fn with_edge_labeled(mut self, edge_labeled: bool) -> Self {
405        self.edge_labeled = edge_labeled;
406        self
407    }
408}
409
410impl igraph_t {
411    /// Whether the graph is bipartite, i.e. whether its vertices can be
412    /// 2-colored so that no edge joins vertices of the same color.
413    ///
414    /// Equivalently, the graph has no cycle of odd length; a graph with a
415    /// self-loop is never bipartite. Edge directions are ignored. Use
416    /// [`bipartite_types`](Self::bipartite_types) to also get a coloring.
417    ///
418    /// Binds [`igraph_is_bipartite`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_is_bipartite).
419    /// Time complexity: O(|V|+|E|).
420    ///
421    /// See also [`Graph::girth`] (a bipartite graph has even girth, or no
422    /// cycle at all), [`Graph::is_bipartite_coloring`] (checks a given
423    /// types vector instead of finding one) and
424    /// [`Graph::vertex_coloring_greedy`] (colorings with more than two
425    /// colors).
426    ///
427    /// # Examples
428    /// ```
429    /// use igraph::prelude::*;
430    /// let c6 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)], 6, false).unwrap();
431    /// assert!(c6.is_bipartite().unwrap());
432    /// let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
433    /// assert!(!c5.is_bipartite().unwrap());
434    /// // The Heawood graph (incidence graph of the Fano plane) is bipartite,
435    /// // the Petersen graph is not (it has 5-cycles).
436    /// assert!(Graph::famous("Heawood").unwrap().is_bipartite().unwrap());
437    /// assert!(!Graph::famous("Petersen").unwrap().is_bipartite().unwrap());
438    /// ```
439    pub fn is_bipartite(&self) -> Result<bool> {
440        let mut res = false;
441        igraph_call!(igraph_is_bipartite(self, &mut res, ptr::null_mut()))?;
442        Ok(res)
443    }
444
445    /// A 2-coloring (vertex types) witnessing that the graph is bipartite, or
446    /// `None` if it is not.
447    ///
448    /// The coloring is not unique in general: e.g. each connected component
449    /// can be flipped independently. Binds
450    /// [`igraph_is_bipartite`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_is_bipartite).
451    /// Time complexity: O(|V|+|E|).
452    ///
453    /// The types can be fed directly to the other functions of this module,
454    /// or to [`Graph::layout_bipartite`] to draw the graph in two rows.
455    ///
456    /// # Examples
457    /// ```
458    /// use igraph::prelude::*;
459    /// let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
460    /// let types = path.bipartite_types().unwrap().unwrap();
461    /// assert_ne!(types[0], types[1]);
462    /// assert_ne!(types[1], types[2]);
463    /// // Draw it in two rows: `true` vertices at y = 0, `false` ones at y = 1.
464    /// let layout = path.layout_bipartite(&types, 1.0, 1.0, 100).unwrap();
465    /// for (v, &t) in types.iter().enumerate() {
466    ///     assert_eq!(layout[(v, 1)], if t { 0.0 } else { 1.0 });
467    /// }
468    /// ```
469    pub fn bipartite_types(&self) -> Result<Option<Vec<bool>>> {
470        let mut res = false;
471        let mut types = VectorBool::new();
472        igraph_call!(igraph_is_bipartite(self, &mut res, &mut types))?;
473        Ok(res.then(|| types.into()))
474    }
475
476    /// Creates a bipartite graph from vertex types and edges, checking that
477    /// every edge connects vertices of different types.
478    ///
479    /// The graph has `types.len()` vertices; every endpoint in `edges` must
480    /// be smaller than that. It is [`Graph::from_edges`] plus a
481    /// bipartiteness check. [`BipartiteGraph::new`] does the same and keeps
482    /// the types together with the graph.
483    ///
484    /// Binds [`igraph_create_bipartite`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_create_bipartite).
485    /// Time complexity: O(|V|+|E|).
486    ///
487    /// # Errors
488    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if an edge
489    /// joins two vertices of the same type,
490    /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if an
491    /// endpoint is out of range.
492    ///
493    /// # Examples
494    /// ```
495    /// use igraph::prelude::*;
496    /// let types = [false, true, false, true];
497    /// let g = Graph::create_bipartite(&types, &[(0, 1), (1, 2), (2, 3)], true).unwrap();
498    /// assert_eq!((g.vcount(), g.ecount(), g.is_directed()), (4, 3, true));
499    /// let err = Graph::create_bipartite(&types, &[(0, 2)], false).unwrap_err();
500    /// assert_eq!(err.kind(), ErrorKind::InvalidValue);
501    /// ```
502    pub fn create_bipartite(
503        types: &[bool],
504        edges: &[(VertexId, VertexId)],
505        directed: bool,
506    ) -> Result<Graph> {
507        let n = types.len() as igraph_int_t;
508        if let Some(&(u, v)) = edges
509            .iter()
510            .find(|&&(u, v)| u < 0 || v < 0 || u >= n || v >= n)
511        {
512            return Err(Error::new(
513                crate::ErrorKind::InvalidVertexId,
514                format!("edge ({u}, {v}) has an endpoint outside 0..{n}"),
515            ));
516        }
517        let flat: Vec<VertexId> = edges.iter().flat_map(|&(u, v)| [u, v]).collect();
518        let t = VectorBool::view(types);
519        let e = VectorInt::view(&flat);
520        Graph::init_with(|g| unsafe {
521            igraph_create_bipartite(g, t.as_ptr(), e.as_ptr(), directed)
522        })
523    }
524
525    /// Creates the complete bipartite graph *K(n1, n2)*.
526    ///
527    /// The first `n1` vertices have type `false`, the following `n2` type
528    /// `true`, and every vertex of the first kind is connected to every
529    /// vertex of the second kind. In directed graphs, `mode` gives the edge
530    /// directions: [`NeighborMode::Out`] from the first kind to the second,
531    /// [`NeighborMode::In`] the opposite, [`NeighborMode::All`] mutual edges
532    /// (so `2·n1·n2` edges). `mode` is ignored for undirected graphs.
533    ///
534    /// Binds [`igraph_full_bipartite`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_full_bipartite).
535    /// Time complexity: O(|V|+|E|).
536    ///
537    /// See also [`Graph::full_multipartite`] for complete *k*-partite graphs
538    /// and [`Graph::realize_bipartite_degree_sequence`] for bipartite graphs
539    /// with prescribed degrees.
540    ///
541    /// # Examples
542    /// ```
543    /// use igraph::prelude::*;
544    /// let k33 = Graph::full_bipartite(3, 3, false, NeighborMode::All).unwrap();
545    /// assert_eq!(k33.graph.ecount(), 9);
546    /// assert_eq!(k33.types, vec![false, false, false, true, true, true]);
547    /// let d = Graph::full_bipartite(2, 3, true, NeighborMode::All).unwrap();
548    /// assert_eq!(d.graph.ecount(), 12);
549    /// ```
550    pub fn full_bipartite(
551        n1: usize,
552        n2: usize,
553        directed: bool,
554        mode: NeighborMode,
555    ) -> Result<BipartiteGraph> {
556        let (n1, n2) = (to_int(n1, "n1")?, to_int(n2, "n2")?);
557        let mut types = VectorBool::new();
558        let graph = Graph::init_with(|g| unsafe {
559            igraph_full_bipartite(g, &mut types, n1, n2, directed, mode.into())
560        })?;
561        Ok(BipartiteGraph {
562            graph,
563            types: types.into(),
564        })
565    }
566
567    /// Creates a bipartite graph from a bipartite adjacency matrix.
568    ///
569    /// For an `n × m` matrix, the graph has `n` vertices of type `false`
570    /// (the rows, ids `0..n`) followed by `m` vertices of type `true` (the
571    /// columns, ids `n..n+m`). If `multiple` is `false`, one edge is created
572    /// for every non-zero element; if it is `true`, element `(i, j)` gives
573    /// the number of edges between row `i` and column `j` (fractional parts
574    /// are discarded; negative, infinite and NaN entries are an error). In directed graphs
575    /// `mode` gives the directions: [`NeighborMode::Out`] from rows to
576    /// columns, [`NeighborMode::In`] from columns to rows, [`NeighborMode::All`]
577    /// mutual edges.
578    ///
579    /// Binds [`igraph_biadjacency`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_biadjacency).
580    /// Time complexity: O(n·m) plus the number of created edges.
581    ///
582    /// See also [`Graph::adjacency`] for ordinary (square) adjacency
583    /// matrices, and [`get_biadjacency`](Self::get_biadjacency) for the
584    /// inverse conversion.
585    ///
586    /// # Errors
587    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
588    /// `multiple` is `true` and an entry is negative, not finite, or too
589    /// large to be an edge count.
590    ///
591    /// # Examples
592    /// ```
593    /// use igraph::prelude::*;
594    /// let m = Matrix::from_rows(&[[0.0, 1.0, 2.0], [1.0, 0.0, 0.0]]).unwrap();
595    /// let b = Graph::biadjacency(&m, false, NeighborMode::All, true).unwrap();
596    /// assert_eq!(b.types, vec![false, false, true, true, true]);
597    /// let mut edges = b.graph.edge_list();
598    /// edges.sort();
599    /// assert_eq!(edges, vec![(0, 3), (0, 4), (0, 4), (1, 2)]);
600    /// ```
601    pub fn biadjacency(
602        biadjmatrix: &Matrix,
603        directed: bool,
604        mode: NeighborMode,
605        multiple: bool,
606    ) -> Result<BipartiteGraph> {
607        if multiple {
608            // igraph 1.0.0 and 1.0.1 only reject negative entries and then
609            // convert them to integers with a plain C cast, which is undefined
610            // behavior for NaN, infinite or out-of-range values.
611            if let Some(&x) = biadjmatrix
612                .as_slice()
613                .iter()
614                .find(|x| !(x.is_finite() && (0.0..=MAX_MULTIPLICITY).contains(*x)))
615            {
616                return Err(Error::invalid(format!(
617                    "invalid edge multiplicity in the bipartite adjacency matrix: {x}"
618                )));
619            }
620        }
621        let mut types = VectorBool::new();
622        let graph = Graph::init_with(|g| unsafe {
623            igraph_biadjacency(g, &mut types, biadjmatrix, directed, mode.into(), multiple)
624        })?;
625        Ok(BipartiteGraph {
626            graph,
627            types: types.into(),
628        })
629    }
630
631    /// Creates a weighted bipartite graph from a bipartite adjacency matrix.
632    ///
633    /// Like [`biadjacency`](Self::biadjacency), but a single edge is created
634    /// for every non-zero element, and the element becomes its weight (any
635    /// real value, including negative, infinite and NaN ones). With
636    /// [`NeighborMode::All`] in directed graphs, both edges of a mutual pair
637    /// get the same weight.
638    ///
639    /// Binds [`igraph_weighted_biadjacency`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_weighted_biadjacency).
640    /// Time complexity: O(n·m).
641    ///
642    /// # Examples
643    /// ```
644    /// use igraph::prelude::*;
645    /// let m = Matrix::from_rows(&[[0.0, -4.5, 2.3], [-0.1, 0.0, 0.0]]).unwrap();
646    /// let w = Graph::weighted_biadjacency(&m, false, NeighborMode::All).unwrap();
647    /// assert_eq!(w.graph.ecount(), 3);
648    /// let mut weights = w.weights.clone();
649    /// weights.sort_by(f64::total_cmp);
650    /// assert_eq!(weights, vec![-4.5, -0.1, 2.3]);
651    /// ```
652    pub fn weighted_biadjacency(
653        biadjmatrix: &Matrix,
654        directed: bool,
655        mode: NeighborMode,
656    ) -> Result<WeightedBipartiteGraph> {
657        let mut types = VectorBool::new();
658        let mut weights = Vector::new();
659        let graph = Graph::init_with(|g| unsafe {
660            igraph_weighted_biadjacency(
661                g,
662                &mut types,
663                &mut weights,
664                biadjmatrix,
665                directed,
666                mode.into(),
667            )
668        })?;
669        Ok(WeightedBipartiteGraph {
670            graph,
671            types: types.into(),
672            weights: weights.into(),
673        })
674    }
675
676    /// The bipartite adjacency matrix of the graph (the inverse of
677    /// [`biadjacency`](Self::biadjacency)).
678    ///
679    /// Rows correspond to vertices of type `false`, columns to vertices of
680    /// type `true`, both in increasing id order (their ids are returned in
681    /// [`Biadjacency::row_ids`] and [`Biadjacency::col_ids`]). Element
682    /// `(i, j)` is the number of edges between the two vertices, regardless
683    /// of their direction, or the sum of their weights if `weights` is
684    /// given. Edges within a class are ignored, with a warning.
685    ///
686    /// Binds [`igraph_get_biadjacency`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_get_biadjacency).
687    /// Time complexity: O(|E|).
688    ///
689    /// See also [`Graph::get_adjacency`] for the full `|V| × |V|` adjacency
690    /// matrix: the bipartite one is its off-diagonal block.
691    ///
692    /// # Errors
693    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `types`
694    /// or `weights` have the wrong length.
695    ///
696    /// # Examples
697    /// ```
698    /// use igraph::prelude::*;
699    /// let g = Graph::from_edges(&[(0, 1), (2, 1), (2, 3)], 4, false).unwrap();
700    /// let b = g.get_biadjacency(&[false, true, false, true], Some(&[1.0, 2.0, 5.0])).unwrap();
701    /// assert_eq!(b.matrix.to_rows(), vec![vec![1.0, 0.0], vec![2.0, 5.0]]);
702    /// assert_eq!((b.row_ids, b.col_ids), (vec![0, 2], vec![1, 3]));
703    /// ```
704    pub fn get_biadjacency(&self, types: &[bool], weights: Option<&[f64]>) -> Result<Biadjacency> {
705        check_types(self, types)?;
706        check_weights(self, weights)?;
707        let t = VectorBool::view(types);
708        let w = weights.map(Vector::view);
709        let wp = w.as_ref().map_or(ptr::null(), |v| v.as_ptr());
710        let mut matrix = Matrix::new();
711        let mut row_ids = VectorInt::new();
712        let mut col_ids = VectorInt::new();
713        igraph_call!(igraph_get_biadjacency(
714            self,
715            t.as_ptr(),
716            wp,
717            &mut matrix,
718            &mut row_ids,
719            &mut col_ids
720        ))?;
721        Ok(Biadjacency {
722            matrix,
723            row_ids: row_ids.into(),
724            col_ids: col_ids.into(),
725        })
726    }
727
728    /// The number of vertices and edges of the two projections, without
729    /// computing them.
730    ///
731    /// Useful to estimate the memory needed by
732    /// [`bipartite_projection`](Self::bipartite_projection) on large graphs.
733    /// The first projection is the one of the vertices of type `false`.
734    ///
735    /// Binds [`igraph_bipartite_projection_size`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_bipartite_projection_size).
736    ///
737    /// # Errors
738    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `types`
739    /// has the wrong length or an edge joins vertices of the same type.
740    ///
741    /// # Examples
742    /// ```
743    /// use igraph::prelude::*;
744    /// let k23 = Graph::full_bipartite(2, 3, false, NeighborMode::All).unwrap();
745    /// let s = k23.graph.bipartite_projection_size(&k23.types).unwrap();
746    /// assert_eq!((s.vcount1, s.ecount1, s.vcount2, s.ecount2), (2, 1, 3, 3));
747    /// ```
748    pub fn bipartite_projection_size(&self, types: &[bool]) -> Result<ProjectionSize> {
749        check_types(self, types)?;
750        let t = VectorBool::view(types);
751        let (mut v1, mut e1, mut v2, mut e2) = (0, 0, 0, 0);
752        igraph_call!(igraph_bipartite_projection_size(
753            self,
754            t.as_ptr(),
755            &mut v1,
756            &mut e1,
757            &mut v2,
758            &mut e2
759        ))?;
760        Ok(ProjectionSize {
761            vcount1: v1 as usize,
762            ecount1: e1 as usize,
763            vcount2: v2 as usize,
764            ecount2: e2 as usize,
765        })
766    }
767
768    /// Both one-mode projections of a bipartite graph.
769    ///
770    /// The projection onto a class has one vertex per vertex of that class
771    /// (in increasing order of the original ids), and two vertices are
772    /// connected if they have at least one common neighbor in the other
773    /// class; the number of common neighbors is returned as the edge
774    /// *multiplicity*. Edge directions are ignored and the projections are
775    /// undirected simple graphs.
776    ///
777    /// By default (`probe1 = None`), [`proj1`](BipartiteProjection::proj1)
778    /// is the projection of the vertices of type `false`. If `probe1` is
779    /// `Some(v)`, `proj1` is the projection containing vertex `v` instead.
780    ///
781    /// Binds [`igraph_bipartite_projection`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_bipartite_projection).
782    /// Time complexity: O(|V|·d²+|E|), d being the average degree.
783    ///
784    /// # Errors
785    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `types`
786    /// has the wrong length or an edge joins vertices of the same type;
787    /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if
788    /// `probe1` is not a vertex.
789    ///
790    /// # Examples
791    /// ```
792    /// use igraph::prelude::*;
793    /// // Two papers (0, 1) and three authors (2, 3, 4).
794    /// let types = [false, false, true, true, true];
795    /// let g = Graph::create_bipartite(&types, &[(0, 2), (0, 3), (1, 3), (1, 4)], false).unwrap();
796    /// let p = g.bipartite_projection(&types, None).unwrap();
797    /// // The papers share author 3.
798    /// assert_eq!(p.proj1.edge_list(), vec![(0, 1)]);
799    /// // Co-authorship: 2-3 and 3-4 (projected ids 0, 1, 2).
800    /// assert_eq!(p.proj2.edge_list(), vec![(0, 1), (1, 2)]);
801    /// assert_eq!(p.multiplicity2, vec![1, 1]);
802    /// ```
803    pub fn bipartite_projection(
804        &self,
805        types: &[bool],
806        probe1: Option<VertexId>,
807    ) -> Result<BipartiteProjection> {
808        check_types(self, types)?;
809        let probe1 = match probe1 {
810            Some(v) if v < 0 || v as usize >= self.vcount() => {
811                return Err(Error::new(
812                    crate::ErrorKind::InvalidVertexId,
813                    format!("invalid probe vertex {v}"),
814                ));
815            }
816            Some(v) => v,
817            None => -1,
818        };
819        let t = VectorBool::view(types);
820        let mut m1 = VectorInt::new();
821        let mut m2 = VectorInt::new();
822        let mut proj2 = None;
823        let mut inner_error = None;
824        let proj1 = Graph::init_with(|p1| {
825            match Graph::init_with(|p2| unsafe {
826                igraph_bipartite_projection(self, t.as_ptr(), p1, p2, &mut m1, &mut m2, probe1)
827            }) {
828                Ok(g) => {
829                    proj2 = Some(g);
830                    igraph_error_type_t_IGRAPH_SUCCESS
831                }
832                // On failure igraph has already destroyed `proj1`: nothing leaks.
833                Err(e) => {
834                    let code = e.code();
835                    inner_error = Some(e);
836                    code
837                }
838            }
839        });
840        match (proj1, proj2) {
841            (Ok(proj1), Some(proj2)) => Ok(BipartiteProjection {
842                proj1,
843                proj2,
844                multiplicity1: m1.into(),
845                multiplicity2: m2.into(),
846            }),
847            // The inner call consumed igraph's error record: report its error.
848            (Err(e), _) => Err(inner_error.unwrap_or(e)),
849            (Ok(_), None) => Err(Error::new(crate::ErrorKind::Internal, "missing projection")),
850        }
851    }
852
853    /// The one-mode projection onto the vertices of type `kind`, with its
854    /// edge multiplicities.
855    ///
856    /// It computes only one of the two projections of
857    /// [`bipartite_projection`](Self::bipartite_projection), saving time and
858    /// memory when the other one is not needed.
859    ///
860    /// Binds [`igraph_bipartite_projection`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_bipartite_projection).
861    ///
862    /// # Errors
863    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `types`
864    /// has the wrong length or an edge joins vertices of the same type.
865    ///
866    /// # Examples
867    /// ```
868    /// use igraph::prelude::*;
869    /// let star = Graph::full_bipartite(1, 4, false, NeighborMode::All).unwrap();
870    /// // The four leaves all share the center: they form a K4.
871    /// let (leaves, mult) = star.graph.bipartite_projection_of(&star.types, true).unwrap();
872    /// assert_eq!((leaves.vcount(), leaves.ecount()), (4, 6));
873    /// assert!(mult.iter().all(|&m| m == 1));
874    /// ```
875    pub fn bipartite_projection_of(&self, types: &[bool], kind: bool) -> Result<(Graph, Vec<i64>)> {
876        check_types(self, types)?;
877        let t = VectorBool::view(types);
878        let mut mult = VectorInt::new();
879        let proj = if kind {
880            Graph::init_with(|p| unsafe {
881                igraph_bipartite_projection(
882                    self,
883                    t.as_ptr(),
884                    ptr::null_mut(),
885                    p,
886                    ptr::null_mut(),
887                    &mut mult,
888                    -1,
889                )
890            })?
891        } else {
892            Graph::init_with(|p| unsafe {
893                igraph_bipartite_projection(
894                    self,
895                    t.as_ptr(),
896                    p,
897                    ptr::null_mut(),
898                    &mut mult,
899                    ptr::null_mut(),
900                    -1,
901                )
902            })?
903        };
904        Ok((proj, mult.into()))
905    }
906
907    /// A random bipartite graph from the *G(n1, n2, p)* model.
908    ///
909    /// Every possible edge between the `n1` bottom vertices (type `false`,
910    /// ids `0..n1`) and the `n2` top vertices (type `true`) is realized
911    /// independently with probability `p`. When multi-edges are allowed
912    /// (see [`BipartiteGameOptions`]), `p` is the *expected number* of edges
913    /// between each pair and may exceed 1.
914    ///
915    /// Binds [`igraph_bipartite_game_gnp`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_bipartite_game_gnp).
916    /// Uses the thread's default random number generator (see [`crate::rng`]).
917    /// Time complexity: O(|V|+|E|).
918    ///
919    /// See also [`Graph::erdos_renyi_game_gnp`], the one-mode version, and
920    /// [`bipartite_game_gnm`](Self::bipartite_game_gnm) for a fixed number
921    /// of edges.
922    ///
923    /// Self-loops are impossible in a bipartite graph, so
924    /// [`EdgeTypeSw::Loops`] behaves like [`EdgeTypeSw::Simple`].
925    ///
926    /// # Errors
927    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `p` is
928    /// NaN, infinite, negative, or larger than 1 without multi-edges;
929    /// [`ErrorKind::Unimplemented`](crate::ErrorKind::Unimplemented) if
930    /// [`BipartiteGameOptions::edge_labeled`] is set without multi-edges
931    /// (igraph 1.0.0 and 1.0.1 implement the edge-labeled G(n1, n2, p) model
932    /// only for multigraphs).
933    ///
934    /// # Examples
935    /// ```
936    /// use igraph::prelude::*;
937    /// rng::seed(7).unwrap();
938    /// let b = Graph::bipartite_game_gnp(10, 20, 0.3, &Default::default()).unwrap();
939    /// assert_eq!(b.graph.vcount(), 30);
940    /// assert!(b.graph.is_bipartite().unwrap());
941    /// let full = Graph::bipartite_game_gnp(3, 4, 1.0, &Default::default()).unwrap();
942    /// assert_eq!(full.graph.ecount(), 12);
943    /// ```
944    pub fn bipartite_game_gnp(
945        n1: usize,
946        n2: usize,
947        p: f64,
948        options: &BipartiteGameOptions,
949    ) -> Result<BipartiteGraph> {
950        // igraph 1.0.0 and 1.0.1 let NaN through (`p < 0.0 || p > 1.0` is
951        // false for NaN) and misbehave on infinite values.
952        if !p.is_finite() {
953            return Err(Error::invalid(format!(
954                "the edge probability (or multiplicity) must be finite, got {p}"
955            )));
956        }
957        let (n1, n2) = (to_int(n1, "n1")?, to_int(n2, "n2")?);
958        let mut types = VectorBool::new();
959        let graph = Graph::init_with(|g| unsafe {
960            igraph_bipartite_game_gnp(
961                g,
962                &mut types,
963                n1,
964                n2,
965                p,
966                options.directed,
967                options.mode.into(),
968                options.allowed_edge_types.into(),
969                options.edge_labeled,
970            )
971        })?;
972        Ok(BipartiteGraph {
973            graph,
974            types: types.into(),
975        })
976    }
977
978    /// A uniformly random bipartite graph from the *G(n1, n2, m)* model:
979    /// `n1` bottom vertices (type `false`), `n2` top vertices (type `true`)
980    /// and exactly `m` edges.
981    ///
982    /// With [`BipartiteGameOptions::edge_labeled`], sampling is uniform over
983    /// ordered edge lists rather than over graphs (this matters only when
984    /// multi-edges are allowed).
985    ///
986    /// Binds [`igraph_bipartite_game_gnm`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_bipartite_game_gnm).
987    /// Uses the thread's default random number generator (see [`crate::rng`]).
988    /// Time complexity: O(|V|+|E|).
989    ///
990    /// See also [`Graph::erdos_renyi_game_gnm`], the one-mode version, and
991    /// [`bipartite_iea_game`](Self::bipartite_iea_game) for a faster,
992    /// non-uniform multigraph model.
993    ///
994    /// # Errors
995    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `m` is
996    /// positive while `n1` or `n2` is zero, or, without multi-edges, if `m`
997    /// is larger than the number of possible edges (`n1·n2`, or `2·n1·n2`
998    /// for directed graphs with [`NeighborMode::All`]).
999    ///
1000    /// # Examples
1001    /// ```
1002    /// use igraph::prelude::*;
1003    /// rng::seed(1).unwrap();
1004    /// let b = Graph::bipartite_game_gnm(5, 5, 12, &Default::default()).unwrap();
1005    /// assert_eq!(b.graph.ecount(), 12);
1006    /// assert!(Graph::bipartite_game_gnm(2, 2, 5, &Default::default()).is_err());
1007    /// ```
1008    pub fn bipartite_game_gnm(
1009        n1: usize,
1010        n2: usize,
1011        m: usize,
1012        options: &BipartiteGameOptions,
1013    ) -> Result<BipartiteGraph> {
1014        let (n1, n2, m) = (to_int(n1, "n1")?, to_int(n2, "n2")?, to_int(m, "m")?);
1015        let mut types = VectorBool::new();
1016        let graph = Graph::init_with(|g| unsafe {
1017            igraph_bipartite_game_gnm(
1018                g,
1019                &mut types,
1020                n1,
1021                n2,
1022                m,
1023                options.directed,
1024                options.mode.into(),
1025                options.allowed_edge_types.into(),
1026                options.edge_labeled,
1027            )
1028        })?;
1029        Ok(BipartiteGraph {
1030            graph,
1031            types: types.into(),
1032        })
1033    }
1034
1035    /// A random bipartite multigraph by *independent edge assignment* (IEA):
1036    /// each of the `m` edges joins a uniformly random bottom–top pair,
1037    /// independently of the others.
1038    ///
1039    /// There are `n1` bottom vertices (type `false`) and `n2` top vertices
1040    /// (type `true`). The resulting multigraphs are not uniformly sampled:
1041    /// a graph has probability proportional to `1 / ∏ A_ij!`, so all simple
1042    /// graphs are equally likely. `mode` directs the edges of directed
1043    /// graphs as in [`BipartiteGameOptions::mode`].
1044    ///
1045    /// This model is *experimental* in igraph (1.0.0 and 1.0.1). It is the
1046    /// same as [`bipartite_game_gnm`](Self::bipartite_game_gnm) with
1047    /// multi-edges allowed and [`BipartiteGameOptions::edge_labeled`] set.
1048    /// Uses the thread's default random number generator (see [`crate::rng`]).
1049    /// See also [`Graph::iea_game`], the one-mode version.
1050    ///
1051    /// Implements [`igraph_bipartite_iea_game`](https://igraph.org/c/html/latest/igraph-Bipartite.html#igraph_bipartite_iea_game)
1052    /// through `igraph_bipartite_game_gnm`: in igraph 1.0.0 and 1.0.1 the C
1053    /// function forwards to the *edge-unlabeled* multigraph G(n1, n2, m)
1054    /// model by mistake, and so samples multigraphs uniformly instead of by
1055    /// independent edge assignment (unlike its one-mode counterpart
1056    /// `igraph_iea_game`). This wrapper calls the edge-labeled model
1057    /// directly, which is the documented IEA process.
1058    /// Time complexity: O(|V|+|E|).
1059    ///
1060    /// # Examples
1061    /// ```
1062    /// use igraph::prelude::*;
1063    /// rng::seed(3).unwrap();
1064    /// // 100 edges between 2 x 2 vertices: plenty of multi-edges.
1065    /// let b = Graph::bipartite_iea_game(2, 2, 100, false, NeighborMode::Out).unwrap();
1066    /// assert_eq!(b.graph.ecount(), 100);
1067    /// assert!(b.graph.edge_list().iter().all(|&(u, v)| u < 2 && v >= 2));
1068    /// ```
1069    pub fn bipartite_iea_game(
1070        n1: usize,
1071        n2: usize,
1072        m: usize,
1073        directed: bool,
1074        mode: NeighborMode,
1075    ) -> Result<BipartiteGraph> {
1076        let (n1, n2, m) = (to_int(n1, "n1")?, to_int(n2, "n2")?, to_int(m, "m")?);
1077        let mut types = VectorBool::new();
1078        // Not `igraph_bipartite_iea_game`, which is buggy: see the docs.
1079        let graph = Graph::init_with(|g| unsafe {
1080            igraph_bipartite_game_gnm(
1081                g,
1082                &mut types,
1083                n1,
1084                n2,
1085                m,
1086                directed,
1087                mode.into(),
1088                EdgeTypeSw::Multi.into(),
1089                true,
1090            )
1091        })?;
1092        Ok(BipartiteGraph {
1093            graph,
1094            types: types.into(),
1095        })
1096    }
1097
1098    /// Whether `matching` is a valid matching of the graph.
1099    ///
1100    /// `matching` has one entry per vertex: the vertex it is matched to, or
1101    /// [`UNMATCHED`] (`-1`). It is valid if its length is the number of
1102    /// vertices, it is symmetric (`matching[matching[i]] == i`), and every
1103    /// matched pair is joined by an edge (directions are ignored). If
1104    /// `types` is given, matched vertices must also have different types.
1105    /// An invalid vector yields `Ok(false)`, not an error.
1106    ///
1107    /// Binds [`igraph_is_matching`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_matching).
1108    /// Time complexity: O(|V|+|E|).
1109    ///
1110    /// # Examples
1111    /// ```
1112    /// use igraph::prelude::*;
1113    /// let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1114    /// assert!(path.is_matching(None, &[1, 0, 3, 2]).unwrap());
1115    /// assert!(path.is_matching(None, &[-1, 2, 1, -1]).unwrap());
1116    /// assert!(!path.is_matching(None, &[3, -1, -1, 0]).unwrap()); // no edge 0-3
1117    /// ```
1118    pub fn is_matching(&self, types: Option<&[bool]>, matching: &[VertexId]) -> Result<bool> {
1119        self.check_matching(types, matching, igraph_is_matching)
1120    }
1121
1122    /// Whether `matching` is a *maximal* matching of the graph: a valid
1123    /// matching (see [`is_matching`](Self::is_matching)) that cannot be
1124    /// extended, i.e. no two unmatched vertices are adjacent (if `types` is
1125    /// given, only edges joining vertices of different types count).
1126    ///
1127    /// A maximal matching is not necessarily *maximum* (largest): on the
1128    /// path `0-1-2-3`, matching only `1-2` is maximal but not maximum.
1129    ///
1130    /// Binds [`igraph_is_maximal_matching`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_maximal_matching).
1131    /// Time complexity: O(|V|+|E|).
1132    ///
1133    /// # Examples
1134    /// ```
1135    /// use igraph::prelude::*;
1136    /// let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1137    /// assert!(path.is_maximal_matching(None, &[-1, 2, 1, -1]).unwrap());
1138    /// assert!(!path.is_maximal_matching(None, &[1, 0, -1, -1]).unwrap()); // 2-3 can be added
1139    /// ```
1140    pub fn is_maximal_matching(
1141        &self,
1142        types: Option<&[bool]>,
1143        matching: &[VertexId],
1144    ) -> Result<bool> {
1145        self.check_matching(types, matching, igraph_is_maximal_matching)
1146    }
1147
1148    fn check_matching(
1149        &self,
1150        types: Option<&[bool]>,
1151        matching: &[VertexId],
1152        f: unsafe extern "C" fn(
1153            *const igraph_t,
1154            *const igraph_vector_bool_t,
1155            *const igraph_vector_int_t,
1156            *mut igraph_bool_t,
1157        ) -> igraph_error_t,
1158    ) -> Result<bool> {
1159        if let Some(t) = types {
1160            check_types(self, t)?;
1161        }
1162        let t = types.map(VectorBool::view);
1163        let tp = t.as_ref().map_or(ptr::null(), |v| v.as_ptr());
1164        let m = VectorInt::view(matching);
1165        let mut res = false;
1166        igraph_call!(f(self, tp, m.as_ptr(), &mut res))?;
1167        Ok(res)
1168    }
1169
1170    /// A maximum matching of a bipartite graph: the largest set of edges no
1171    /// two of which share a vertex or, with `weights`, the matching of
1172    /// largest total weight.
1173    ///
1174    /// Unweighted matchings are found with a push-relabel algorithm, in
1175    /// O(√|V|·|E|) time; weighted ones with the Hungarian algorithm, in
1176    /// O(|V|·|E|) time. The weighted algorithm is reliable only for integer
1177    /// weights; slacks of at most `f64::EPSILON` are considered zero (use
1178    /// [`maximum_bipartite_matching_eps`](Self::maximum_bipartite_matching_eps)
1179    /// to tune this tolerance). Edge directions are ignored.
1180    ///
1181    /// A weighted maximum matching maximizes the total weight, not the
1182    /// number of pairs: edges of negative weight are never chosen, so
1183    /// [`BipartiteMatching::size`] may be smaller than in the unweighted
1184    /// case.
1185    ///
1186    /// The size of an unweighted maximum matching equals the maximum flow
1187    /// from one class to the other with unit capacities (see
1188    /// [`Graph::maxflow_value`]) and, by König's theorem, the size of a
1189    /// minimum vertex cover. Check a result with
1190    /// [`is_matching`](Self::is_matching) and
1191    /// [`is_maximal_matching`](Self::is_maximal_matching).
1192    ///
1193    /// Binds [`igraph_maximum_bipartite_matching`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_maximum_bipartite_matching).
1194    ///
1195    /// # Errors
1196    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `types`
1197    /// or `weights` have the wrong length, an edge joins two vertices of
1198    /// the same type (self-loops included), or a weight is not finite.
1199    /// These are checked on the Rust side, because igraph's own checks are
1200    /// incomplete (in igraph 1.0.0 and 1.0.1): it can silently return a wrong
1201    /// matching, or loop forever on infinite weights.
1202    ///
1203    /// # Examples
1204    /// ```
1205    /// use igraph::prelude::*;
1206    /// // Weighted example from igraph's unit tests.
1207    /// let types: Vec<bool> = (0..10).map(|i| i >= 5).collect();
1208    /// let g = Graph::from_edges(&[(0, 8), (2, 7), (3, 7), (3, 8), (4, 5), (4, 9)], 10, false).unwrap();
1209    /// let w = [8.0, 5.0, 9.0, 18.0, 20.0, 13.0];
1210    /// let m = g.maximum_bipartite_matching(&types, Some(&w)).unwrap();
1211    /// assert_eq!(m.weight, 43.0); // 2-7, 3-8 and 4-5: 5 + 18 + 20
1212    /// assert_eq!(m.pairs(), vec![(2, 7), (3, 8), (4, 5)]);
1213    /// // Vertices 0 and 3 compete for 8, 2 and 3 for 7: at most 3 pairs.
1214    /// let m = g.maximum_bipartite_matching(&types, None).unwrap();
1215    /// assert_eq!((m.size, m.weight), (3, 3.0));
1216    /// ```
1217    pub fn maximum_bipartite_matching(
1218        &self,
1219        types: &[bool],
1220        weights: Option<&[f64]>,
1221    ) -> Result<BipartiteMatching> {
1222        self.maximum_bipartite_matching_eps(types, weights, f64::EPSILON)
1223    }
1224
1225    /// Like [`maximum_bipartite_matching`](Self::maximum_bipartite_matching),
1226    /// with an explicit tolerance `eps` for the equality tests of the
1227    /// weighted algorithm (ignored if `weights` is `None`).
1228    ///
1229    /// An edge is considered *tight* when its slack (the difference between
1230    /// the sum of the dual labels of its endpoints and its weight) is at most
1231    /// `eps`; a small positive value avoids the accumulation of rounding
1232    /// errors with fractional weights, while with integer weights
1233    /// `eps = 0.0` is safe. A negative `eps` is clamped to zero by igraph
1234    /// (with a warning).
1235    ///
1236    /// Binds [`igraph_maximum_bipartite_matching`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_maximum_bipartite_matching).
1237    ///
1238    /// # Errors
1239    /// As [`maximum_bipartite_matching`](Self::maximum_bipartite_matching);
1240    /// also [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
1241    /// `eps` is NaN (igraph would never terminate).
1242    ///
1243    /// # Examples
1244    /// ```
1245    /// use igraph::prelude::*;
1246    /// let k22 = Graph::full_bipartite(2, 2, false, NeighborMode::All).unwrap();
1247    /// // Edges 0-2, 0-3, 1-2, 1-3: the diagonal 0-2, 1-3 is the best.
1248    /// let w = [0.5, 0.25, 0.25, 0.5];
1249    /// let m = k22.graph.maximum_bipartite_matching_eps(&k22.types, Some(&w), 1e-9).unwrap();
1250    /// assert_eq!(m.pairs(), vec![(0, 2), (1, 3)]);
1251    /// assert_eq!(m.weight, 1.0);
1252    /// ```
1253    pub fn maximum_bipartite_matching_eps(
1254        &self,
1255        types: &[bool],
1256        weights: Option<&[f64]>,
1257        eps: f64,
1258    ) -> Result<BipartiteMatching> {
1259        check_types(self, types)?;
1260        check_weights(self, weights)?;
1261        check_finite_weights(weights)?;
1262        check_bipartite_edges(self, types)?;
1263        if eps.is_nan() {
1264            return Err(Error::invalid("eps must not be NaN"));
1265        }
1266        let t = VectorBool::view(types);
1267        let w = weights.map(Vector::view);
1268        let wp = w.as_ref().map_or(ptr::null(), |v| v.as_ptr());
1269        let mut size: igraph_int_t = 0;
1270        let mut weight: igraph_real_t = 0.0;
1271        let mut matching = VectorInt::new();
1272        igraph_call!(igraph_maximum_bipartite_matching(
1273            self,
1274            t.as_ptr(),
1275            &mut size,
1276            &mut weight,
1277            &mut matching,
1278            wp,
1279            eps
1280        ))?;
1281        Ok(BipartiteMatching {
1282            size: size as usize,
1283            weight,
1284            matching: matching.into(),
1285        })
1286    }
1287}