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}