igraph/constructors.rs
1//! Deterministic graph generators (`igraph_constructors.h`).
2//!
3//! This module turns igraph's *deterministic constructors* into associated
4//! functions of [`Graph`] returning [`Result`]`<Graph>`: given the same
5//! arguments they always build the very same graph, with the same vertex and
6//! edge ids. (Random generators live in [`crate::games`].)
7//!
8//! ```
9//! use igraph::prelude::*;
10//!
11//! // The Petersen graph, three ways: by name, as G(5, 2), and from its LCF code.
12//! let by_name = Graph::famous("Petersen").unwrap();
13//! let gp = Graph::generalized_petersen(5, 2).unwrap();
14//! assert_eq!((by_name.vcount(), by_name.ecount()), (10, 15));
15//! assert_eq!((gp.vcount(), gp.ecount()), (10, 15));
16//! // Both are 3-regular.
17//! for g in [&by_name, &gp] {
18//! let degrees = g.degree(VertexSelector::All, NeighborMode::All, Loops::Twice).unwrap();
19//! assert!(degrees.iter().all(|&d| d == 3));
20//! }
21//!
22//! // A 3x4 grid, a 6-cycle and a star with 5 leaves.
23//! let grid = Graph::square_lattice(&[3, 4], 1, false, false, None).unwrap();
24//! assert_eq!((grid.vcount(), grid.ecount()), (12, 17));
25//! let c6 = Graph::cycle_graph(6, false, false).unwrap();
26//! assert_eq!(c6.ecount(), 6);
27//! let star = Graph::star(6, StarMode::Undirected, 0).unwrap();
28//! assert_eq!(star.degree_of(0, NeighborMode::All, Loops::Twice).unwrap(), 5);
29//! ```
30//!
31//! # Provided functionality
32//!
33//! | Family | Functions |
34//! |---|---|
35//! | From matrices | [`Graph::adjacency`], [`Graph::weighted_adjacency`], [`Graph::sparse_adjacency`], [`Graph::sparse_weighted_adjacency`] |
36//! | From edge lists | [`Graph::small`] (see also [`Graph::from_edges`]) |
37//! | Paths, cycles, stars | [`Graph::ring`], [`Graph::path_graph`], [`Graph::cycle_graph`], [`Graph::star`], [`Graph::wheel`] |
38//! | Complete graphs | [`Graph::full`], [`Graph::full_citation`], [`Graph::full_multipartite`], [`Graph::turan`] |
39//! | Lattices | [`Graph::square_lattice`], [`Graph::triangular_lattice`], [`Graph::hexagonal_lattice`], [`Graph::hypercube`] |
40//! | Trees | [`Graph::kary_tree`], [`Graph::symmetric_tree`], [`Graph::regular_tree`], [`Graph::tree_from_parent_vector`], [`Graph::from_prufer`] |
41//! | Circulant-like | [`Graph::circulant`], [`Graph::generalized_petersen`], [`Graph::lcf`], [`Graph::extended_chordal_ring`] |
42//! | Word graphs | [`Graph::de_bruijn`], [`Graph::kautz`] |
43//! | Named graphs | [`Graph::famous`] (with [`FamousGraph`]), [`Graph::atlas`], [`Graph::mycielski_graph`] |
44//! | Degree sequences | [`Graph::realize_degree_sequence`], [`Graph::realize_bipartite_degree_sequence`] |
45//! | Derived graphs | [`Graph::linegraph`] |
46//!
47//! The corresponding chapter of the C documentation is
48//! [Deterministic graph generators](https://igraph.org/c/html/latest/igraph-Generators.html).
49//!
50//! # Conventions
51//!
52//! - Vertex counts and sizes are `usize`; values that may legitimately be
53//! negative (shifts, parent ids, degrees coming from
54//! [`Graph::degree`]) are `i64`.
55//! - Every function returns an [`Error`] rather than panicking on invalid
56//! input; most errors are [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue).
57//!
58//! # See also
59//!
60//! | Need | Where |
61//! |---|---|
62//! | Random graphs (Erdős–Rényi, random regular, random trees, degree sequences, ...) | [`crate::games`], e.g. [`Graph::erdos_renyi_game_gnm`], [`Graph::k_regular_game`], [`Graph::tree_game`], [`Graph::degree_sequence_game`] |
63//! | The inverse conversions (graph → matrix, tree → Prüfer code) | [`Graph::get_adjacency`], [`Graph::get_adjacency_sparse`], [`Graph::to_prufer`] in [`crate::conversion`] |
64//! | Bipartite constructors | [`Graph::full_bipartite`], [`BipartiteGraph`](crate::bipartite::BipartiteGraph) in [`crate::bipartite`] |
65//! | Graphs derived from other graphs | [`Graph::complementer`], [`Graph::mycielskian`], [`Graph::disjoint_union`] in [`crate::operators`] |
66//! | Is a degree sequence realizable at all? | [`is_graphical`](crate::mixing::is_graphical), [`is_bigraphical`](crate::mixing::is_bigraphical) |
67//! | Comparing generated graphs up to relabelling | [`Graph::isomorphic`], [`Graph::count_automorphisms`] in [`crate::isomorphism`] |
68//! | Drawing them | [`Graph::layout_circle`] (rings), [`Graph::layout_star`], [`Graph::layout_grid`] (lattices), [`Graph::layout_reingold_tilford`] (trees) in [`crate::layout`] |
69//! | Reading graphs from files | [`crate::foreign`] |
70//!
71//! ```
72//! use igraph::prelude::*;
73//!
74//! // LCF notation and the named graph agree up to relabelling: the Heawood
75//! // graph two ways.
76//! let heawood = Graph::famous("Heawood")?;
77//! assert!(Graph::lcf(14, &[5, -5], 7)?.isomorphic(&heawood)?);
78//! // Its automorphism group PGL(2, 7) has order 336, and its girth is 6.
79//! assert_eq!(heawood.count_automorphisms(None)?, 336.0);
80//! assert_eq!(heawood.girth()?, Some(6));
81//!
82//! // Round trip through the adjacency matrix of the conversion module.
83//! let a = heawood.get_adjacency(GetAdjacency::Both, None, Loops::Twice)?;
84//! assert_eq!(Graph::adjacency(&a, Adjacency::Undirected, Loops::Twice)?, heawood);
85//! # Ok::<(), igraph::Error>(())
86//! ```
87//!
88//! # Differences from the C library
89//!
90//! A few defects of igraph 1.0.0 and 1.0.1 (the generator sources are
91//! unchanged in 1.0.1) are worked around on the Rust side, so the wrappers
92//! behave as documented:
93//!
94//! - [`Graph::sparse_adjacency`] / [`Graph::sparse_weighted_adjacency`]
95//! agree with their dense counterparts even when an entry below the
96//! diagonal has no mirror entry (igraph drops it in the unweighted `Max`
97//! mode and in the weighted `Max`, `Min` and `Plus` modes), and when
98//! explicit zeros are given in the `Undirected` mode (igraph's structural
99//! symmetry test would reject them).
100//! - [`Graph::adjacency`] and [`Graph::sparse_adjacency`] reject NaN,
101//! infinite, negative and fractional edge counts (igraph casts them to
102//! integers unchecked).
103//! - [`Graph::star`] and [`Graph::wheel`] with `n = 1` give the singleton
104//! graph (igraph returns the null graph).
105//! - [`Graph::lcf`] with `n = 0` gives the null graph (igraph divides by
106//! zero), and LCF shifts and chordal ring offsets are reduced modulo the
107//! number of vertices (igraph adds them without overflow checks).
108//! - [`Graph::extended_chordal_ring`] accepts a chord matrix without columns
109//! (igraph divides by the number of columns).
110
111use crate::{
112 constants::{Adjacency, Loops, NeighborMode, RealizeDegseq, StarMode, TreeMode, WheelMode},
113 error::{Error, Result},
114 ffi::*,
115 graph::{Graph, VertexId},
116 linalg::SparseMat,
117 matrix::{Matrix, MatrixInt},
118 vector::{Vector, VectorBool, VectorInt},
119};
120use std::{collections::BTreeMap, ffi::CString};
121
122/// Converts a count to `igraph_int_t`, failing on overflow.
123fn int(n: usize, what: &str) -> Result<igraph_int_t> {
124 igraph_int_t::try_from(n).map_err(|_| Error::invalid(format!("{what} is too large: {n}")))
125}
126
127/// `base^exp` in exact integer arithmetic, `None` on overflow.
128///
129/// igraph's de Bruijn and Kautz generators compute these powers as doubles
130/// and convert them to `igraph_int_t` *before* checking the range, which is
131/// undefined behaviour for results of 2^63 and more: callers check here first.
132fn checked_ipow(base: igraph_int_t, exp: igraph_int_t) -> Option<igraph_int_t> {
133 match base {
134 0 => Some(if exp == 0 { 1 } else { 0 }),
135 1 => Some(1),
136 _ => base.checked_pow(u32::try_from(exp).ok()?),
137 }
138}
139
140/// For hexagon-shaped lattices (`dims.len() == 3`) igraph computes row counts,
141/// lengths and starts from sums of the sizes without overflow checks. Rejects
142/// sizes for which `2 * (d0 + d1 + d2) + 4`, a bound on all of them, overflows.
143fn check_hex_shape(dims: &[igraph_int_t], what: &str) -> Result<()> {
144 if dims.len() == 3 {
145 dims.iter()
146 .try_fold(0 as igraph_int_t, |acc, &d| acc.checked_add(d))
147 .and_then(|sum| sum.checked_mul(2))
148 .and_then(|b| b.checked_add(4))
149 .ok_or_else(|| {
150 Error::invalid(format!("Lattice dimensions {dims:?} too large for {what}."))
151 })?;
152 }
153 Ok(())
154}
155
156/// Converts a slice of counts into an owned igraph integer vector.
157fn int_vector(values: &[usize], what: &str) -> Result<VectorInt> {
158 values
159 .iter()
160 .map(|&v| int(v, what))
161 .collect::<Result<Vec<_>>>()
162 .map(VectorInt::from)
163}
164
165/// In igraph 1.0.0 and 1.0.1, `igraph_star` (and thus `igraph_wheel`) builds the graph
166/// from its edge list with no explicit vertex count, so `n = 1` yields the
167/// null graph. Adds the missing vertices, if any.
168fn fix_single_vertex(mut g: Graph, n: igraph_int_t) -> Result<Graph> {
169 let missing = usize::try_from(n).unwrap_or(0).saturating_sub(g.vcount());
170 if missing > 0 {
171 g.add_vertices(missing)?;
172 }
173 Ok(g)
174}
175
176/// Which kinds of edges a degree sequence realization may use
177/// (`igraph_edge_type_sw_t` flags, including their combination), taken by
178/// [`Graph::realize_degree_sequence`] and
179/// [`Graph::realize_bipartite_degree_sequence`].
180///
181/// Re-exported from [`constants`](crate::constants::AllowedEdgeTypes); it is
182/// the same type as the one taken by
183/// [`is_graphical`](crate::mixing::is_graphical), so the same value can first
184/// test and then realize a degree sequence:
185///
186/// ```
187/// use igraph::{constructors::AllowedEdgeTypes, mixing::is_graphical, prelude::*};
188/// let degrees = [4, 2];
189/// for allowed in [AllowedEdgeTypes::MULTI, AllowedEdgeTypes::ALL] {
190/// let graphical = is_graphical(°rees, None, allowed).unwrap();
191/// let realized =
192/// Graph::realize_degree_sequence(°rees, None, allowed, RealizeDegseq::Smallest);
193/// assert_eq!(graphical, realized.is_ok());
194/// }
195/// // Without self-loops, vertex 0 cannot place 4 stubs on its only neighbour's 2.
196/// assert!(!is_graphical(°rees, None, AllowedEdgeTypes::MULTI).unwrap());
197/// ```
198pub use crate::constants::AllowedEdgeTypes;
199
200macro_rules! famous_graphs {
201 ($( $(#[$meta:meta])* $variant:ident = $name:literal, $n:literal, $m:literal; )+) => {
202 /// The named graphs known to [`Graph::famous`].
203 ///
204 /// Each variant documents its size; [`FamousGraph::name`] gives the
205 /// name understood by igraph, and [`FamousGraph::ALL`] lists them all.
206 /// `FamousGraph` implements `AsRef<str>`, so it can be passed to
207 /// [`Graph::famous`] directly:
208 ///
209 /// ```
210 /// use igraph::{constructors::FamousGraph, prelude::*};
211 /// let g = Graph::famous(FamousGraph::Heawood).unwrap();
212 /// assert_eq!((g.vcount(), g.ecount()), FamousGraph::Heawood.size());
213 /// ```
214 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
215 pub enum FamousGraph {
216 $( $(#[$meta])* $variant ),+
217 }
218
219 impl FamousGraph {
220 /// All the famous graphs, in the order of the igraph documentation
221 /// (alphabetical, except that `Noperfectmatching` precedes `Nonline`).
222 pub const ALL: &'static [FamousGraph] = &[$(FamousGraph::$variant),+];
223
224 /// The name igraph uses for this graph (matching is case insensitive).
225 pub fn name(self) -> &'static str {
226 match self { $(FamousGraph::$variant => $name),+ }
227 }
228
229 /// The `(vertex count, edge count)` of this graph.
230 pub fn size(self) -> (usize, usize) {
231 match self { $(FamousGraph::$variant => ($n, $m)),+ }
232 }
233 }
234 };
235}
236
237famous_graphs! {
238 /// The bull graph: a triangle with two pendant "horns" (5 vertices, 5 edges).
239 Bull = "Bull", 5, 5;
240 /// The Chvátal graph: the smallest triangle-free, 4-chromatic, 4-regular graph (12, 24).
241 Chvatal = "Chvatal", 12, 24;
242 /// The Coxeter graph: a non-Hamiltonian cubic symmetric graph (28, 42).
243 Coxeter = "Coxeter", 28, 42;
244 /// The skeleton of the cube (8, 12).
245 Cubical = "Cubical", 8, 12;
246 /// The diamond: two triangles sharing an edge (4, 5).
247 Diamond = "Diamond", 4, 5;
248 /// The skeleton of the dodecahedron (20, 30).
249 Dodecahedron = "Dodecahedron", 20, 30;
250 /// The Folkman graph: the smallest semisymmetric graph (20, 40).
251 Folkman = "Folkman", 20, 40;
252 /// The Franklin graph, related to colorings of the Klein bottle (12, 18).
253 Franklin = "Franklin", 12, 18;
254 /// The Frucht graph: the smallest cubic graph with no non-trivial automorphism (12, 18).
255 Frucht = "Frucht", 12, 18;
256 /// The Grötzsch graph: triangle-free with chromatic number 4 (11, 20).
257 Grotzsch = "Grotzsch", 11, 20;
258 /// The Heawood graph: the 6-cage, the smallest cubic graph of girth 6 (14, 21).
259 Heawood = "Heawood", 14, 21;
260 /// The Herschel graph: the smallest non-Hamiltonian polyhedral graph (11, 18).
261 Herschel = "Herschel", 11, 18;
262 /// The house graph: a triangle on top of a square (5, 6).
263 House = "House", 5, 6;
264 /// The house graph with an X in the square (5, 8).
265 HouseX = "HouseX", 5, 8;
266 /// The skeleton of the icosahedron (12, 30).
267 Icosahedron = "Icosahedron", 12, 30;
268 /// Krackhardt's kite social network (10, 18).
269 KrackhardtKite = "Krackhardt_Kite", 10, 18;
270 /// The Levi graph: a 4-arc transitive cubic graph (30, 45).
271 Levi = "Levi", 30, 45;
272 /// The McGee graph: the unique 3-regular 7-cage (24, 36).
273 McGee = "McGee", 24, 36;
274 /// The Meredith graph: 4-regular, 4-connected and non-Hamiltonian (70, 140).
275 Meredith = "Meredith", 70, 140;
276 /// A connected graph without a perfect matching (16, 27).
277 NoPerfectMatching = "Noperfectmatching", 16, 27;
278 /// The disjoint union of the 9 forbidden subgraphs of line graphs (50, 72).
279 Nonline = "Nonline", 50, 72;
280 /// The skeleton of the octahedron (6, 12).
281 Octahedron = "Octahedron", 6, 12;
282 /// The Petersen graph: 3-regular, the smallest hypohamiltonian graph (10, 15).
283 Petersen = "Petersen", 10, 15;
284 /// The Robertson graph: the unique (4,5)-cage (19, 38).
285 Robertson = "Robertson", 19, 38;
286 /// A smallest non-trivial graph whose automorphism group is cyclic (9, 15).
287 SmallestCyclicGroup = "Smallestcyclicgroup", 9, 15;
288 /// The skeleton of the tetrahedron, i.e. `K_4` (4, 6).
289 Tetrahedron = "Tetrahedron", 4, 6;
290 /// The Thomassen graph: the smallest hypotraceable graph (34, 52).
291 Thomassen = "Thomassen", 34, 52;
292 /// The Tutte graph: a counterexample to Tait's Hamiltonian conjecture (46, 69).
293 Tutte = "Tutte", 46, 69;
294 /// A triangle-free, uniquely 3-colorable graph (12, 22).
295 Uniquely3Colorable = "Uniquely3colorable", 12, 22;
296 /// The Walther graph: an identity graph (25, 31).
297 Walther = "Walther", 25, 31;
298 /// Zachary's karate club social network (34, 78).
299 Zachary = "Zachary", 34, 78;
300}
301
302impl AsRef<str> for FamousGraph {
303 fn as_ref(&self) -> &str {
304 self.name()
305 }
306}
307
308impl std::fmt::Display for FamousGraph {
309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310 f.write_str(self.name())
311 }
312}
313
314/// Checks that `x` is a valid *edge count* for the unweighted adjacency
315/// constructors: finite, non-negative and integral.
316///
317/// igraph converts matrix entries to integers with a plain C cast, which is
318/// undefined behaviour for NaN, infinities and out-of-range values, and
319/// silently truncates fractions; all of these are rejected here.
320fn check_edge_count(x: f64, row: usize, col: usize) -> Result<()> {
321 // 2^63: the first double that does not fit into an igraph_int_t.
322 const LIMIT: f64 = 9_223_372_036_854_775_808.0;
323 if x.is_finite() && (0.0..LIMIT).contains(&x) && x.fract() == 0.0 {
324 Ok(())
325 } else {
326 Err(Error::invalid(format!(
327 "adjacency matrix entry ({row}, {col}) = {x} is not a non-negative integer edge count"
328 )))
329 }
330}
331
332/// Validates every entry of a dense matrix with [`check_edge_count`].
333fn check_edge_counts(matrix: &Matrix) -> Result<()> {
334 let nrow = matrix.nrow();
335 // Column-major storage: element k is at (k % nrow, k / nrow).
336 for (k, &x) in matrix.as_slice().iter().enumerate() {
337 check_edge_count(x, k % nrow, k / nrow)?;
338 }
339 Ok(())
340}
341
342/// Whether the sparse routines of `mode` may combine `A[i][j]` with `A[j][i]`
343/// while only visiting the stored entries of the upper triangle.
344///
345/// In igraph 1.0.0 and 1.0.1 these routines (`igraph_sparse_adjacency` in
346/// the `MAX` mode, `igraph_sparse_weighted_adjacency` in the `MAX`, `MIN`
347/// and `PLUS` modes) skip every stored entry below the diagonal, so an entry
348/// `A[i][j]` (`i > j`) whose mirror `A[j][i]` is not stored is silently lost
349/// (the dense routines do not have this problem). Storing an explicit zero
350/// at the mirror position restores the documented semantics. (The unweighted
351/// `MIN` mode loses nothing, since `min(x, 0) = 0` there, and the unweighted
352/// `PLUS` mode visits every entry; padding is harmless for both.)
353fn needs_mirror_padding(mode: Adjacency) -> bool {
354 matches!(mode, Adjacency::Max | Adjacency::Min | Adjacency::Plus)
355}
356
357/// Builds the `n x n` column-compressed adjacency matrix for `mode` from
358/// triplets, storing exactly the nonzero entries of the matrix they
359/// describe: duplicated positions are summed on the Rust side, and positions
360/// whose sum is zero are left out. The latter matters for
361/// [`Adjacency::Undirected`], where igraph's symmetry test is *structural*
362/// (an explicitly stored zero without a stored mirror makes the matrix
363/// "non-symmetric"), while the dense routines only compare values.
364///
365/// When [`needs_mirror_padding`] holds, an explicit zero is then stored at
366/// `(j, i)` for every stored `(i, j)` whose mirror position is empty. With
367/// `counts`, every given value and every sum must be an edge count (see
368/// [`check_edge_count`]).
369///
370/// No position is stored twice, so the number of edges igraph's weighted
371/// routines emit never exceeds the number of nonzero entries they allocate
372/// room for.
373fn compressed_adjacency(
374 n: usize,
375 entries: &[(VertexId, VertexId, f64)],
376 mode: Adjacency,
377 counts: bool,
378) -> Result<SparseMat> {
379 let n_int = int(n, "the number of vertices")?;
380 for &(i, j, _) in entries {
381 if !(0..n_int).contains(&i) || !(0..n_int).contains(&j) {
382 return Err(Error::new(
383 crate::error::ErrorKind::InvalidVertexId,
384 format!("matrix entry ({i}, {j}) out of bounds for {n} vertices"),
385 ));
386 }
387 }
388 // In bounds (checked above), so all the `as usize` casts below are lossless.
389 if counts {
390 for &(i, j, x) in entries {
391 check_edge_count(x, i as usize, j as usize)?;
392 }
393 }
394 let mut summed: BTreeMap<(VertexId, VertexId), f64> = BTreeMap::new();
395 for &(i, j, x) in entries {
396 *summed.entry((i, j)).or_insert(0.0) += x;
397 }
398 // `0.0 == -0.0`; NaN sums are kept, as a dense matrix would keep them.
399 summed.retain(|_, x| *x != 0.0);
400 if counts {
401 for (&(i, j), &x) in &summed {
402 check_edge_count(x, i as usize, j as usize)?;
403 }
404 }
405 let padding: Vec<(VertexId, VertexId)> = if needs_mirror_padding(mode) {
406 summed
407 .keys()
408 .filter(|&&(i, j)| i != j && !summed.contains_key(&(j, i)))
409 .map(|&(i, j)| (j, i))
410 .collect()
411 } else {
412 Vec::new()
413 };
414 let stored = summed.len() + padding.len();
415 let mut triplet = SparseMat::with_capacity(n, n, stored.max(1))?;
416 for (&(i, j), &x) in &summed {
417 triplet.entry(i as usize, j as usize, x)?;
418 }
419 for &(i, j) in &padding {
420 triplet.entry(i as usize, j as usize, 0.0)?;
421 }
422 triplet.compress()
423}
424
425/// Deterministic generators, see the [module docs](self).
426impl igraph_t {
427 /// Creates a graph from an adjacency matrix.
428 ///
429 /// Row/column `i` of the square matrix becomes vertex `i`; entries are
430 /// *edge counts* (non-negative integers), interpreted according to
431 /// `mode` (`A[i][j]` is the element in row `i`, column `j`):
432 ///
433 /// - [`Adjacency::Directed`]: directed graph with `A[i][j]` edges `i -> j`;
434 /// - [`Adjacency::Undirected`]: undirected graph, the matrix must be symmetric;
435 /// - [`Adjacency::Max`] / [`Adjacency::Min`] / [`Adjacency::Plus`]:
436 /// `max`, `min` or sum of `A[i][j]` and `A[j][i]` undirected edges;
437 /// - [`Adjacency::Upper`] / [`Adjacency::Lower`]: only the upper / lower
438 /// triangle (diagonal included) is used.
439 ///
440 /// `loops` says how the diagonal is read: [`Loops::None`] ignores it,
441 /// [`Loops::Once`] reads it as the number of self-loops, [`Loops::Twice`]
442 /// as *twice* that number (the usual degree convention for undirected
443 /// graphs; odd values are an error). In the [`Adjacency::Directed`],
444 /// [`Adjacency::Upper`] and [`Adjacency::Lower`] modes `Twice` is treated
445 /// as `Once`: a directed loop adds one to both the in- and the
446 /// out-degree, and a triangle only holds the diagonal once. Edge
447 /// ordering is not specified.
448 ///
449 /// Binds [`igraph_adjacency`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_adjacency).
450 /// Time complexity: O(|V|²).
451 ///
452 /// See also [`Graph::get_adjacency`], the inverse conversion, and
453 /// [`sparse_adjacency`](Self::sparse_adjacency) for large sparse graphs.
454 ///
455 /// # Errors
456 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for a
457 /// non-square matrix, entries that are not non-negative integers
458 /// (negative, fractional, NaN or infinite; checked on the Rust side, since
459 /// igraph would truncate them with an unchecked cast), a non-symmetric
460 /// matrix with [`Adjacency::Undirected`], or an odd diagonal with
461 /// [`Loops::Twice`] in the modes that honour it.
462 ///
463 /// # Examples
464 /// ```
465 /// use igraph::prelude::*;
466 /// let a = Matrix::from_rows(&[[0.0, 1.0, 1.0], [1.0, 0.0, 0.0], [1.0, 0.0, 2.0]]).unwrap();
467 /// let g = Graph::adjacency(&a, Adjacency::Undirected, Loops::Twice).unwrap();
468 /// // Two edges plus one self-loop on vertex 2 (its diagonal entry counts it twice).
469 /// assert_eq!(g.ecount(), 3);
470 /// assert_eq!(g.degree_of(2, NeighborMode::All, Loops::Twice).unwrap(), 3);
471 /// // `get_adjacency` gives the matrix back (with the same loop convention).
472 /// assert_eq!(g.get_adjacency(GetAdjacency::Both, None, Loops::Twice).unwrap(), a);
473 /// ```
474 pub fn adjacency(matrix: &Matrix, mode: Adjacency, loops: Loops) -> Result<Graph> {
475 check_edge_counts(matrix)?;
476 Graph::init_with(|g| unsafe { igraph_adjacency(g, matrix, mode.into(), loops.into()) })
477 }
478
479 /// Creates a weighted graph from a weighted adjacency matrix, returning
480 /// the graph and its edge weights (indexed by edge id).
481 ///
482 /// Zero entries mean "no edge" (negative weights are allowed). The modes
483 /// are as in [`adjacency`](Self::adjacency), except that at most one edge
484 /// is created per vertex pair: [`Adjacency::Undirected`] requires a
485 /// symmetric matrix, and `Max`/`Min`/`Plus` combine the two weights
486 /// `A[i][j]` and `A[j][i]` into the weight of a single edge. For the
487 /// diagonal, [`Loops::None`] ignores it, [`Loops::Once`] takes the entry
488 /// as the loop weight and [`Loops::Twice`] as twice the loop weight
489 /// (halving it; `Twice` is treated as `Once` in the
490 /// [`Adjacency::Directed`], [`Adjacency::Upper`] and [`Adjacency::Lower`]
491 /// modes).
492 ///
493 /// Binds [`igraph_weighted_adjacency`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_weighted_adjacency).
494 /// Time complexity: O(|V|²).
495 ///
496 /// See also [`Graph::get_adjacency`] with `weights`, the inverse conversion.
497 ///
498 /// # Errors
499 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for a
500 /// non-square matrix, or a non-symmetric one with [`Adjacency::Undirected`].
501 ///
502 /// # Examples
503 /// ```
504 /// use igraph::prelude::*;
505 /// let a = Matrix::from_rows(&[[0.0, 2.5, 0.0], [0.0, 0.0, -1.0], [4.0, 0.0, 0.0]]).unwrap();
506 /// let (g, w) = Graph::weighted_adjacency(&a, Adjacency::Directed, Loops::None).unwrap();
507 /// let mut edges: Vec<_> = g.edge_list().into_iter().zip(w).collect();
508 /// edges.sort_by_key(|e| e.0);
509 /// assert_eq!(edges, vec![((0, 1), 2.5), ((1, 2), -1.0), ((2, 0), 4.0)]);
510 /// ```
511 pub fn weighted_adjacency(
512 matrix: &Matrix,
513 mode: Adjacency,
514 loops: Loops,
515 ) -> Result<(Graph, Vec<f64>)> {
516 let mut weights = Vector::new();
517 let g = Graph::init_with(|g| unsafe {
518 igraph_weighted_adjacency(g, matrix, mode.into(), &mut weights, loops.into())
519 })?;
520 Ok((g, weights.into()))
521 }
522
523 /// Creates a graph on `n` vertices from a *sparse* adjacency matrix given
524 /// as `(row, column, value)` triplets.
525 ///
526 /// This is the sparse counterpart of [`adjacency`](Self::adjacency), with
527 /// the same `mode` and `loops` semantics and the same result; entries not
528 /// listed are zero, and repeated `(row, column)` pairs are summed. The
529 /// triplets are summed up and compressed into igraph's column-compressed
530 /// sparse matrix before the call.
531 ///
532 /// igraph 1.0.0 and 1.0.1 lose the entries below the diagonal whose
533 /// mirror entry is absent in the [`Adjacency::Max`] mode (and in the
534 /// `Max`/`Min`/`Plus` modes of the weighted variant), and their
535 /// [`Adjacency::Undirected`] mode rejects an explicitly stored zero
536 /// without a stored mirror as non-symmetric. This wrapper stores only the
537 /// nonzero sums, plus explicit zeros at the missing mirror positions where
538 /// needed, so that the result always agrees with the dense
539 /// [`adjacency`](Self::adjacency).
540 ///
541 /// Binds [`igraph_sparse_adjacency`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_sparse_adjacency).
542 /// Time complexity: O(|E|), plus O(t log t) for summing up the `t`
543 /// triplets.
544 ///
545 /// See also [`Graph::get_adjacency_sparse`], whose
546 /// [`entries`](crate::conversion::CooMatrix::entries) are exactly the
547 /// triplets this function takes.
548 ///
549 /// # Errors
550 /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if a
551 /// triplet lies outside the `n × n` matrix;
552 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if a value
553 /// (or the sum of the values given for one position) is not a
554 /// non-negative integer; otherwise as
555 /// [`adjacency`](Self::adjacency).
556 ///
557 /// # Examples
558 /// ```
559 /// use igraph::prelude::*;
560 /// // A directed 1000-cycle, without ever allocating a dense 1000 x 1000 matrix.
561 /// let n = 1000;
562 /// let entries: Vec<_> = (0..n as i64).map(|i| (i, (i + 1) % n as i64, 1.0)).collect();
563 /// let g = Graph::sparse_adjacency(n, &entries, Adjacency::Directed, Loops::None).unwrap();
564 /// assert_eq!((g.vcount(), g.ecount()), (1000, 1000));
565 /// assert!(g.is_directed());
566 ///
567 /// // Round trip through the sparse adjacency matrix of the conversion module.
568 /// let karate = Graph::famous("Zachary").unwrap();
569 /// let coo = karate.get_adjacency_sparse(GetAdjacency::Upper, None, Loops::Twice).unwrap();
570 /// let back = Graph::sparse_adjacency(34, &coo.entries, Adjacency::Upper, Loops::Twice).unwrap();
571 /// assert!(back.isomorphic(&karate).unwrap());
572 /// assert_eq!(back.ecount(), 78);
573 /// ```
574 pub fn sparse_adjacency(
575 n: usize,
576 entries: &[(VertexId, VertexId, f64)],
577 mode: Adjacency,
578 loops: Loops,
579 ) -> Result<Graph> {
580 let mut sparse = compressed_adjacency(n, entries, mode, true)?;
581 Graph::init_with(|g| unsafe {
582 igraph_sparse_adjacency(g, &mut sparse, mode.into(), loops.into())
583 })
584 }
585
586 /// Creates a weighted graph on `n` vertices from a sparse weighted
587 /// adjacency matrix given as `(row, column, weight)` triplets, returning
588 /// the graph and its edge weights.
589 ///
590 /// The sparse counterpart of [`weighted_adjacency`](Self::weighted_adjacency);
591 /// repeated `(row, column)` pairs are summed, explicit zeros mean no edge,
592 /// and the result agrees with the dense version (see the note about
593 /// mirror entries in [`sparse_adjacency`](Self::sparse_adjacency)).
594 ///
595 /// Binds [`igraph_sparse_weighted_adjacency`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_sparse_weighted_adjacency).
596 /// Time complexity: O(|E|), plus O(t log t) for summing up the `t`
597 /// triplets.
598 ///
599 /// # Errors
600 /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if a
601 /// triplet lies outside the `n × n` matrix; otherwise as
602 /// [`weighted_adjacency`](Self::weighted_adjacency) (any real weight is
603 /// accepted).
604 ///
605 /// # Examples
606 /// ```
607 /// use igraph::prelude::*;
608 /// let entries = [(0, 1, 0.5), (1, 0, 0.5), (1, 2, 3.0), (2, 1, 3.0)];
609 /// let (g, w) =
610 /// Graph::sparse_weighted_adjacency(3, &entries, Adjacency::Undirected, Loops::None)
611 /// .unwrap();
612 /// assert_eq!(g.ecount(), 2);
613 /// assert_eq!(w.iter().sum::<f64>(), 3.5);
614 /// ```
615 pub fn sparse_weighted_adjacency(
616 n: usize,
617 entries: &[(VertexId, VertexId, f64)],
618 mode: Adjacency,
619 loops: Loops,
620 ) -> Result<(Graph, Vec<f64>)> {
621 let mut sparse = compressed_adjacency(n, entries, mode, false)?;
622 let mut weights = Vector::new();
623 let g = Graph::init_with(|g| unsafe {
624 igraph_sparse_weighted_adjacency(
625 g,
626 &mut sparse,
627 mode.into(),
628 &mut weights,
629 loops.into(),
630 )
631 })?;
632 Ok((g, weights.into()))
633 }
634
635 /// Shorthand to create a small graph from a flat edge list
636 /// `[from0, to0, from1, to1, ...]`, in the spirit of `igraph_small`.
637 ///
638 /// The C function `igraph_small` is variadic and terminated by `-1`; in
639 /// Rust a slice literal does the same job safely. The graph has
640 /// `max(n, largest id + 1)` vertices. This is the same as
641 /// [`Graph::from_flat_edges`], provided for familiarity with the C API;
642 /// [`Graph::from_edges`] takes `(from, to)` pairs instead.
643 ///
644 /// Binds [`igraph_create`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_create)
645 /// (the non-variadic equivalent of
646 /// [`igraph_small`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_small)).
647 ///
648 /// # Errors
649 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for an odd
650 /// number of ids, [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId)
651 /// for negative ids.
652 ///
653 /// # Examples
654 /// ```
655 /// use igraph::prelude::*;
656 /// let bowtie = Graph::small(5, false, &[0, 1, 1, 2, 2, 0, 2, 3, 3, 4, 4, 2]).unwrap();
657 /// assert_eq!(bowtie.degree_of(2, NeighborMode::All, Loops::Twice).unwrap(), 4);
658 /// ```
659 pub fn small(n: usize, directed: bool, edges: &[VertexId]) -> Result<Graph> {
660 Graph::from_flat_edges(edges, n, directed)
661 }
662
663 /// Creates a star graph: vertex `center` connected to all the other
664 /// `n - 1` vertices.
665 ///
666 /// `mode` chooses between an undirected star ([`StarMode::Undirected`]),
667 /// edges pointing out of ([`StarMode::Out`]) or into ([`StarMode::In`])
668 /// the center, or mutual directed edges ([`StarMode::Mutual`]).
669 ///
670 /// Binds [`igraph_star`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_star).
671 /// Time complexity: O(|V|).
672 ///
673 /// See also [`wheel`](Self::wheel), and [`Graph::layout_star`] to draw it.
674 ///
675 /// The star on a single vertex is that vertex alone (igraph 1.0.0 and 1.0.1
676 /// return the null graph there; this wrapper adds the missing vertex).
677 ///
678 /// # Errors
679 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `center`
680 /// is not a vertex of the graph, in particular always for `n = 0`.
681 ///
682 /// # Examples
683 /// ```
684 /// use igraph::prelude::*;
685 /// let s = Graph::star(5, StarMode::In, 2).unwrap();
686 /// assert_eq!(s.degree_of(2, NeighborMode::In, Loops::Twice).unwrap(), 4);
687 /// assert_eq!(s.degree_of(2, NeighborMode::Out, Loops::Twice).unwrap(), 0);
688 /// ```
689 pub fn star(n: usize, mode: StarMode, center: VertexId) -> Result<Graph> {
690 let n = int(n, "the number of vertices")?;
691 let g = Graph::init_with(|g| unsafe { igraph_star(g, n, mode.into(), center) })?;
692 fix_single_vertex(g, n)
693 }
694
695 /// Creates a wheel graph: a star (the spokes) plus a cycle through the
696 /// `n - 1` non-center vertices (the rim).
697 ///
698 /// `mode` orients the spokes like in [`star`](Self::star); in the directed
699 /// modes the rim is a directed cycle (mutual for [`WheelMode::Mutual`]).
700 /// Note that the wheels on 2 and 3 vertices are not simple (they contain a
701 /// self-loop and a multi-edge, respectively).
702 ///
703 /// Binds [`igraph_wheel`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_wheel).
704 /// Time complexity: O(|V|).
705 ///
706 /// The wheel on one vertex is the singleton graph.
707 ///
708 /// # Errors
709 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `center`
710 /// is not a vertex of the graph (always for `n = 0`).
711 ///
712 /// # Examples
713 /// ```
714 /// use igraph::prelude::*;
715 /// let w = Graph::wheel(6, WheelMode::Undirected, 0).unwrap();
716 /// assert_eq!(w.ecount(), 10); // 5 spokes + 5 rim edges
717 /// ```
718 pub fn wheel(n: usize, mode: WheelMode, center: VertexId) -> Result<Graph> {
719 let n = int(n, "the number of vertices")?;
720 let g = Graph::init_with(|g| unsafe { igraph_wheel(g, n, mode.into(), center) })?;
721 fix_single_vertex(g, n)
722 }
723
724 /// The `dim`-dimensional hypercube graph `Q_dim`.
725 ///
726 /// It has `2^dim` vertices and `dim * 2^(dim-1)` edges; two vertices are
727 /// adjacent when the binary representations of their ids differ in
728 /// exactly one bit. Directed edges point from lower to higher ids.
729 ///
730 /// Binds [`igraph_hypercube`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_hypercube).
731 /// Time complexity: O(2^dim).
732 ///
733 /// See also [`square_lattice`](Self::square_lattice): `Q_dim` is the
734 /// `2 x 2 x ... x 2` lattice.
735 ///
736 /// # Errors
737 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
738 /// `dim > 57`, so that the edge count would not fit into half the range
739 /// of `igraph_int_t` (smaller but still huge dimensions fail with an
740 /// out-of-memory error).
741 ///
742 /// # Examples
743 /// ```
744 /// use igraph::prelude::*;
745 /// let q3 = Graph::hypercube(3, false).unwrap();
746 /// assert_eq!((q3.vcount(), q3.ecount()), (8, 12));
747 /// assert_eq!(q3.neighbors(0, NeighborMode::All).unwrap(), vec![1, 2, 4]);
748 /// assert!(q3.isomorphic(&Graph::famous("Cubical").unwrap()).unwrap());
749 /// ```
750 pub fn hypercube(dim: usize, directed: bool) -> Result<Graph> {
751 let dim = int(dim, "the dimension")?;
752 Graph::init_with(|g| unsafe { igraph_hypercube(g, dim, directed) })
753 }
754
755 /// Creates an arbitrary-dimensional square lattice (grid).
756 ///
757 /// `dims` gives the size along each dimension (an empty slice gives the
758 /// singleton graph). The vertex at position `(i_1, i_2, ..., i_d)` gets id
759 /// `i_1 + n_1 * i_2 + n_1 * n_2 * i_3 + ...`. Vertices within `nei` steps
760 /// of each other are connected (`nei = 1` is the usual grid).
761 /// `periodic`, when given, must have one flag per dimension, making the
762 /// lattice wrap around (a torus) along the flagged dimensions.
763 /// When `directed`, edges point from lower to higher ids unless `mutual`
764 /// (or periodicity) is set.
765 ///
766 /// Binds [`igraph_square_lattice`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_square_lattice).
767 /// Time complexity: O(|V| + |E|) for `nei < 2`.
768 ///
769 /// See also [`Graph::layout_grid`], which places a 2D lattice on its grid
770 /// (pass `Some(dims[0])` as the width), and
771 /// [`triangular_lattice`](Self::triangular_lattice) /
772 /// [`hexagonal_lattice`](Self::hexagonal_lattice).
773 ///
774 /// # Errors
775 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
776 /// `periodic` and `dims` have different lengths.
777 ///
778 /// # Examples
779 /// ```
780 /// use igraph::prelude::*;
781 /// // A 4x4 torus is 4-regular.
782 /// let torus = Graph::square_lattice(&[4, 4], 1, false, false, Some(&[true, true])).unwrap();
783 /// assert_eq!(torus.ecount(), 32);
784 /// let deg = torus.degree(VertexSelector::All, NeighborMode::All, Loops::Twice).unwrap();
785 /// assert!(deg.iter().all(|&d| d == 4));
786 /// // Vertex (x, y) of a 5 x 3 grid has id x + 5 y, which `layout_grid` puts at (x, y).
787 /// let grid = Graph::square_lattice(&[5, 3], 1, false, false, None).unwrap();
788 /// let layout = grid.layout_grid(Some(5)).unwrap();
789 /// assert_eq!((layout[(7, 0)], layout[(7, 1)]), (2.0, 1.0));
790 /// ```
791 pub fn square_lattice(
792 dims: &[usize],
793 nei: usize,
794 directed: bool,
795 mutual: bool,
796 periodic: Option<&[bool]>,
797 ) -> Result<Graph> {
798 if let Some(p) = periodic
799 && p.len() != dims.len()
800 {
801 return Err(Error::invalid(format!(
802 "periodic has {} flags but the lattice has {} dimensions",
803 p.len(),
804 dims.len()
805 )));
806 }
807 let dims = int_vector(dims, "a lattice dimension")?;
808 let nei = int(nei, "the neighborhood order")?;
809 let periodic = periodic.map(VectorBool::view);
810 let periodic_ptr = periodic.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
811 Graph::init_with(|g| unsafe {
812 igraph_square_lattice(g, &dims, nei, directed, mutual, periodic_ptr)
813 })
814 }
815
816 /// Creates a cycle graph `C_n` (`circular = true`) or a path graph `P_n`
817 /// (`circular = false`).
818 ///
819 /// In directed graphs all edges follow the same orientation along the
820 /// ring, or are mutual when `mutual` is set (ignored when undirected).
821 /// For `n` = 1 or 2 the cycle is not simple (a self-loop, or two parallel
822 /// edges).
823 ///
824 /// Binds [`igraph_ring`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_ring).
825 /// Time complexity: O(|V|).
826 ///
827 /// See also [`circulant`](Self::circulant) for rings with chords, and
828 /// [`Graph::layout_circle`] to draw it.
829 ///
830 /// # Examples
831 /// ```
832 /// use igraph::prelude::*;
833 /// let ring = Graph::ring(5, true, false, true).unwrap();
834 /// assert_eq!(ring.edge_list(), vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]);
835 /// ```
836 pub fn ring(n: usize, directed: bool, mutual: bool, circular: bool) -> Result<Graph> {
837 let n = int(n, "the number of vertices")?;
838 Graph::init_with(|g| unsafe { igraph_ring(g, n, directed, mutual, circular) })
839 }
840
841 /// The path graph `P_n` on `n` vertices: `0 - 1 - ... - (n-1)`.
842 ///
843 /// A convenience form of [`ring`](Self::ring) with `circular = false`;
844 /// `mutual` adds both directions in directed graphs.
845 ///
846 /// Binds [`igraph_path_graph`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_path_graph).
847 /// Time complexity: O(|V|).
848 ///
849 /// # Examples
850 /// ```
851 /// use igraph::prelude::*;
852 /// let p = Graph::path_graph(4, true, true).unwrap();
853 /// assert_eq!(p.ecount(), 6); // 3 links, both directions
854 /// ```
855 pub fn path_graph(n: usize, directed: bool, mutual: bool) -> Result<Graph> {
856 let n = int(n, "the number of vertices")?;
857 Graph::init_with(|g| unsafe { igraph_path_graph(g, n, directed, mutual) })
858 }
859
860 /// The cycle graph `C_n` on `n` vertices.
861 ///
862 /// A convenience form of [`ring`](Self::ring) with `circular = true`. For
863 /// `n` = 1 or 2 the result has a self-loop or parallel edges.
864 ///
865 /// Binds [`igraph_cycle_graph`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_cycle_graph).
866 /// Time complexity: O(|V|).
867 ///
868 /// # Examples
869 /// ```
870 /// use igraph::prelude::*;
871 /// let c = Graph::cycle_graph(7, false, false).unwrap();
872 /// assert_eq!((c.vcount(), c.ecount()), (7, 7));
873 /// ```
874 pub fn cycle_graph(n: usize, directed: bool, mutual: bool) -> Result<Graph> {
875 let n = int(n, "the number of vertices")?;
876 Graph::init_with(|g| unsafe { igraph_cycle_graph(g, n, directed, mutual) })
877 }
878
879 /// Creates a `children`-ary tree on `n` vertices, filled level by level
880 /// in breadth-first order (vertex `i`'s children are
881 /// `children*i + 1 ..= children*i + children`).
882 ///
883 /// For a complete tree with `l` levels below the root use
884 /// `n = (children^(l+1) - 1) / (children - 1)`. `mode` gives the edge
885 /// orientation (parent → child for [`TreeMode::Out`]). `n = 0` gives the
886 /// null graph.
887 ///
888 /// Binds [`igraph_kary_tree`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_kary_tree).
889 /// Time complexity: O(|V| + |E|).
890 ///
891 /// See also [`Graph::tree_game`] for uniformly random trees and
892 /// [`Graph::layout_reingold_tilford`] to draw trees.
893 ///
894 /// # Errors
895 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
896 /// `children` is zero.
897 ///
898 /// # Examples
899 /// ```
900 /// use igraph::prelude::*;
901 /// let t = Graph::kary_tree(7, 2, TreeMode::Out).unwrap(); // a complete binary tree
902 /// assert_eq!(t.neighbors(1, NeighborMode::Out).unwrap(), vec![3, 4]);
903 /// assert!(t.is_tree(NeighborMode::Out).unwrap());
904 /// ```
905 pub fn kary_tree(n: usize, children: usize, mode: TreeMode) -> Result<Graph> {
906 let n = int(n, "the number of vertices")?;
907 let children = int(children, "the number of children")?;
908 Graph::init_with(|g| unsafe { igraph_kary_tree(g, n, children, mode.into()) })
909 }
910
911 /// Creates a symmetric tree where every vertex at distance `d` from the
912 /// root has `branches[d]` children.
913 ///
914 /// The tree has `1 + b_0 + b_0 b_1 + ...` vertices, numbered in
915 /// breadth-first order from the root `0`.
916 ///
917 /// Binds [`igraph_symmetric_tree`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_symmetric_tree).
918 /// Time complexity: O(|V| + |E|).
919 ///
920 /// An empty `branches` gives the singleton graph.
921 ///
922 /// # Errors
923 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if a
924 /// branch count is zero.
925 ///
926 /// # Examples
927 /// ```
928 /// use igraph::prelude::*;
929 /// let t = Graph::symmetric_tree(&[3, 2], TreeMode::Undirected).unwrap();
930 /// assert_eq!(t.vcount(), 1 + 3 + 3 * 2);
931 /// ```
932 pub fn symmetric_tree(branches: &[usize], mode: TreeMode) -> Result<Graph> {
933 let branches = int_vector(branches, "a branching count")?;
934 Graph::init_with(|g| unsafe { igraph_symmetric_tree(g, &branches, mode.into()) })
935 }
936
937 /// Creates a regular tree (Bethe lattice) of height `h` in which every
938 /// non-leaf vertex has total degree `k`.
939 ///
940 /// Unlike a [`kary_tree`](Self::kary_tree), the root has `k` children and
941 /// the other internal vertices `k - 1`, so that all internal degrees are
942 /// equal. `h` is the distance between the root and the leaves.
943 ///
944 /// Binds [`igraph_regular_tree`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_regular_tree).
945 /// Time complexity: O(|V| + |E|).
946 ///
947 /// # Errors
948 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) unless
949 /// `h >= 1` and `k >= 2`.
950 ///
951 /// # Examples
952 /// ```
953 /// use igraph::prelude::*;
954 /// let t = Graph::regular_tree(2, 3, TreeMode::Undirected).unwrap();
955 /// assert_eq!(t.vcount(), 1 + 3 + 3 * 2);
956 /// ```
957 pub fn regular_tree(h: usize, k: usize, mode: TreeMode) -> Result<Graph> {
958 let h = int(h, "the height")?;
959 let k = int(k, "the degree")?;
960 Graph::init_with(|g| unsafe { igraph_regular_tree(g, h, k, mode.into()) })
961 }
962
963 /// Builds a tree or forest from a parent vector: `parents[v]` is the
964 /// parent of vertex `v`, or a negative value if `v` is a root.
965 ///
966 /// Such vectors are produced by BFS/DFS traversals, shortest path trees,
967 /// dominator trees, etc. The graph has `parents.len()` vertices; with
968 /// [`TreeMode::Out`] edges point from parents to children, with
969 /// [`TreeMode::In`] from children to parents.
970 ///
971 /// Binds [`igraph_tree_from_parent_vector`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_tree_from_parent_vector).
972 /// Time complexity: O(n).
973 ///
974 /// See also [`Graph::bfs_simple`] and the other traversals of
975 /// [`crate::visitor`], whose `parents` give such vectors (map `None` to `-1`).
976 ///
977 /// # Errors
978 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
979 /// vector encodes a cycle or a self-loop,
980 /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) for
981 /// out-of-range parents.
982 ///
983 /// # Examples
984 /// ```
985 /// use igraph::prelude::*;
986 /// // Two trees: 0 <- {1, 2}, 2 <- 3, and the lone root 4.
987 /// let f = Graph::tree_from_parent_vector(&[-1, 0, 0, 2, -1], TreeMode::Out).unwrap();
988 /// assert_eq!(f.ecount(), 3);
989 /// assert_eq!(f.neighbors(0, NeighborMode::Out).unwrap(), vec![1, 2]);
990 ///
991 /// // The BFS tree of the Petersen graph: 1 root, 3 children, 6 grandchildren.
992 /// let petersen = Graph::famous("Petersen").unwrap();
993 /// let bfs = petersen.bfs_simple(0, NeighborMode::All).unwrap();
994 /// let parents: Vec<i64> = bfs.parents.iter().map(|p| p.unwrap_or(-1)).collect();
995 /// let tree = Graph::tree_from_parent_vector(&parents, TreeMode::Out).unwrap();
996 /// assert!(tree.is_tree(NeighborMode::Out).unwrap());
997 /// assert_eq!(tree.ecount(), 9);
998 /// ```
999 pub fn tree_from_parent_vector(parents: &[VertexId], mode: TreeMode) -> Result<Graph> {
1000 let parents = VectorInt::view(parents);
1001 Graph::init_with(|g| unsafe {
1002 igraph_tree_from_parent_vector(g, parents.as_ptr(), mode.into())
1003 })
1004 }
1005
1006 /// Builds the labelled tree encoded by a Prüfer sequence.
1007 ///
1008 /// A sequence of length `n - 2` with entries in `0..n` encodes a unique
1009 /// tree on `n` vertices (Cayley's formula counts `n^(n-2)` of them); a
1010 /// vertex appears in the sequence exactly `degree - 1` times.
1011 ///
1012 /// Binds [`igraph_from_prufer`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_from_prufer).
1013 /// Time complexity: O(|V|).
1014 ///
1015 /// See also [`Graph::to_prufer`], the inverse conversion.
1016 ///
1017 /// # Errors
1018 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for an
1019 /// invalid sequence (entries out of range).
1020 ///
1021 /// # Examples
1022 /// ```
1023 /// use igraph::prelude::*;
1024 /// let t = Graph::from_prufer(&[3, 3, 3]).unwrap(); // the star K_{1,4} centered at 3
1025 /// assert_eq!(t.degree_of(3, NeighborMode::All, Loops::Twice).unwrap(), 4);
1026 /// assert_eq!(t.to_prufer().unwrap(), vec![3, 3, 3]);
1027 /// ```
1028 pub fn from_prufer(prufer: &[VertexId]) -> Result<Graph> {
1029 let prufer = VectorInt::view(prufer);
1030 Graph::init_with(|g| unsafe { igraph_from_prufer(g, prufer.as_ptr()) })
1031 }
1032
1033 /// Creates the complete graph on `n` vertices.
1034 ///
1035 /// Directed complete graphs have both `i -> j` and `j -> i`; with `loops`
1036 /// every vertex also gets a single self-loop.
1037 ///
1038 /// Binds [`igraph_full`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_full).
1039 /// Time complexity: O(|V|²).
1040 ///
1041 /// See also [`Graph::is_complete`], [`Graph::complementer`] (the
1042 /// complement of `K_n` is the empty graph) and [`Graph::full_bipartite`].
1043 ///
1044 /// # Examples
1045 /// ```
1046 /// use igraph::prelude::*;
1047 /// assert_eq!(Graph::full(5, false, false).unwrap().ecount(), 10);
1048 /// assert_eq!(Graph::full(5, true, false).unwrap().ecount(), 20);
1049 /// assert_eq!(Graph::full(5, false, true).unwrap().ecount(), 15);
1050 /// ```
1051 pub fn full(n: usize, directed: bool, loops: bool) -> Result<Graph> {
1052 let n = int(n, "the number of vertices")?;
1053 Graph::init_with(|g| unsafe { igraph_full(g, n, directed, loops) })
1054 }
1055
1056 /// Creates a complete multipartite graph with partitions of the given
1057 /// `sizes`, returning the graph and the partition index of each vertex.
1058 ///
1059 /// Vertices are numbered partition by partition; every pair of vertices
1060 /// in different partitions is connected. In directed graphs, `mode`
1061 /// [`NeighborMode::Out`] points edges from lower to higher partitions,
1062 /// [`NeighborMode::In`] the opposite, and [`NeighborMode::All`] creates
1063 /// mutual edges.
1064 ///
1065 /// Binds [`igraph_full_multipartite`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_full_multipartite).
1066 /// Time complexity: O(|V| + |E|).
1067 ///
1068 /// See also [`Graph::full_bipartite`] for two partitions with boolean
1069 /// vertex types, and [`turan`](Self::turan).
1070 ///
1071 /// # Examples
1072 /// ```
1073 /// use igraph::prelude::*;
1074 /// let (k233, types) =
1075 /// Graph::full_multipartite(&[2, 3, 3], false, NeighborMode::All).unwrap();
1076 /// assert_eq!(k233.ecount(), 2 * 3 + 2 * 3 + 3 * 3);
1077 /// assert_eq!(types, vec![0, 0, 1, 1, 1, 2, 2, 2]);
1078 /// ```
1079 pub fn full_multipartite(
1080 sizes: &[usize],
1081 directed: bool,
1082 mode: NeighborMode,
1083 ) -> Result<(Graph, Vec<i64>)> {
1084 let sizes = int_vector(sizes, "a partition size")?;
1085 let mut types = VectorInt::new();
1086 let g = Graph::init_with(|g| unsafe {
1087 igraph_full_multipartite(g, &mut types, &sizes, directed, mode.into())
1088 })?;
1089 Ok((g, types.into()))
1090 }
1091
1092 /// Creates the Turán graph `T(n, r)`, returning the graph and the
1093 /// partition index of each vertex.
1094 ///
1095 /// It is the complete `r`-partite graph on `n` vertices with partition
1096 /// sizes as equal as possible: by Turán's theorem, the densest graph on
1097 /// `n` vertices without a clique of size `r + 1`. It is undirected; `n = 0`
1098 /// gives the null graph, and `r > n` gives the complete graph.
1099 ///
1100 /// Binds [`igraph_turan`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_turan).
1101 /// Time complexity: O(|V| + |E|).
1102 ///
1103 /// # Errors
1104 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `r` is zero.
1105 ///
1106 /// # Examples
1107 /// ```
1108 /// use igraph::prelude::*;
1109 /// let (t, types) = Graph::turan(6, 3).unwrap(); // the octahedron K_{2,2,2}
1110 /// assert_eq!(t.ecount(), 12);
1111 /// assert_eq!(types, vec![0, 0, 1, 1, 2, 2]);
1112 /// // Turán's theorem: no clique on r + 1 = 4 vertices.
1113 /// assert_eq!(t.clique_number().unwrap(), 3);
1114 /// ```
1115 pub fn turan(n: usize, r: usize) -> Result<(Graph, Vec<i64>)> {
1116 let n = int(n, "the number of vertices")?;
1117 let r = int(r, "the number of partitions")?;
1118 let mut types = VectorInt::new();
1119 let g = Graph::init_with(|g| unsafe { igraph_turan(g, &mut types, n, r) })?;
1120 Ok((g, types.into()))
1121 }
1122
1123 /// Creates a full citation graph: the complete directed acyclic graph in
1124 /// which `i -> j` is an edge exactly when `j < i`.
1125 ///
1126 /// With `directed = false` it is just the complete graph.
1127 ///
1128 /// Binds [`igraph_full_citation`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_full_citation).
1129 /// Time complexity: O(|V|²).
1130 ///
1131 /// # Examples
1132 /// ```
1133 /// use igraph::prelude::*;
1134 /// let g = Graph::full_citation(4, true).unwrap();
1135 /// assert_eq!(g.neighbors(3, NeighborMode::Out).unwrap(), vec![0, 1, 2]);
1136 /// assert!(g.neighbors(0, NeighborMode::Out).unwrap().is_empty());
1137 /// ```
1138 pub fn full_citation(n: usize, directed: bool) -> Result<Graph> {
1139 let n = int(n, "the number of vertices")?;
1140 Graph::init_with(|g| unsafe { igraph_full_citation(g, n, directed) })
1141 }
1142
1143 /// Creates graph number `number` of *An Atlas of Graphs* (Read and
1144 /// Wilson, 1998).
1145 ///
1146 /// The atlas holds all 1253 simple undirected unlabelled graphs on 0 to 7
1147 /// vertices, ordered by number of vertices, then number of edges, then
1148 /// degree sequence (lexicographically, e.g. 111223 < 112222), then
1149 /// increasing number of automorphisms. Graphs on 0, 1, ..., 7 vertices
1150 /// start at numbers 0, 1, 2, 4, 8, 19, 53 and 209.
1151 ///
1152 /// Binds [`igraph_atlas`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_atlas).
1153 /// Time complexity: O(|V| + |E|).
1154 ///
1155 /// See also [`Graph::isomorphic`] and [`Graph::canonical_permutation`] to
1156 /// locate a given small graph in the atlas.
1157 ///
1158 /// # Errors
1159 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
1160 /// `number > 1252`.
1161 ///
1162 /// # Examples
1163 /// ```
1164 /// use igraph::prelude::*;
1165 /// let k7 = Graph::atlas(1252).unwrap(); // the last one is K_7
1166 /// assert_eq!((k7.vcount(), k7.ecount()), (7, 21));
1167 /// ```
1168 pub fn atlas(number: usize) -> Result<Graph> {
1169 let number = int(number, "the atlas number")?;
1170 Graph::init_with(|g| unsafe { igraph_atlas(g, number) })
1171 }
1172
1173 /// Creates an extended chordal ring: a cycle on `nodes` vertices plus
1174 /// chords described by the rows of `w`.
1175 ///
1176 /// For each row `L` of length `p` (all rows have the same length, which
1177 /// must divide `nodes`), vertex `i` is connected to vertex
1178 /// `(i + L[i mod p]) mod nodes`. Entries may be negative. The result is
1179 /// not simplified: duplicate chords (and chords along the cycle) produce
1180 /// multi-edges, and offsets that are multiples of `nodes` self-loops.
1181 /// Note that igraph's definition differs from the one in Kotsis (1993).
1182 ///
1183 /// Binds [`igraph_extended_chordal_ring`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_extended_chordal_ring).
1184 /// Time complexity: O(|V| + |E|).
1185 ///
1186 /// # Errors
1187 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
1188 /// `nodes < 3`, rows have different lengths, or the row length does not
1189 /// divide `nodes`.
1190 ///
1191 /// # Examples
1192 /// ```
1193 /// use igraph::prelude::*;
1194 /// // One row [3]: each vertex also links 3 steps ahead; on 6 vertices the
1195 /// // three "diameters" appear twice, giving the utility graph K_{3,3} plus duplicates.
1196 /// let g = Graph::extended_chordal_ring(6, &[[3]], false).unwrap();
1197 /// assert_eq!(g.ecount(), 6 + 6);
1198 /// ```
1199 pub fn extended_chordal_ring<R: AsRef<[i64]>>(
1200 nodes: usize,
1201 w: &[R],
1202 directed: bool,
1203 ) -> Result<Graph> {
1204 let nodes = int(nodes, "the number of vertices")?;
1205 if nodes < 3 {
1206 return Err(Error::invalid(format!(
1207 "an extended chordal ring has at least 3 vertices, got {nodes}"
1208 )));
1209 }
1210 // igraph divides by the number of columns: an empty chord matrix
1211 // (no rows, or only empty rows) is passed as a 0 x 1 matrix, meaning
1212 // "no chords". Offsets are reduced modulo `nodes`, which keeps their
1213 // meaning and avoids the unchecked `i + offset` overflow in C.
1214 let w = if w.iter().all(|row| row.as_ref().is_empty()) {
1215 MatrixInt::zeros(0, 1)
1216 } else {
1217 let rows: Vec<Vec<i64>> = w
1218 .iter()
1219 .map(|row| row.as_ref().iter().map(|&x| x.rem_euclid(nodes)).collect())
1220 .collect();
1221 MatrixInt::from_rows(&rows)?
1222 };
1223 Graph::init_with(|g| unsafe { igraph_extended_chordal_ring(g, nodes, &w, directed) })
1224 }
1225
1226 /// The line graph `L(G)` of this graph: one vertex per edge (edge `i`
1227 /// becomes vertex `i`).
1228 ///
1229 /// For undirected graphs, two vertices of `L(G)` are adjacent when the
1230 /// corresponding edges share an endpoint (twice, if they share both
1231 /// endpoints in a multigraph; the single vertex of a self-loop counts as
1232 /// two endpoints, so a self-loop and an edge incident to it are joined
1233 /// twice). For directed graphs, `e -> f` is an edge when the target of
1234 /// `e` is the source of `f`. Self-loops are self-adjacent and get a single
1235 /// self-loop in `L(G)`, in both cases.
1236 ///
1237 /// Binds [`igraph_linegraph`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_linegraph).
1238 /// Time complexity: O(|V| + |E|).
1239 ///
1240 /// See also the other graph transformations of [`crate::operators`].
1241 ///
1242 /// # Examples
1243 /// ```
1244 /// use igraph::prelude::*;
1245 /// // The line graph of the star K_{1,4} is K_4.
1246 /// let l = Graph::star(5, StarMode::Undirected, 0).unwrap().linegraph().unwrap();
1247 /// assert_eq!((l.vcount(), l.ecount()), (4, 6));
1248 /// ```
1249 pub fn linegraph(&self) -> Result<Graph> {
1250 Graph::init_with(|l| unsafe { igraph_linegraph(self, l) })
1251 }
1252
1253 /// The de Bruijn graph `B(m, n)`: vertices are the `m^n` strings of
1254 /// length `n` over an alphabet of `m` letters, with an edge `v -> w` when
1255 /// `w` is obtained by dropping the first letter of `v` and appending one.
1256 ///
1257 /// Vertex ids are the strings read as base-`m` numbers. Every vertex has
1258 /// in- and out-degree `m` (loops included), so the graph has `m^(n+1)`
1259 /// edges and is Eulerian; its Eulerian circuits spell de Bruijn sequences.
1260 ///
1261 /// `B(m, 0)` is the singleton graph and `B(0, n)` for `n > 0` the null
1262 /// graph.
1263 ///
1264 /// Binds [`igraph_de_bruijn`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_de_bruijn).
1265 /// Time complexity: O(|V| + |E|).
1266 ///
1267 /// See also [`Graph::eulerian_cycle`] to spell a de Bruijn sequence (as
1268 /// in the example below) and [`kautz`](Self::kautz).
1269 ///
1270 /// # Errors
1271 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
1272 /// number of vertices `m^n` does not fit in an `i64`, and
1273 /// [`ErrorKind::Overflow`](crate::ErrorKind::Overflow) if the `m^(n+1)`
1274 /// edges do not fit in igraph's edge vector (both checked before calling
1275 /// igraph).
1276 ///
1277 /// # Examples
1278 /// ```
1279 /// use igraph::prelude::*;
1280 /// let b = Graph::de_bruijn(2, 3).unwrap();
1281 /// assert_eq!((b.vcount(), b.ecount()), (8, 16));
1282 /// // "011" -> "110" and "111"
1283 /// assert_eq!(b.neighbors(0b011, NeighborMode::Out).unwrap(), vec![0b110, 0b111]);
1284 ///
1285 /// // An Eulerian circuit of B(2, 3) spells a de Bruijn sequence of order 4:
1286 /// // each vertex appends its last letter, and all 16 4-bit words occur
1287 /// // exactly once as cyclic substrings.
1288 /// let walk = b.eulerian_cycle().unwrap();
1289 /// let seq: Vec<i64> = walk.vertices[1..].iter().map(|v| v % 2).collect();
1290 /// assert_eq!(seq.len(), 16);
1291 /// let words: std::collections::BTreeSet<i64> = (0..16)
1292 /// .map(|i| (0..4).fold(0, |acc, j| 2 * acc + seq[(i + j) % 16]))
1293 /// .collect();
1294 /// assert_eq!(words.len(), 16);
1295 /// ```
1296 pub fn de_bruijn(m: usize, n: usize) -> Result<Graph> {
1297 let m = int(m, "the alphabet size")?;
1298 let n = int(n, "the string length")?;
1299 if n > 0 && m > 0 {
1300 let nodes = checked_ipow(m, n).ok_or_else(|| {
1301 Error::invalid(format!(
1302 "Parameters ({m}, {n}) too large for De Bruijn graph."
1303 ))
1304 })?;
1305 // igraph then needs `2 * m * m^n` edge endpoints (it reports
1306 // `IGRAPH_EOVERFLOW` otherwise). Checking it here also keeps
1307 // `m^n` far below 2^63: for `m` close to `i64::MAX` and `n = 1`,
1308 // `pow(m, 1)` rounds up to 2^63 as a double, and igraph's
1309 // conversion back to an integer would be undefined behaviour.
1310 nodes
1311 .checked_mul(m)
1312 .and_then(|e| e.checked_mul(2))
1313 .ok_or_else(|| {
1314 Error::new(
1315 crate::error::ErrorKind::Overflow,
1316 format!("Parameters ({m}, {n}) too large for De Bruijn graph."),
1317 )
1318 })?;
1319 }
1320 Graph::init_with(|g| unsafe { igraph_de_bruijn(g, m, n) })
1321 }
1322
1323 /// The Kautz graph `K(m, n)`: vertices are the strings of length `n + 1`
1324 /// over an alphabet of `m + 1` letters with no two equal consecutive
1325 /// letters; `v -> w` when `w` is `v` shifted by one letter.
1326 ///
1327 /// It has `(m+1) m^n` vertices, each with in- and out-degree `m`, and no
1328 /// self-loops. Degenerate cases: `K(m, 0)` is the complete directed graph
1329 /// on `m + 1` vertices, and `K(0, n)` for `n > 0` is the null graph.
1330 ///
1331 /// Binds [`igraph_kautz`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_kautz).
1332 /// Time complexity: roughly O(|V| + |E|).
1333 ///
1334 /// # Errors
1335 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
1336 /// graph would be too large (`m^n` or `(m+1)^(n+1)` does not fit in an
1337 /// `i64`; checked before calling igraph).
1338 ///
1339 /// # Examples
1340 /// ```
1341 /// use igraph::prelude::*;
1342 /// let k = Graph::kautz(2, 1).unwrap();
1343 /// assert_eq!((k.vcount(), k.ecount()), (6, 12));
1344 /// ```
1345 pub fn kautz(m: usize, n: usize) -> Result<Graph> {
1346 let m = int(m, "m")?;
1347 let n = int(n, "n")?;
1348 // `K(m, 0)` is the complete digraph on `m + 1` vertices; otherwise igraph
1349 // needs `m^n` and `(m + 1)^(n + 1)`.
1350 let fits = match (m, n) {
1351 (_, 0) => m.checked_add(1).is_some(),
1352 (0, _) => true,
1353 _ => {
1354 checked_ipow(m, n).is_some()
1355 && m.checked_add(1)
1356 .zip(n.checked_add(1))
1357 .and_then(|(b, e)| checked_ipow(b, e))
1358 .is_some()
1359 }
1360 };
1361 if !fits {
1362 return Err(Error::invalid(format!(
1363 "Parameters ({m}, {n}) too large for Kautz graph."
1364 )));
1365 }
1366 Graph::init_with(|g| unsafe { igraph_kautz(g, m, n) })
1367 }
1368
1369 /// The circulant graph `C_n(shifts)`: vertex `j` is connected to
1370 /// `(j + s) mod n` for every shift `s`.
1371 ///
1372 /// Shifts may be negative; shifts that are multiples of `n` are ignored
1373 /// and no multi-edges or self-loops are created.
1374 ///
1375 /// Binds [`igraph_circulant`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_circulant).
1376 /// Time complexity: O(|V| |shifts|).
1377 ///
1378 /// See also [`extended_chordal_ring`](Self::extended_chordal_ring), which
1379 /// allows position-dependent chords and multi-edges, and
1380 /// [`Graph::k_regular_game`] for *random* regular graphs.
1381 ///
1382 /// # Examples
1383 /// ```
1384 /// use igraph::prelude::*;
1385 /// // C_5(1, 2) is K_5.
1386 /// assert_eq!(Graph::circulant(5, &[1, 2], false).unwrap().ecount(), 10);
1387 /// ```
1388 pub fn circulant(n: usize, shifts: &[i64], directed: bool) -> Result<Graph> {
1389 let n = int(n, "the number of vertices")?;
1390 let shifts = VectorInt::view(shifts);
1391 Graph::init_with(|g| unsafe { igraph_circulant(g, n, shifts.as_ptr(), directed) })
1392 }
1393
1394 /// The generalized Petersen graph `G(n, k)`: an outer `n`-cycle
1395 /// `v_0 .. v_(n-1)` (ids `0..n`), an inner circulant `u_i ~ u_(i+k mod n)`
1396 /// (ids `n..2n`) and the spokes `v_i ~ u_i`.
1397 ///
1398 /// It has `2n` vertices and `3n` edges and is cubic. `G(5, 2)` is the
1399 /// Petersen graph, `G(4, 1)` the cube, `G(10, 3)` the Desargues graph.
1400 ///
1401 /// Binds [`igraph_generalized_petersen`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_generalized_petersen).
1402 /// Time complexity: O(|V|).
1403 ///
1404 /// # Errors
1405 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) unless
1406 /// `n >= 3` and `0 < k < n / 2`.
1407 ///
1408 /// # Examples
1409 /// ```
1410 /// use igraph::prelude::*;
1411 /// let desargues = Graph::generalized_petersen(10, 3).unwrap();
1412 /// assert_eq!((desargues.vcount(), desargues.ecount()), (20, 30));
1413 /// let petersen = Graph::generalized_petersen(5, 2).unwrap();
1414 /// assert!(petersen.isomorphic(&Graph::famous("Petersen").unwrap()).unwrap());
1415 /// ```
1416 pub fn generalized_petersen(n: usize, k: usize) -> Result<Graph> {
1417 let n = int(n, "the number of vertices")?;
1418 let k = int(k, "the shift")?;
1419 Graph::init_with(|g| unsafe { igraph_generalized_petersen(g, n, k) })
1420 }
1421
1422 /// Creates a named graph, such as `"Petersen"` or `"Zachary"`.
1423 ///
1424 /// The name is case insensitive; the supported graphs (and their sizes)
1425 /// are listed by [`FamousGraph`], which can be passed directly. Some names
1426 /// have aliases in igraph: `Dodecahedral`, `Icosahedral`, `Octahedral`,
1427 /// `Tetrahedral` and `Groetzsch`.
1428 ///
1429 /// Binds [`igraph_famous`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_famous).
1430 /// Time complexity: O(|V| + |E|).
1431 ///
1432 /// See also [`atlas`](Self::atlas) for all small graphs, and
1433 /// [`crate::foreign`] to read graphs from files.
1434 ///
1435 /// # Errors
1436 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for an
1437 /// unknown name (or one containing a NUL byte).
1438 ///
1439 /// # Examples
1440 /// ```
1441 /// use igraph::{constructors::FamousGraph, prelude::*};
1442 /// let karate = Graph::famous("zachary").unwrap();
1443 /// assert_eq!((karate.vcount(), karate.ecount()), (34, 78));
1444 /// let kite = Graph::famous(FamousGraph::KrackhardtKite).unwrap();
1445 /// assert_eq!(kite.vcount(), 10);
1446 /// assert_eq!(Graph::famous("Unicorn").unwrap_err().kind(), ErrorKind::InvalidValue);
1447 /// // The Frucht graph is cubic but has no symmetry at all.
1448 /// let frucht = Graph::famous(FamousGraph::Frucht).unwrap();
1449 /// assert_eq!(frucht.count_automorphisms(None).unwrap(), 1.0);
1450 /// ```
1451 pub fn famous(name: impl AsRef<str>) -> Result<Graph> {
1452 let name = CString::new(name.as_ref())
1453 .map_err(|_| Error::invalid("graph names cannot contain NUL bytes"))?;
1454 Graph::init_with(|g| unsafe { igraph_famous(g, name.as_ptr()) })
1455 }
1456
1457 /// Creates a graph from LCF (Lederberg–Coxeter–Frucht) notation
1458 /// `[shifts]^repeats` on `n` vertices.
1459 ///
1460 /// The graph is the cycle `0 - 1 - ... - (n-1) - 0` plus, going around
1461 /// the cycle, a chord from vertex `i` to `i + s` for the shifts `s`
1462 /// repeated `repeats` times. Normally `n = shifts.len() * repeats`, and the
1463 /// result is a cubic Hamiltonian graph. The result is always simple:
1464 /// duplicate chords are merged and loops (shifts that are multiples of
1465 /// `n`) dropped. Shifts are taken modulo `n`, so any `i64` is accepted;
1466 /// `n = 0` gives the null graph.
1467 ///
1468 /// Binds [`igraph_lcf`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_lcf)
1469 /// (and covers the variadic `igraph_lcf_small`).
1470 /// Time complexity: O(|V| + |E|).
1471 ///
1472 /// See also [`generalized_petersen`](Self::generalized_petersen) and
1473 /// [`famous`](Self::famous) for other ways to build well-known cubic graphs.
1474 ///
1475 /// # Examples
1476 /// ```
1477 /// use igraph::prelude::*;
1478 /// // The Heawood graph is [5, -5]^7.
1479 /// let h = Graph::lcf(14, &[5, -5], 7).unwrap();
1480 /// assert_eq!((h.vcount(), h.ecount()), (14, 21));
1481 /// assert!(h.isomorphic(&Graph::famous("Heawood").unwrap()).unwrap());
1482 /// ```
1483 pub fn lcf(n: usize, shifts: &[i64], repeats: usize) -> Result<Graph> {
1484 let n = int(n, "the number of vertices")?;
1485 let repeats = int(repeats, "the number of repeats")?;
1486 if n == 0 {
1487 // igraph 1.0.0 and 1.0.1 compute `i % n` for every chord: with n = 0 and a
1488 // non-empty chord list it dies with a division by zero (SIGFPE).
1489 return Graph::empty(0, false);
1490 }
1491 // igraph computes `n + i + shift` without overflow checks (UB in C
1492 // for huge shifts), and rejects shifts below `-(n + i)`; reducing
1493 // them modulo `n` keeps the meaning and avoids both problems.
1494 let shifts: VectorInt = shifts.iter().map(|&s| s.rem_euclid(n)).collect();
1495 Graph::init_with(|g| unsafe { igraph_lcf(g, n, &shifts, repeats) })
1496 }
1497
1498 /// Builds a graph realizing the given degree sequence, deterministically.
1499 ///
1500 /// With `in_degrees = None` an undirected graph with degrees
1501 /// `out_degrees` is created, otherwise a directed graph with the given
1502 /// out- and in-degrees. Simple graphs are built with the Havel–Hakimi
1503 /// (undirected) or Kleitman–Wang (directed) algorithm: repeatedly pick a
1504 /// vertex and connect all its stubs to the vertices with the largest
1505 /// remaining degrees. Multigraphs use an analogous one-edge-at-a-time
1506 /// procedure; with self-loops allowed, leftover stubs become loops on a
1507 /// single vertex.
1508 ///
1509 /// `allowed` selects the kind of graph (directed graphs support only
1510 /// [`AllowedEdgeTypes::SIMPLE`]; [`AllowedEdgeTypes::LOOPS`] alone is not
1511 /// implemented). `method` selects the vertex order:
1512 /// [`RealizeDegseq::Smallest`] (smallest remaining degree first; in the
1513 /// undirected case it yields a *connected* graph whenever one exists, so
1514 /// it builds a tree from tree degrees), [`RealizeDegseq::Largest`]
1515 /// (strongly assortative, often disconnected) or
1516 /// [`RealizeDegseq::Index`] (in vertex order).
1517 ///
1518 /// Binds [`igraph_realize_degree_sequence`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_realize_degree_sequence).
1519 /// Time complexity: O(V + α(V) E) for simple undirected graphs.
1520 ///
1521 /// See also [`is_graphical`](crate::mixing::is_graphical), which only
1522 /// decides whether a realization exists (it takes the same
1523 /// [`AllowedEdgeTypes`] flags), and
1524 /// [`Graph::degree_sequence_game`] for *random* realizations.
1525 ///
1526 /// # Errors
1527 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
1528 /// sequence is not graphical for the requested kind of graph, or lengths
1529 /// or sums of the directed sequences differ;
1530 /// [`ErrorKind::Unimplemented`](crate::ErrorKind::Unimplemented) for
1531 /// unsupported combinations.
1532 ///
1533 /// # Examples
1534 /// ```
1535 /// use igraph::{constructors::AllowedEdgeTypes, prelude::*};
1536 /// let degrees = [3, 3, 2, 2, 2, 1, 1];
1537 /// let g = Graph::realize_degree_sequence(
1538 /// °rees, None, AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest,
1539 /// ).unwrap();
1540 /// assert_eq!(g.degree(VertexSelector::All, NeighborMode::All, Loops::Twice).unwrap(), degrees);
1541 /// // [3, 3] cannot be realized as a simple graph...
1542 /// assert!(Graph::realize_degree_sequence(
1543 /// &[3, 3], None, AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest).is_err());
1544 /// // ... but it can as a multigraph: three parallel edges.
1545 /// let m = Graph::realize_degree_sequence(
1546 /// &[3, 3], None, AllowedEdgeTypes::MULTI, RealizeDegseq::Smallest).unwrap();
1547 /// assert_eq!(m.ecount(), 3);
1548 /// ```
1549 pub fn realize_degree_sequence(
1550 out_degrees: &[i64],
1551 in_degrees: Option<&[i64]>,
1552 allowed: impl Into<AllowedEdgeTypes>,
1553 method: RealizeDegseq,
1554 ) -> Result<Graph> {
1555 let out = VectorInt::view(out_degrees);
1556 let ind = in_degrees.map(VectorInt::view);
1557 let ind_ptr = ind.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
1558 let allowed = igraph_edge_type_sw_t::from(allowed.into());
1559 Graph::init_with(|g| unsafe {
1560 igraph_realize_degree_sequence(g, out.as_ptr(), ind_ptr, allowed, method.into())
1561 })
1562 }
1563
1564 /// Builds a bipartite graph realizing the bidegree sequence
1565 /// `(degrees1, degrees2)`, deterministically.
1566 ///
1567 /// Vertices `0..degrees1.len()` form the first partition, followed by
1568 /// the second one. A Havel–Hakimi-like algorithm is used; `allowed` is
1569 /// [`AllowedEdgeTypes::SIMPLE`] or [`AllowedEdgeTypes::MULTI`] (a
1570 /// bipartite graph has no self-loops, so igraph ignores the loops flag:
1571 /// `LOOPS` acts as `SIMPLE`, `ALL` as `MULTI`), and
1572 /// `method` has the same meaning as in
1573 /// [`realize_degree_sequence`](Self::realize_degree_sequence) (with
1574 /// [`RealizeDegseq::Smallest`] the result is connected whenever the
1575 /// sequence is potentially connected).
1576 ///
1577 /// Binds [`igraph_realize_bipartite_degree_sequence`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_realize_bipartite_degree_sequence).
1578 ///
1579 /// See also [`is_bigraphical`](crate::mixing::is_bigraphical), which only
1580 /// decides whether a realization exists.
1581 ///
1582 /// # Errors
1583 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
1584 /// bidegree sequence cannot be realized.
1585 ///
1586 /// # Examples
1587 /// ```
1588 /// use igraph::{constructors::AllowedEdgeTypes, prelude::*};
1589 /// // Three students, two projects: who works on what.
1590 /// let g = Graph::realize_bipartite_degree_sequence(
1591 /// &[1, 2, 1], &[2, 2], AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest,
1592 /// ).unwrap();
1593 /// assert_eq!((g.vcount(), g.ecount()), (5, 4));
1594 /// assert!(g.is_bipartite().unwrap());
1595 /// ```
1596 pub fn realize_bipartite_degree_sequence(
1597 degrees1: &[i64],
1598 degrees2: &[i64],
1599 allowed: impl Into<AllowedEdgeTypes>,
1600 method: RealizeDegseq,
1601 ) -> Result<Graph> {
1602 let d1 = VectorInt::view(degrees1);
1603 let d2 = VectorInt::view(degrees2);
1604 let allowed = igraph_edge_type_sw_t::from(allowed.into());
1605 Graph::init_with(|g| unsafe {
1606 igraph_realize_bipartite_degree_sequence(
1607 g,
1608 d1.as_ptr(),
1609 d2.as_ptr(),
1610 allowed,
1611 method.into(),
1612 )
1613 })
1614 }
1615
1616 /// Creates a triangular lattice of the given shape.
1617 ///
1618 /// Vertices are points `(i, j)` connected to `(i+1, j)`, `(i, j+1)` and
1619 /// `(i-1, j+1)` when present, so degrees are at most 6. `dims` of length
1620 /// 1 gives a triangle with `dims[0]` vertices per side, length 2 a
1621 /// "quasi-rectangle" with sides of `dims[0]` and `dims[1]` vertices,
1622 /// length 3 a hexagon with the given side lengths. Vertices are ordered
1623 /// row by row. This is the planar dual of
1624 /// [`hexagonal_lattice`](Self::hexagonal_lattice) with the same `dims`.
1625 ///
1626 /// Binds [`igraph_triangular_lattice`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_triangular_lattice).
1627 /// Time complexity: O(|V|).
1628 ///
1629 /// # Errors
1630 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) unless
1631 /// `dims` has length 1, 2 or 3, or if a hexagon shape is so large that
1632 /// its row sizes overflow an `i64` (checked before calling igraph).
1633 ///
1634 /// # Examples
1635 /// ```
1636 /// use igraph::prelude::*;
1637 /// let t = Graph::triangular_lattice(&[5], false, false).unwrap();
1638 /// assert_eq!((t.vcount(), t.ecount()), (15, 30));
1639 /// ```
1640 pub fn triangular_lattice(dims: &[usize], directed: bool, mutual: bool) -> Result<Graph> {
1641 let dims = int_vector(dims, "a lattice dimension")?;
1642 check_hex_shape(&dims, "a triangular lattice")?;
1643 Graph::init_with(|g| unsafe { igraph_triangular_lattice(g, &dims, directed, mutual) })
1644 }
1645
1646 /// Creates a hexagonal (honeycomb) lattice of the given shape.
1647 ///
1648 /// `dims` is interpreted as in
1649 /// [`triangular_lattice`](Self::triangular_lattice), but counts
1650 /// *hexagons*: the 6-cycles of the result correspond one-to-one to the
1651 /// vertices of the triangular lattice with the same `dims`. Degrees are
1652 /// at most 3.
1653 ///
1654 /// Binds [`igraph_hexagonal_lattice`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_hexagonal_lattice).
1655 /// Time complexity: O(|V|).
1656 ///
1657 /// # Errors
1658 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) unless
1659 /// `dims` has length 1, 2 or 3, or if a hexagon shape is so large that
1660 /// its row sizes overflow an `i64` (checked before calling igraph).
1661 ///
1662 /// # Examples
1663 /// ```
1664 /// use igraph::prelude::*;
1665 /// let benzene = Graph::hexagonal_lattice(&[1], false, false).unwrap();
1666 /// assert_eq!((benzene.vcount(), benzene.ecount()), (6, 6));
1667 /// ```
1668 pub fn hexagonal_lattice(dims: &[usize], directed: bool, mutual: bool) -> Result<Graph> {
1669 let dims = int_vector(dims, "a lattice dimension")?;
1670 check_hex_shape(&dims, "a hexagonal lattice")?;
1671 Graph::init_with(|g| unsafe { igraph_hexagonal_lattice(g, &dims, directed, mutual) })
1672 }
1673
1674 /// The Mycielski graph `M_k`: triangle-free with chromatic number `k`.
1675 ///
1676 /// Obtained by iterating the Mycielski construction: `M_0` is the null
1677 /// graph, `M_1` a single vertex, `M_2` an edge, `M_3` the 5-cycle, `M_4`
1678 /// the Grötzsch graph. For `k > 1`, `M_k` has `3 * 2^(k-2) - 1` vertices
1679 /// and `(7 * 3^(k-2) + 1) / 2 - 3 * 2^(k-2)` edges.
1680 ///
1681 /// This function is marked *experimental* in igraph 1.0.x.
1682 ///
1683 /// Binds [`igraph_mycielski_graph`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_mycielski_graph).
1684 /// Time complexity: O(3^k).
1685 ///
1686 /// See also [`Graph::mycielskian`], which applies the construction to an
1687 /// arbitrary graph.
1688 ///
1689 /// # Examples
1690 /// ```
1691 /// use igraph::prelude::*;
1692 /// let m4 = Graph::mycielski_graph(4).unwrap();
1693 /// assert_eq!((m4.vcount(), m4.ecount()), (11, 20));
1694 /// assert!(m4.isomorphic(&Graph::famous("Grotzsch").unwrap()).unwrap());
1695 /// assert_eq!(m4.count_triangles().unwrap(), 0.0);
1696 /// ```
1697 pub fn mycielski_graph(k: usize) -> Result<Graph> {
1698 let k = int(k, "the order")?;
1699 Graph::init_with(|g| unsafe { igraph_mycielski_graph(g, k) })
1700 }
1701}