rust_igraph/core/graph.rs
1//! `Graph` — pure-Rust port of `igraph_t`.
2//!
3//! Storage is the **indexed edge list** that upstream igraph uses (see
4//! `references/igraph/include/igraph_datatype.h:105-116`):
5//!
6//! - `from[e]`, `to[e]` — canonical edge list. Edge `e` runs from
7//! `from[e]` to `to[e]`; `|from| == |to| == ecount`.
8//! - `oi[i]` — edge ids ordered by `from` (and then `to`).
9//! - `ii[i]` — edge ids ordered by `to` (and then `from`).
10//! - `os[v]..os[v+1]` — slice of `oi` covering vertex `v`'s out-edges.
11//! - `is[v]..is[v+1]` — slice of `ii` covering vertex `v`'s in-edges.
12//!
13//! For undirected graphs the edge list is canonicalised so `from[e] <= to[e]`
14//! (matching upstream igraph's invariant in `type_indexededgelist.c:282-288`).
15//! The doubled in/out indexing makes `neighbors()` symmetric for undirected
16//! graphs without storing each edge twice.
17//!
18//! ALGO-CORE-001a (Phase 1, this file): struct + `new`/`with_vertices` +
19//! `add_vertices`/`add_edge`/`add_edges` + `vcount`/`ecount`/`is_directed` +
20//! `neighbors`/`degree` + `Clone`.
21//!
22//! Follow-up AWUs:
23//! - 001b: `incident`, edge-id helpers.
24//! - 001c: `delete_vertices`/`delete_edges`.
25//! - 001d: `edge`/`edges`/`get_eid`/`get_eids`/`get_all_eids_between`.
26//! - 001e: property cache, `is_same_graph`.
27//!
28//! Attribute system → ALGO-AT-* (out of scope here).
29
30use std::collections::HashMap;
31
32use super::attributes::AttributeValue;
33use super::cache::{
34 CachedProperty, PropertyCache, invalidate_after_add_edges, invalidate_after_add_vertices,
35};
36use super::error::{IgraphError, IgraphResult};
37
38/// Vertex id. The Phase-0 ADR-0007 fixes this to `u32`; `Option<VertexId>`
39/// is the idiomatic "no vertex" sentinel (igraph C uses `-1`).
40pub type VertexId = u32;
41
42/// Edge id. Same width as [`VertexId`]; an edge id is its position in
43/// `from`/`to`.
44pub type EdgeId = u32;
45
46/// Iterator over graph edges as `(from, to)` pairs.
47pub type EdgeIter<'a> = std::iter::Map<
48 std::iter::Zip<std::slice::Iter<'a, VertexId>, std::slice::Iter<'a, VertexId>>,
49 fn((&'a VertexId, &'a VertexId)) -> (VertexId, VertexId),
50>;
51
52/// Zero-allocation iterator over the neighbors of a vertex.
53///
54/// For directed graphs, yields out-neighbors in ascending order.
55/// For undirected graphs, yields all neighbors in ascending order by
56/// merging the out-edge and in-edge sublists on the fly.
57///
58/// Created by [`Graph::neighbors_iter`].
59pub struct NeighborsIter<'a> {
60 graph: &'a Graph,
61 out_pos: usize,
62 out_end: usize,
63 in_pos: usize,
64 in_end: usize,
65 directed: bool,
66}
67
68impl Iterator for NeighborsIter<'_> {
69 type Item = VertexId;
70
71 fn next(&mut self) -> Option<Self::Item> {
72 if self.directed {
73 if self.out_pos < self.out_end {
74 let eid = self.graph.oi[self.out_pos] as usize;
75 self.out_pos += 1;
76 Some(self.graph.to[eid])
77 } else {
78 None
79 }
80 } else {
81 let have_out = self.out_pos < self.out_end;
82 let have_in = self.in_pos < self.in_end;
83 match (have_out, have_in) {
84 (false, false) => None,
85 (true, false) => {
86 let eid = self.graph.oi[self.out_pos] as usize;
87 self.out_pos += 1;
88 Some(self.graph.to[eid])
89 }
90 (false, true) => {
91 let eid = self.graph.ii[self.in_pos] as usize;
92 self.in_pos += 1;
93 Some(self.graph.from[eid])
94 }
95 (true, true) => {
96 let a = self.graph.to[self.graph.oi[self.out_pos] as usize];
97 let b = self.graph.from[self.graph.ii[self.in_pos] as usize];
98 if a <= b {
99 self.out_pos += 1;
100 Some(a)
101 } else {
102 self.in_pos += 1;
103 Some(b)
104 }
105 }
106 }
107 }
108 }
109
110 fn size_hint(&self) -> (usize, Option<usize>) {
111 let remaining = (self.out_end - self.out_pos) + (self.in_end - self.in_pos);
112 (remaining, Some(remaining))
113 }
114}
115
116impl ExactSizeIterator for NeighborsIter<'_> {}
117
118/// Counterpart of `igraph_t` (see `references/igraph/include/igraph_datatype.h`).
119///
120/// Phase-0 callers (`bfs`, `read_edgelist`, oracle tests) only depended on
121/// `with_vertices`, `add_edge`, `add_edges`, `vcount`, `ecount`, `neighbors`,
122/// `degree` — those signatures are preserved here, so existing call sites
123/// compile unchanged. New for Phase 1: `new` (with `directed` flag),
124/// `is_directed`.
125#[derive(Debug, Clone, Default)]
126pub struct Graph {
127 /// Vertex count. Redundant with the highest used id; mirrors `igraph_t::n`.
128 n: u32,
129 /// Whether the graph is directed.
130 directed: bool,
131 /// Source endpoints, one per edge.
132 from: Vec<VertexId>,
133 /// Target endpoints, one per edge.
134 to: Vec<VertexId>,
135 /// Edge ids in `from`-major order.
136 oi: Vec<EdgeId>,
137 /// Edge ids in `to`-major order.
138 ii: Vec<EdgeId>,
139 /// `os[v]..os[v+1]` is the slice of `oi` for vertex `v`'s out-edges.
140 /// Length is `n + 1`; `os[0] == 0`, `os[n] == ecount`.
141 os: Vec<u32>,
142 /// `is[v]..is[v+1]` for incoming. Same shape as `os`.
143 is: Vec<u32>,
144 /// Boolean property cache. Mirrors `igraph_t::cache`.
145 cache: PropertyCache,
146 /// Graph-level attributes (name → value).
147 gattrs: HashMap<String, AttributeValue>,
148 /// Vertex attributes (name → vec of values, one per vertex).
149 vertex_attrs: HashMap<String, Vec<AttributeValue>>,
150 /// Edge attributes (name → vec of values, one per edge).
151 edge_attrs: HashMap<String, Vec<AttributeValue>>,
152}
153
154impl Graph {
155 /// Construct an empty graph on `n` vertices.
156 ///
157 /// Counterpart of `igraph_empty()`; `directed` defaults to `false` if
158 /// you use [`Graph::with_vertices`] instead.
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use rust_igraph::Graph;
164 ///
165 /// let g = Graph::new(5, true).unwrap();
166 /// assert_eq!(g.vcount(), 5);
167 /// assert_eq!(g.ecount(), 0);
168 /// assert!(g.is_directed());
169 /// ```
170 pub fn new(n: u32, directed: bool) -> IgraphResult<Self> {
171 let mut g = Self {
172 n: 0,
173 directed,
174 from: Vec::new(),
175 to: Vec::new(),
176 oi: Vec::new(),
177 ii: Vec::new(),
178 os: vec![0],
179 is: vec![0],
180 cache: PropertyCache::new(),
181 gattrs: HashMap::new(),
182 vertex_attrs: HashMap::new(),
183 edge_attrs: HashMap::new(),
184 };
185 g.add_vertices(n)?;
186 Ok(g)
187 }
188
189 /// Build a graph from an edge list, inferring the vertex count from
190 /// the highest endpoint.
191 ///
192 /// This is the most ergonomic way to create a small graph. The vertex
193 /// count is `max(u, v) + 1` over all `(u, v)` pairs (or 0 if `edges`
194 /// is empty and `n_override` is `None`).
195 ///
196 /// `n_override` can force a minimum vertex count (useful when you want
197 /// isolated vertices beyond the edges). Pass `None` to auto-derive.
198 ///
199 /// # Examples
200 ///
201 /// ```
202 /// use rust_igraph::Graph;
203 ///
204 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], false, None).unwrap();
205 /// assert_eq!(g.vcount(), 3);
206 /// assert_eq!(g.ecount(), 3);
207 /// assert!(!g.is_directed());
208 /// ```
209 pub fn from_edges(
210 edges: &[(u32, u32)],
211 directed: bool,
212 n_override: Option<u32>,
213 ) -> IgraphResult<Self> {
214 let max_id = edges
215 .iter()
216 .flat_map(|&(u, v)| [u, v])
217 .max()
218 .map_or(Some(0), |m| m.checked_add(1));
219 let auto_n = max_id.ok_or(IgraphError::InvalidArgument(
220 "vertex id overflow in from_edges".to_owned(),
221 ))?;
222 let n = n_override.map_or(auto_n, |ov| ov.max(auto_n));
223 let mut g = Self::new(n, directed)?;
224 g.add_edges(edges.to_vec())?;
225 Ok(g)
226 }
227
228 /// Build a graph from weighted edges, returning both the graph and the
229 /// weight vector (indexed by edge id).
230 ///
231 /// Each element of `edges` is `(from, to, weight)`. The resulting weight
232 /// vector has length equal to the edge count, with `weights[eid]`
233 /// corresponding to the edge added from the `eid`-th tuple.
234 ///
235 /// # Examples
236 ///
237 /// ```
238 /// use rust_igraph::Graph;
239 ///
240 /// let (g, weights) = Graph::from_weighted_edges(
241 /// &[(0, 1, 1.5), (1, 2, 2.0), (2, 0, 0.5)],
242 /// false,
243 /// None,
244 /// ).unwrap();
245 /// assert_eq!(g.vcount(), 3);
246 /// assert_eq!(g.ecount(), 3);
247 /// assert_eq!(weights, vec![1.5, 2.0, 0.5]);
248 /// ```
249 pub fn from_weighted_edges(
250 edges: &[(u32, u32, f64)],
251 directed: bool,
252 n_override: Option<u32>,
253 ) -> IgraphResult<(Self, Vec<f64>)> {
254 let plain: Vec<(u32, u32)> = edges.iter().map(|&(u, v, _)| (u, v)).collect();
255 let weights: Vec<f64> = edges.iter().map(|&(_, _, w)| w).collect();
256 let g = Self::from_edges(&plain, directed, n_override)?;
257 Ok((g, weights))
258 }
259
260 /// Parse an undirected graph from an edge-list string.
261 ///
262 /// Each non-empty, non-comment line should contain two whitespace-separated
263 /// vertex ids. Lines starting with `#` are ignored. This is the most
264 /// convenient way to construct a graph inline (e.g. in tests or examples).
265 ///
266 /// # Examples
267 ///
268 /// ```
269 /// use rust_igraph::Graph;
270 ///
271 /// let g = Graph::from_edge_list_str("0 1\n1 2\n2 0").unwrap();
272 /// assert_eq!(g.vcount(), 3);
273 /// assert_eq!(g.ecount(), 3);
274 /// ```
275 pub fn from_edge_list_str(s: &str) -> IgraphResult<Self> {
276 use std::io::Cursor;
277 crate::algorithms::io::edgelist::read_edgelist(Cursor::new(s))
278 }
279
280 /// Construct a graph from an adjacency matrix.
281 ///
282 /// Counterpart of `igraph_adjacency()`. The matrix should be a
283 /// square `n×n` slice-of-slices where `matrix[i][j]` gives the
284 /// number of edges from vertex `i` to vertex `j` (or the edge
285 /// weight; see below).
286 ///
287 /// For undirected graphs (`directed = false`), only the upper
288 /// triangle is used (including diagonal for self-loops); the lower
289 /// triangle is ignored. Each non-zero entry `matrix[i][j]` (with
290 /// `i <= j`) creates one edge.
291 ///
292 /// For directed graphs, every non-zero entry creates one edge.
293 ///
294 /// Entries are rounded to the nearest integer to determine edge
295 /// count. If you need fractional weights, use
296 /// [`from_adjacency_matrix_weighted`](Graph::from_adjacency_matrix_weighted).
297 ///
298 /// # Errors
299 ///
300 /// Returns an error if the matrix is not square.
301 ///
302 /// # Examples
303 ///
304 /// ```
305 /// use rust_igraph::Graph;
306 ///
307 /// let adj = vec![
308 /// vec![0.0, 1.0, 1.0],
309 /// vec![1.0, 0.0, 1.0],
310 /// vec![1.0, 1.0, 0.0],
311 /// ];
312 /// let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
313 /// assert_eq!(g.vcount(), 3);
314 /// assert_eq!(g.ecount(), 3); // triangle
315 /// ```
316 pub fn from_adjacency_matrix(matrix: &[Vec<f64>], directed: bool) -> IgraphResult<Self> {
317 let n = matrix.len();
318 for row in matrix {
319 if row.len() != n {
320 return Err(IgraphError::InvalidArgument(format!(
321 "adjacency matrix is not square: got row of length {} for {}×{} matrix",
322 row.len(),
323 n,
324 n
325 )));
326 }
327 }
328
329 let n_u32 = u32::try_from(n)
330 .map_err(|_| IgraphError::InvalidArgument("matrix too large for u32".to_owned()))?;
331 let mut graph = Self::new(n_u32, directed)?;
332
333 #[allow(clippy::cast_possible_truncation)]
334 if directed {
335 for (i, row) in matrix.iter().enumerate() {
336 for (j, &val) in row.iter().enumerate() {
337 let count = val.round() as i64;
338 for _ in 0..count.max(0) {
339 graph.add_edge(i as u32, j as u32)?;
340 }
341 }
342 }
343 } else {
344 for (i, row) in matrix.iter().enumerate() {
345 for (j, &val) in row.iter().enumerate().skip(i) {
346 let count = val.round() as i64;
347 for _ in 0..count.max(0) {
348 graph.add_edge(i as u32, j as u32)?;
349 }
350 }
351 }
352 }
353
354 Ok(graph)
355 }
356
357 /// Construct a graph from an adjacency matrix, also returning edge weights.
358 ///
359 /// Like [`from_adjacency_matrix`](Graph::from_adjacency_matrix), but
360 /// instead of rounding entries to edge counts, each non-zero entry
361 /// creates exactly one edge with the matrix value as its weight.
362 ///
363 /// Returns the graph and a weight vector aligned with edge indices.
364 ///
365 /// # Errors
366 ///
367 /// Returns an error if the matrix is not square.
368 ///
369 /// # Examples
370 ///
371 /// ```
372 /// use rust_igraph::Graph;
373 ///
374 /// let adj = vec![
375 /// vec![0.0, 2.5, 0.0],
376 /// vec![2.5, 0.0, 1.0],
377 /// vec![0.0, 1.0, 0.0],
378 /// ];
379 /// let (g, weights) = Graph::from_adjacency_matrix_weighted(&adj, false).unwrap();
380 /// assert_eq!(g.vcount(), 3);
381 /// assert_eq!(g.ecount(), 2);
382 /// assert!((weights[0] - 2.5).abs() < 1e-10);
383 /// assert!((weights[1] - 1.0).abs() < 1e-10);
384 /// ```
385 pub fn from_adjacency_matrix_weighted(
386 matrix: &[Vec<f64>],
387 directed: bool,
388 ) -> IgraphResult<(Self, Vec<f64>)> {
389 let n = matrix.len();
390 for row in matrix {
391 if row.len() != n {
392 return Err(IgraphError::InvalidArgument(format!(
393 "adjacency matrix is not square: got row of length {} for {}×{} matrix",
394 row.len(),
395 n,
396 n
397 )));
398 }
399 }
400
401 let n_u32 = u32::try_from(n)
402 .map_err(|_| IgraphError::InvalidArgument("matrix too large for u32".to_owned()))?;
403 let mut graph = Self::new(n_u32, directed)?;
404 let mut weights = Vec::new();
405
406 #[allow(clippy::cast_possible_truncation)]
407 if directed {
408 for (i, row) in matrix.iter().enumerate() {
409 for (j, &w) in row.iter().enumerate() {
410 if w != 0.0 {
411 graph.add_edge(i as u32, j as u32)?;
412 weights.push(w);
413 }
414 }
415 }
416 } else {
417 for (i, row) in matrix.iter().enumerate() {
418 for (j, &w) in row.iter().enumerate().skip(i) {
419 if w != 0.0 {
420 graph.add_edge(i as u32, j as u32)?;
421 weights.push(w);
422 }
423 }
424 }
425 }
426
427 Ok((graph, weights))
428 }
429
430 /// Construct a graph from an adjacency list.
431 ///
432 /// `adj_list[v]` contains the neighbors of vertex `v`. The number of
433 /// vertices is `adj_list.len()`.
434 ///
435 /// For undirected graphs (`directed = false`), an edge `(u, v)` should
436 /// appear in both `adj_list[u]` and `adj_list[v]`; each pair is
437 /// counted once (duplicates are deduplicated by only adding edge `(u, v)`
438 /// when `u <= v` or when it appears only in `adj_list[u]`).
439 ///
440 /// For directed graphs, `adj_list[v]` lists the **out-neighbors** of `v`.
441 ///
442 /// # Errors
443 ///
444 /// Returns an error if any neighbor index is out of range.
445 ///
446 /// # Examples
447 ///
448 /// ```
449 /// use rust_igraph::Graph;
450 ///
451 /// // Triangle: 0-1, 1-2, 0-2
452 /// let adj = vec![vec![1, 2], vec![0, 2], vec![0, 1]];
453 /// let g = Graph::from_adjacency_list(&adj, false).unwrap();
454 /// assert_eq!(g.vcount(), 3);
455 /// assert_eq!(g.ecount(), 3);
456 /// ```
457 ///
458 /// ```
459 /// use rust_igraph::Graph;
460 ///
461 /// // Directed: 0->1, 0->2, 1->2
462 /// let adj = vec![vec![1, 2], vec![2], vec![]];
463 /// let g = Graph::from_adjacency_list(&adj, true).unwrap();
464 /// assert_eq!(g.vcount(), 3);
465 /// assert_eq!(g.ecount(), 3);
466 /// assert!(g.is_directed());
467 /// ```
468 pub fn from_adjacency_list(adj_list: &[Vec<u32>], directed: bool) -> IgraphResult<Self> {
469 let n = u32::try_from(adj_list.len()).map_err(|_| {
470 IgraphError::InvalidArgument("adjacency list too large for u32".to_owned())
471 })?;
472
473 let mut graph = Self::new(n, directed)?;
474
475 if directed {
476 for (src, neighbors) in adj_list.iter().enumerate() {
477 #[allow(clippy::cast_possible_truncation)]
478 let src_u32 = src as u32;
479 for &tgt in neighbors {
480 if tgt >= n {
481 return Err(IgraphError::VertexOutOfRange { id: tgt, n });
482 }
483 graph.add_edge(src_u32, tgt)?;
484 }
485 }
486 } else {
487 for (src, neighbors) in adj_list.iter().enumerate() {
488 #[allow(clippy::cast_possible_truncation)]
489 let src_u32 = src as u32;
490 for &tgt in neighbors {
491 if tgt >= n {
492 return Err(IgraphError::VertexOutOfRange { id: tgt, n });
493 }
494 if src_u32 <= tgt {
495 graph.add_edge(src_u32, tgt)?;
496 }
497 }
498 }
499 }
500
501 Ok(graph)
502 }
503
504 /// Construct an empty *undirected* graph on `n` vertices.
505 ///
506 /// Builds the graph directly (no intermediate `Result`) since an
507 /// empty undirected graph with `n` vertices cannot fail to construct.
508 ///
509 /// # Examples
510 ///
511 /// ```
512 /// use rust_igraph::Graph;
513 ///
514 /// let g = Graph::with_vertices(4);
515 /// assert_eq!(g.vcount(), 4);
516 /// assert!(!g.is_directed());
517 /// ```
518 pub fn with_vertices(n: u32) -> Self {
519 let len = n as usize + 1;
520 Self {
521 n,
522 directed: false,
523 from: Vec::new(),
524 to: Vec::new(),
525 oi: Vec::new(),
526 ii: Vec::new(),
527 os: vec![0; len],
528 is: vec![0; len],
529 cache: PropertyCache::new(),
530 gattrs: HashMap::new(),
531 vertex_attrs: HashMap::new(),
532 edge_attrs: HashMap::new(),
533 }
534 }
535
536 /// Number of vertices. Counterpart of `igraph_vcount()`.
537 ///
538 /// # Examples
539 ///
540 /// ```
541 /// use rust_igraph::Graph;
542 ///
543 /// let g = Graph::with_vertices(10);
544 /// assert_eq!(g.vcount(), 10);
545 /// ```
546 #[must_use]
547 pub fn vcount(&self) -> u32 {
548 self.n
549 }
550
551 /// Number of edges. Counterpart of `igraph_ecount()`.
552 ///
553 /// # Examples
554 ///
555 /// ```
556 /// use rust_igraph::Graph;
557 ///
558 /// let mut g = Graph::with_vertices(3);
559 /// g.add_edge(0, 1).unwrap();
560 /// g.add_edge(1, 2).unwrap();
561 /// assert_eq!(g.ecount(), 2);
562 /// ```
563 #[must_use]
564 pub fn ecount(&self) -> usize {
565 self.from.len()
566 }
567
568 /// `true` if the graph is directed. Counterpart of `igraph_is_directed()`.
569 ///
570 /// # Examples
571 ///
572 /// ```
573 /// use rust_igraph::Graph;
574 ///
575 /// let g = Graph::new(3, true).unwrap();
576 /// assert!(g.is_directed());
577 ///
578 /// let g2 = Graph::with_vertices(3);
579 /// assert!(!g2.is_directed());
580 /// ```
581 #[must_use]
582 pub fn is_directed(&self) -> bool {
583 self.directed
584 }
585
586 /// Iterator over vertex ids `0..vcount()`.
587 ///
588 /// # Examples
589 ///
590 /// ```
591 /// use rust_igraph::Graph;
592 ///
593 /// let g = Graph::with_vertices(4);
594 /// let ids: Vec<u32> = g.vertex_ids().collect();
595 /// assert_eq!(ids, vec![0, 1, 2, 3]);
596 /// ```
597 pub fn vertex_ids(&self) -> impl Iterator<Item = VertexId> {
598 0..self.n
599 }
600
601 /// Iterator over edge ids `0..ecount()`.
602 ///
603 /// # Examples
604 ///
605 /// ```
606 /// use rust_igraph::Graph;
607 ///
608 /// let mut g = Graph::with_vertices(3);
609 /// g.add_edge(0, 1).unwrap();
610 /// g.add_edge(1, 2).unwrap();
611 /// let ids: Vec<u32> = g.edge_ids().collect();
612 /// assert_eq!(ids, vec![0, 1]);
613 /// ```
614 pub fn edge_ids(&self) -> impl Iterator<Item = u32> {
615 let m = u32::try_from(self.from.len()).unwrap_or(u32::MAX);
616 0..m
617 }
618
619 /// Iterator over all edges as `(from, to)` pairs.
620 ///
621 /// Yields edges in edge-id order. For undirected graphs, `from <= to`
622 /// (canonicalised storage order).
623 ///
624 /// # Examples
625 ///
626 /// ```
627 /// use rust_igraph::Graph;
628 ///
629 /// let mut g = Graph::with_vertices(3);
630 /// g.add_edge(0, 1).unwrap();
631 /// g.add_edge(1, 2).unwrap();
632 /// let edges: Vec<(u32, u32)> = g.edges().collect();
633 /// assert_eq!(edges, vec![(0, 1), (1, 2)]);
634 /// ```
635 pub fn edges(&self) -> impl Iterator<Item = (VertexId, VertexId)> + '_ {
636 self.from.iter().zip(self.to.iter()).map(|(&u, &v)| (u, v))
637 }
638
639 /// Returns an iterator over edges as `(from, to)` pairs in edge-id order.
640 ///
641 /// This is the named-return counterpart to the `IntoIterator` impl
642 /// for `&Graph`, enabling `graph.iter().filter(...)` usage.
643 ///
644 /// # Examples
645 ///
646 /// ```
647 /// use rust_igraph::Graph;
648 ///
649 /// let mut g = Graph::with_vertices(3);
650 /// g.add_edge(0, 1).unwrap();
651 /// g.add_edge(1, 2).unwrap();
652 ///
653 /// let edges: Vec<_> = g.iter().collect();
654 /// assert_eq!(edges, vec![(0, 1), (1, 2)]);
655 /// ```
656 pub fn iter(&self) -> EdgeIter<'_> {
657 self.from.iter().zip(self.to.iter()).map(|(&a, &b)| (a, b))
658 }
659
660 /// Check whether an edge exists between `from` and `to`.
661 ///
662 /// On undirected graphs `(u, v)` and `(v, u)` are equivalent.
663 /// Returns `false` for out-of-range vertex ids rather than erroring.
664 ///
665 /// # Examples
666 ///
667 /// ```
668 /// use rust_igraph::Graph;
669 ///
670 /// let mut g = Graph::with_vertices(3);
671 /// g.add_edge(0, 1).unwrap();
672 /// assert!(g.has_edge(0, 1));
673 /// assert!(g.has_edge(1, 0)); // undirected
674 /// assert!(!g.has_edge(0, 2));
675 /// ```
676 pub fn has_edge(&self, from: VertexId, to: VertexId) -> bool {
677 self.find_eid(from, to).ok().flatten().is_some()
678 }
679
680 /// Append `nv` isolated vertices, returning the inclusive id range
681 /// `(first, last)` of the new vertices. If `nv == 0` returns
682 /// `(self.n, self.n)` and does nothing.
683 ///
684 /// Counterpart of `igraph_add_vertices()`.
685 ///
686 /// # Examples
687 ///
688 /// ```
689 /// use rust_igraph::Graph;
690 ///
691 /// let mut g = Graph::with_vertices(3);
692 /// let (first, last) = g.add_vertices(2).unwrap();
693 /// assert_eq!(first, 3);
694 /// assert_eq!(last, 4);
695 /// assert_eq!(g.vcount(), 5);
696 /// ```
697 pub fn add_vertices(&mut self, nv: u32) -> IgraphResult<(VertexId, VertexId)> {
698 let new_n = self
699 .n
700 .checked_add(nv)
701 .ok_or(IgraphError::Internal("vertex count overflow"))?;
702 let first = self.n;
703 // os/is grow by `nv` entries, all initialised to ecount.
704 let ec = u32::try_from(self.ecount())
705 .map_err(|_| IgraphError::Internal("edge count exceeds u32::MAX"))?;
706 for _ in 0..nv {
707 self.os.push(ec);
708 self.is.push(ec);
709 }
710 // Extend vertex attribute vectors with defaults.
711 for vals in self.vertex_attrs.values_mut() {
712 if let Some(first_val) = vals.first() {
713 let default = first_val.default_for_same_type();
714 vals.resize(new_n as usize, default);
715 }
716 }
717 self.n = new_n;
718 if nv > 0 {
719 invalidate_after_add_vertices(&self.cache);
720 }
721 Ok((first, new_n.saturating_sub(1)))
722 }
723
724 /// Add a single edge from `u` to `v`.
725 ///
726 /// Self-loops and parallel edges are allowed. For undirected graphs the
727 /// edge is canonicalised so the stored `from <= to`.
728 ///
729 /// # Examples
730 ///
731 /// ```
732 /// use rust_igraph::Graph;
733 ///
734 /// let mut g = Graph::with_vertices(3);
735 /// g.add_edge(0, 1).unwrap();
736 /// g.add_edge(1, 2).unwrap();
737 /// assert_eq!(g.ecount(), 2);
738 /// ```
739 pub fn add_edge(&mut self, u: VertexId, v: VertexId) -> IgraphResult<()> {
740 self.add_edges(std::iter::once((u, v)))
741 }
742
743 /// Add a sequence of edges. After all edges are appended, the indexes
744 /// (`oi` / `ii` / `os` / `is`) are rebuilt in one pass — counterpart of
745 /// `igraph_add_edges` (`type_indexededgelist.c:254-367`).
746 ///
747 /// # Examples
748 ///
749 /// ```
750 /// use rust_igraph::Graph;
751 ///
752 /// let mut g = Graph::with_vertices(4);
753 /// g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
754 /// assert_eq!(g.ecount(), 3);
755 /// ```
756 pub fn add_edges<I>(&mut self, edges: I) -> IgraphResult<()>
757 where
758 I: IntoIterator<Item = (VertexId, VertexId)>,
759 {
760 let m_before = self.ecount();
761 for (u, v) in edges {
762 self.check_vertex(u)?;
763 self.check_vertex(v)?;
764 if !self.directed && u > v {
765 self.from.push(v);
766 self.to.push(u);
767 } else {
768 self.from.push(u);
769 self.to.push(v);
770 }
771 }
772 self.rebuild_indexes()?;
773 let m_after = self.ecount();
774 // Extend edge attribute vectors with defaults.
775 if m_after > m_before {
776 for vals in self.edge_attrs.values_mut() {
777 if let Some(first_val) = vals.first() {
778 let default = first_val.default_for_same_type();
779 vals.resize(m_after, default);
780 }
781 }
782 invalidate_after_add_edges(&self.cache);
783 }
784 Ok(())
785 }
786
787 /// Out-edge neighbour iterator for vertex `v`.
788 ///
789 /// For undirected graphs this returns *all* neighbours (since the
790 /// indexing tracks both endpoints symmetrically). Order is the upstream
791 /// igraph order — edges are visited in `oi` order, then `ii` order, with
792 /// duplicates suppressed when the same edge is incident on both.
793 ///
794 /// Counterpart of `igraph_neighbors(graph, _, vid, IGRAPH_ALL, ...)`.
795 ///
796 /// # Examples
797 ///
798 /// ```
799 /// use rust_igraph::Graph;
800 ///
801 /// let mut g = Graph::with_vertices(4);
802 /// g.add_edge(0, 1).unwrap();
803 /// g.add_edge(0, 2).unwrap();
804 /// g.add_edge(0, 3).unwrap();
805 /// let neis = g.neighbors(0).unwrap();
806 /// assert_eq!(neis, vec![1, 2, 3]);
807 /// ```
808 pub fn neighbors(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
809 self.check_vertex(v)?;
810 let v_idx = v as usize;
811 if self.directed {
812 // Directed: only outgoing neighbours; oi sorted by (from, to)
813 // so the out-neighbour list is already sorted ascending.
814 let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
815 let out: Vec<VertexId> = self.oi[out_range]
816 .iter()
817 .map(|&e| self.to[e as usize])
818 .collect();
819 Ok(out)
820 } else {
821 // Undirected: merge the two already-sorted sublists from oi
822 // (out-side, ascending in `to`) and ii (in-side, ascending
823 // in `from`) into one ascending neighbour list. Matches
824 // upstream `igraph_neighbors(_, _, _, IGRAPH_ALL)` and
825 // python-igraph's `Graph.neighbors(v)` exactly.
826 let out_start = self.os[v_idx] as usize;
827 let out_end = self.os[v_idx + 1] as usize;
828 let in_start = self.is[v_idx] as usize;
829 let in_end = self.is[v_idx + 1] as usize;
830 let mut out = Vec::with_capacity((out_end - out_start) + (in_end - in_start));
831 let mut out_idx = out_start;
832 let mut in_idx = in_start;
833 while out_idx < out_end && in_idx < in_end {
834 let a = self.to[self.oi[out_idx] as usize];
835 let b = self.from[self.ii[in_idx] as usize];
836 if a <= b {
837 out.push(a);
838 out_idx += 1;
839 } else {
840 out.push(b);
841 in_idx += 1;
842 }
843 }
844 while out_idx < out_end {
845 out.push(self.to[self.oi[out_idx] as usize]);
846 out_idx += 1;
847 }
848 while in_idx < in_end {
849 out.push(self.from[self.ii[in_idx] as usize]);
850 in_idx += 1;
851 }
852 Ok(out)
853 }
854 }
855
856 /// Zero-allocation iterator over the neighbors of vertex `v`.
857 ///
858 /// For directed graphs, yields out-neighbors in ascending order.
859 /// For undirected graphs, yields all neighbors in ascending order
860 /// (merged from out-edge and in-edge sublists without allocation).
861 ///
862 /// Prefer this over [`Graph::neighbors`] in hot loops where avoiding
863 /// a `Vec` allocation matters.
864 ///
865 /// # Examples
866 ///
867 /// ```
868 /// use rust_igraph::Graph;
869 ///
870 /// let mut g = Graph::with_vertices(4);
871 /// g.add_edge(0, 1).unwrap();
872 /// g.add_edge(0, 2).unwrap();
873 /// g.add_edge(0, 3).unwrap();
874 /// let neis: Vec<u32> = g.neighbors_iter(0).unwrap().collect();
875 /// assert_eq!(neis, vec![1, 2, 3]);
876 /// ```
877 pub fn neighbors_iter(&self, v: VertexId) -> IgraphResult<NeighborsIter<'_>> {
878 self.check_vertex(v)?;
879 let v_idx = v as usize;
880 let out_pos = self.os[v_idx] as usize;
881 let out_end = self.os[v_idx + 1] as usize;
882 let (in_pos, in_end) = if self.directed {
883 (0, 0)
884 } else {
885 (self.is[v_idx] as usize, self.is[v_idx + 1] as usize)
886 };
887 Ok(NeighborsIter {
888 graph: self,
889 out_pos,
890 out_end,
891 in_pos,
892 in_end,
893 directed: self.directed,
894 })
895 }
896
897 /// Convert the graph to an adjacency list representation.
898 ///
899 /// Returns a `Vec<Vec<u32>>` where `result[v]` contains the neighbors
900 /// of vertex `v`. For directed graphs, returns out-neighbors.
901 ///
902 /// For undirected graphs, each edge `(u, v)` causes `v` to appear in
903 /// `result[u]` and `u` to appear in `result[v]`.
904 ///
905 /// # Examples
906 ///
907 /// ```
908 /// use rust_igraph::Graph;
909 ///
910 /// let mut g = Graph::with_vertices(3);
911 /// g.add_edge(0, 1).unwrap();
912 /// g.add_edge(1, 2).unwrap();
913 /// let adj = g.to_adjacency_list().unwrap();
914 /// assert_eq!(adj[0], vec![1]);
915 /// assert_eq!(adj[1], vec![0, 2]);
916 /// assert_eq!(adj[2], vec![1]);
917 /// ```
918 pub fn to_adjacency_list(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
919 let n = self.vcount();
920 let mut adj = vec![Vec::new(); n as usize];
921 for v in 0..n {
922 adj[v as usize] = self.neighbors(v)?;
923 }
924 Ok(adj)
925 }
926
927 /// Return the adjacency matrix as a dense `n × n` matrix of `f64`.
928 ///
929 /// Entry `[i][j]` is the number of edges from vertex `i` to vertex `j`.
930 /// For undirected graphs the matrix is symmetric. Self-loops contribute
931 /// 1 to `[i][i]` (not 2).
932 ///
933 /// # Examples
934 ///
935 /// ```
936 /// use rust_igraph::Graph;
937 ///
938 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
939 /// let m = g.to_adjacency_matrix();
940 /// assert_eq!(m[0][1], 1.0);
941 /// assert_eq!(m[1][0], 1.0);
942 /// assert_eq!(m[0][2], 0.0);
943 /// ```
944 pub fn to_adjacency_matrix(&self) -> Vec<Vec<f64>> {
945 let n = self.n as usize;
946 let mut mat = vec![vec![0.0f64; n]; n];
947 for eid in 0..self.ecount() {
948 let u = self.from[eid] as usize;
949 let v = self.to[eid] as usize;
950 mat[u][v] += 1.0;
951 if !self.directed && u != v {
952 mat[v][u] += 1.0;
953 }
954 }
955 mat
956 }
957
958 /// Degree of vertex `v` — number of edges incident to it.
959 ///
960 /// On undirected graphs every edge counts once except a self-loop which
961 /// counts twice (matches upstream igraph's `IGRAPH_LOOPS = TWICE` default
962 /// at `type_indexededgelist.c:1162`).
963 ///
964 /// Counterpart of `igraph_degree_1(_, _, _, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)`.
965 ///
966 /// # Examples
967 ///
968 /// ```
969 /// use rust_igraph::Graph;
970 ///
971 /// let mut g = Graph::with_vertices(3);
972 /// g.add_edge(0, 1).unwrap();
973 /// g.add_edge(0, 2).unwrap();
974 /// assert_eq!(g.degree(0).unwrap(), 2);
975 /// assert_eq!(g.degree(1).unwrap(), 1);
976 /// ```
977 pub fn degree(&self, v: VertexId) -> IgraphResult<usize> {
978 self.check_vertex(v)?;
979 let v_idx = v as usize;
980 let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
981 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
982 Ok(out + in_count)
983 }
984
985 /// Out-degree of vertex `v` (number of outgoing edges).
986 ///
987 /// For undirected graphs, this equals the total degree.
988 ///
989 /// # Examples
990 ///
991 /// ```
992 /// use rust_igraph::Graph;
993 ///
994 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,0)], true, None).unwrap();
995 /// assert_eq!(g.out_degree(0).unwrap(), 2);
996 /// assert_eq!(g.out_degree(1).unwrap(), 1);
997 /// ```
998 pub fn out_degree(&self, v: VertexId) -> IgraphResult<usize> {
999 self.check_vertex(v)?;
1000 let v_idx = v as usize;
1001 if self.directed {
1002 Ok((self.os[v_idx + 1] - self.os[v_idx]) as usize)
1003 } else {
1004 let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
1005 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
1006 Ok(out + in_count)
1007 }
1008 }
1009
1010 /// In-degree of vertex `v` (number of incoming edges).
1011 ///
1012 /// For undirected graphs, this equals the total degree.
1013 ///
1014 /// # Examples
1015 ///
1016 /// ```
1017 /// use rust_igraph::Graph;
1018 ///
1019 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,0)], true, None).unwrap();
1020 /// assert_eq!(g.in_degree(0).unwrap(), 1);
1021 /// assert_eq!(g.in_degree(1).unwrap(), 1);
1022 /// assert_eq!(g.in_degree(2).unwrap(), 1);
1023 /// ```
1024 pub fn in_degree(&self, v: VertexId) -> IgraphResult<usize> {
1025 self.check_vertex(v)?;
1026 let v_idx = v as usize;
1027 if self.directed {
1028 Ok((self.is[v_idx + 1] - self.is[v_idx]) as usize)
1029 } else {
1030 let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
1031 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
1032 Ok(out + in_count)
1033 }
1034 }
1035
1036 /// Maximum degree across all vertices (total degree for undirected,
1037 /// out-degree for directed). Returns 0 for empty graphs.
1038 ///
1039 /// Counterpart of `igraph_maxdegree()`.
1040 ///
1041 /// # Examples
1042 ///
1043 /// ```
1044 /// use rust_igraph::Graph;
1045 ///
1046 /// let mut g = Graph::with_vertices(4);
1047 /// g.add_edge(0, 1).unwrap();
1048 /// g.add_edge(0, 2).unwrap();
1049 /// g.add_edge(0, 3).unwrap();
1050 /// assert_eq!(g.max_degree(), 3);
1051 /// ```
1052 pub fn max_degree(&self) -> usize {
1053 let n = self.vcount();
1054 if n == 0 {
1055 return 0;
1056 }
1057 (0..n)
1058 .map(|v| {
1059 let v_idx = v as usize;
1060 let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
1061 let inc = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
1062 if self.directed { out } else { out + inc }
1063 })
1064 .max()
1065 .unwrap_or(0)
1066 }
1067
1068 /// Minimum degree across all vertices (total degree for undirected,
1069 /// out-degree for directed). Returns 0 for empty graphs.
1070 ///
1071 /// Counterpart of `igraph_mindegree()` (custom extension).
1072 ///
1073 /// # Examples
1074 ///
1075 /// ```
1076 /// use rust_igraph::Graph;
1077 ///
1078 /// let mut g = Graph::with_vertices(4);
1079 /// g.add_edge(0, 1).unwrap();
1080 /// g.add_edge(0, 2).unwrap();
1081 /// // vertex 3 has degree 0
1082 /// assert_eq!(g.min_degree(), 0);
1083 /// ```
1084 pub fn min_degree(&self) -> usize {
1085 let n = self.vcount();
1086 if n == 0 {
1087 return 0;
1088 }
1089 (0..n)
1090 .map(|v| {
1091 let v_idx = v as usize;
1092 let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
1093 let inc = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
1094 if self.directed { out } else { out + inc }
1095 })
1096 .min()
1097 .unwrap_or(0)
1098 }
1099
1100 // ---------------------------------------------------------------
1101 // ALGO-CORE-001b: edge-id helpers + incident edges.
1102 // ---------------------------------------------------------------
1103
1104 /// Source endpoint of edge `eid`. Counterpart of `IGRAPH_FROM`
1105 /// (`igraph_interface.h:115`).
1106 ///
1107 /// # Examples
1108 ///
1109 /// ```
1110 /// use rust_igraph::Graph;
1111 ///
1112 /// let mut g = Graph::with_vertices(3);
1113 /// g.add_edge(0, 2).unwrap();
1114 /// assert_eq!(g.edge_source(0).unwrap(), 0);
1115 /// ```
1116 pub fn edge_source(&self, eid: EdgeId) -> IgraphResult<VertexId> {
1117 self.check_edge(eid)?;
1118 Ok(self.from[eid as usize])
1119 }
1120
1121 /// Target endpoint of edge `eid`. Counterpart of `IGRAPH_TO`
1122 /// (`igraph_interface.h:128`).
1123 ///
1124 /// # Examples
1125 ///
1126 /// ```
1127 /// use rust_igraph::Graph;
1128 ///
1129 /// let mut g = Graph::with_vertices(3);
1130 /// g.add_edge(0, 2).unwrap();
1131 /// assert_eq!(g.edge_target(0).unwrap(), 2);
1132 /// ```
1133 pub fn edge_target(&self, eid: EdgeId) -> IgraphResult<VertexId> {
1134 self.check_edge(eid)?;
1135 Ok(self.to[eid as usize])
1136 }
1137
1138 /// Both endpoints of edge `eid`, ordered as `(from, to)`. Counterpart
1139 /// of `igraph_edge` (`igraph_interface.h:71`).
1140 ///
1141 /// # Examples
1142 ///
1143 /// ```
1144 /// use rust_igraph::Graph;
1145 ///
1146 /// let mut g = Graph::with_vertices(3);
1147 /// g.add_edge(0, 1).unwrap();
1148 /// let (from, to) = g.edge(0).unwrap();
1149 /// assert_eq!(from, 0);
1150 /// assert_eq!(to, 1);
1151 /// ```
1152 pub fn edge(&self, eid: EdgeId) -> IgraphResult<(VertexId, VertexId)> {
1153 self.check_edge(eid)?;
1154 let i = eid as usize;
1155 Ok((self.from[i], self.to[i]))
1156 }
1157
1158 /// The other endpoint of `eid` given one endpoint `vid`. Counterpart
1159 /// of `IGRAPH_OTHER` (`igraph_interface.h:145`). Errors if `vid` is
1160 /// not actually an endpoint of `eid`.
1161 ///
1162 /// # Examples
1163 ///
1164 /// ```
1165 /// use rust_igraph::Graph;
1166 ///
1167 /// let mut g = Graph::with_vertices(3);
1168 /// g.add_edge(0, 2).unwrap();
1169 /// assert_eq!(g.edge_other(0, 0).unwrap(), 2);
1170 /// assert_eq!(g.edge_other(0, 2).unwrap(), 0);
1171 /// ```
1172 pub fn edge_other(&self, eid: EdgeId, vid: VertexId) -> IgraphResult<VertexId> {
1173 let (u, v) = self.edge(eid)?;
1174 if vid == u {
1175 Ok(v)
1176 } else if vid == v {
1177 Ok(u)
1178 } else {
1179 Err(IgraphError::InvalidArgument(format!(
1180 "vertex {vid} is not an endpoint of edge {eid} ({u}, {v})"
1181 )))
1182 }
1183 }
1184
1185 /// Edge ids incident to vertex `v`, in the same iteration order as
1186 /// [`Graph::neighbors`].
1187 ///
1188 /// For undirected graphs returns the union of out-side (`oi`) and
1189 /// in-side (`ii`) edges — every edge incident to `v` once, except
1190 /// self-loops which appear twice (matching `igraph_neighbors` /
1191 /// `igraph_degree`'s `IGRAPH_LOOPS_TWICE` default at
1192 /// `type_indexededgelist.c:1162`).
1193 ///
1194 /// For directed graphs returns out-edges only, mirroring this AWU's
1195 /// `neighbors()` choice. (The full mode-aware variant lands later
1196 /// alongside `igraph_neighbors(mode = IN/OUT/ALL)`.)
1197 ///
1198 /// Counterpart of `igraph_incident(_, _, v, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)`
1199 /// for undirected; `IGRAPH_OUT` mode for directed.
1200 ///
1201 /// # Examples
1202 ///
1203 /// ```
1204 /// use rust_igraph::Graph;
1205 ///
1206 /// let mut g = Graph::with_vertices(3);
1207 /// g.add_edge(0, 1).unwrap(); // edge 0
1208 /// g.add_edge(0, 2).unwrap(); // edge 1
1209 /// let inc = g.incident(0).unwrap();
1210 /// assert_eq!(inc.len(), 2);
1211 /// ```
1212 pub fn incident(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
1213 self.check_vertex(v)?;
1214 let v_idx = v as usize;
1215 let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
1216 if self.directed {
1217 Ok(self.oi[out_range].to_vec())
1218 } else {
1219 let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
1220 let mut out = Vec::with_capacity(out_range.len() + in_range.len());
1221 out.extend_from_slice(&self.oi[out_range]);
1222 out.extend_from_slice(&self.ii[in_range]);
1223 Ok(out)
1224 }
1225 }
1226
1227 /// Companion to [`incident`](Self::incident): returns *only* the
1228 /// edges incoming to `v` for directed graphs. For undirected
1229 /// graphs the result is identical to `incident` (every edge is
1230 /// bidirectional).
1231 ///
1232 /// Counterpart of `igraph_incident(_, _, v, IGRAPH_IN, IGRAPH_LOOPS_TWICE)`.
1233 pub(crate) fn incident_in(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
1234 self.check_vertex(v)?;
1235 let v_idx = v as usize;
1236 if self.directed {
1237 let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
1238 Ok(self.ii[in_range].to_vec())
1239 } else {
1240 self.incident(v)
1241 }
1242 }
1243
1244 /// Edge id between `from` and `to`, if any.
1245 ///
1246 /// On undirected graphs `(u, v)` and `(v, u)` are equivalent.
1247 /// On directed graphs the search follows the edge direction
1248 /// `from -> to`. Returns [`crate::IgraphError::InvalidArgument`]
1249 /// when no such edge exists; for the "no error, return None" variant
1250 /// use [`Self::find_eid`].
1251 ///
1252 /// Counterpart of
1253 /// `igraph_get_eid(_, _, from, to, /*directed=*/true, /*error=*/true)`
1254 /// from `references/igraph/src/graph/type_indexededgelist.c:1522-1555`.
1255 /// Phase-1 minimal slice: linear scan across the from-bucket; the
1256 /// upstream binary-search optimisation lands in a perf pass.
1257 ///
1258 /// # Examples
1259 ///
1260 /// ```
1261 /// use rust_igraph::Graph;
1262 ///
1263 /// let mut g = Graph::with_vertices(3);
1264 /// g.add_edge(0, 1).unwrap();
1265 /// g.add_edge(1, 2).unwrap();
1266 /// assert_eq!(g.get_eid(0, 1).unwrap(), 0);
1267 /// assert_eq!(g.get_eid(1, 2).unwrap(), 1);
1268 /// assert!(g.get_eid(0, 2).is_err());
1269 /// ```
1270 pub fn get_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<EdgeId> {
1271 self.find_eid(from, to)?
1272 .ok_or_else(|| IgraphError::InvalidArgument(format!("no edge between {from} and {to}")))
1273 }
1274
1275 /// Edge id between `from` and `to`, or `None` if not connected.
1276 ///
1277 /// Same semantics as [`Self::get_eid`] but no-error variant
1278 /// matching upstream's `error=false` mode. When parallel edges
1279 /// exist, returns the lowest edge id (matching upstream's
1280 /// "always returns the same edge ID" guarantee).
1281 ///
1282 /// # Examples
1283 ///
1284 /// ```
1285 /// use rust_igraph::Graph;
1286 ///
1287 /// let mut g = Graph::with_vertices(3);
1288 /// g.add_edge(0, 1).unwrap();
1289 /// assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
1290 /// assert_eq!(g.find_eid(0, 2).unwrap(), None);
1291 /// ```
1292 pub fn find_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<Option<EdgeId>> {
1293 self.check_vertex(from)?;
1294 self.check_vertex(to)?;
1295 if self.directed {
1296 // Search out-bucket of `from` for `to[e] == to`.
1297 let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
1298 for &e in &self.oi[range] {
1299 if self.to[e as usize] == to {
1300 return Ok(Some(e));
1301 }
1302 }
1303 Ok(None)
1304 } else {
1305 // Undirected: edges canonicalised so `from[e] <= to[e]`.
1306 // Search the bucket of the smaller endpoint for the larger.
1307 let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
1308 let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
1309 for &e in &self.oi[range] {
1310 if self.to[e as usize] == hi {
1311 return Ok(Some(e));
1312 }
1313 }
1314 Ok(None)
1315 }
1316 }
1317
1318 /// All edge ids between `from` and `to`, including parallel edges
1319 /// and (for self-loops) the loop edge once.
1320 ///
1321 /// Counterpart of
1322 /// `igraph_get_all_eids_between()` from
1323 /// `references/igraph/src/graph/type_indexededgelist.c:~1700`.
1324 /// On undirected graphs `(u, v)` and `(v, u)` are equivalent. The
1325 /// returned vector is sorted ascending by edge id.
1326 ///
1327 /// # Examples
1328 ///
1329 /// ```
1330 /// use rust_igraph::Graph;
1331 ///
1332 /// let mut g = Graph::with_vertices(2);
1333 /// g.add_edge(0, 1).unwrap();
1334 /// g.add_edge(0, 1).unwrap(); // parallel edge
1335 /// let eids = g.get_all_eids_between(0, 1).unwrap();
1336 /// assert_eq!(eids, vec![0, 1]);
1337 /// ```
1338 pub fn get_all_eids_between(&self, from: VertexId, to: VertexId) -> IgraphResult<Vec<EdgeId>> {
1339 self.check_vertex(from)?;
1340 self.check_vertex(to)?;
1341 let mut out = Vec::new();
1342 if self.directed {
1343 let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
1344 for &e in &self.oi[range] {
1345 if self.to[e as usize] == to {
1346 out.push(e);
1347 }
1348 }
1349 } else {
1350 let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
1351 let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
1352 for &e in &self.oi[range] {
1353 if self.to[e as usize] == hi {
1354 out.push(e);
1355 }
1356 }
1357 }
1358 out.sort_unstable();
1359 Ok(out)
1360 }
1361
1362 /// Out-neighbours of `v` (always — directed or undirected). Each
1363 /// edge contributes one entry, in `oi[os[v]..os[v+1]]` order
1364 /// (lex by `(from, to)`). Self-loops appear once.
1365 ///
1366 /// Internal helper used by direction-aware algorithms (e.g.
1367 /// strongly connected components). The full mode-aware public
1368 /// surface ships with the next `igraph_neighbors` AWU.
1369 pub(crate) fn out_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
1370 self.check_vertex(v)?;
1371 let v_idx = v as usize;
1372 let range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
1373 Ok(self.oi[range]
1374 .iter()
1375 .map(|&e| self.to[e as usize])
1376 .collect())
1377 }
1378
1379 /// In-neighbours of `v` (always — directed or undirected). Each
1380 /// edge contributes one entry, in `ii[is[v]..is[v+1]]` order
1381 /// (lex by `(to, from)`). Self-loops appear once.
1382 ///
1383 /// Companion to [`out_neighbors_vec`](Self::out_neighbors_vec); see
1384 /// its doc for context on visibility.
1385 pub(crate) fn in_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
1386 self.check_vertex(v)?;
1387 let v_idx = v as usize;
1388 let range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
1389 Ok(self.ii[range]
1390 .iter()
1391 .map(|&e| self.from[e as usize])
1392 .collect())
1393 }
1394
1395 // ---------------------------------------------------------------
1396 // ALGO-CORE-001c: delete_edges + delete_vertices + delete_vertices_map.
1397 // ---------------------------------------------------------------
1398
1399 /// Remove the given edges from the graph.
1400 ///
1401 /// `edges` may contain the same id more than once — the second and
1402 /// later occurrences are no-ops. Remaining edges keep their
1403 /// pairwise relative order but are renumbered so edge ids stay
1404 /// contiguous starting at 0. Returns
1405 /// [`IgraphError::EdgeOutOfRange`] if any id is `>= ecount()`; on
1406 /// error the graph is left unchanged.
1407 ///
1408 /// Counterpart of `igraph_delete_edges`
1409 /// (`references/igraph/src/graph/type_indexededgelist.c:500`).
1410 ///
1411 /// # Examples
1412 ///
1413 /// ```
1414 /// use rust_igraph::Graph;
1415 ///
1416 /// let mut g = Graph::with_vertices(3);
1417 /// g.add_edge(0, 1).unwrap();
1418 /// g.add_edge(1, 2).unwrap();
1419 /// g.add_edge(0, 2).unwrap();
1420 /// g.delete_edges(&[1]).unwrap(); // remove edge 1-2
1421 /// assert_eq!(g.ecount(), 2);
1422 /// ```
1423 pub fn delete_edges(&mut self, edges: &[EdgeId]) -> IgraphResult<()> {
1424 let m = self.ecount();
1425 let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
1426
1427 // Validate up front so a bad id leaves graph state untouched.
1428 for &eid in edges {
1429 if (eid as usize) >= m {
1430 return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
1431 }
1432 }
1433 if edges.is_empty() {
1434 return Ok(());
1435 }
1436
1437 let mut remove = vec![false; m];
1438 for &eid in edges {
1439 remove[eid as usize] = true;
1440 }
1441
1442 let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
1443 let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
1444 for (e, &is_removed) in remove.iter().enumerate() {
1445 if !is_removed {
1446 new_from.push(self.from[e]);
1447 new_to.push(self.to[e]);
1448 }
1449 }
1450 // Filter edge attributes to match retained edges.
1451 for vals in self.edge_attrs.values_mut() {
1452 let mut new_vals = Vec::with_capacity(new_from.len());
1453 for (e, &is_removed) in remove.iter().enumerate() {
1454 if !is_removed {
1455 new_vals.push(vals[e].clone());
1456 }
1457 }
1458 *vals = new_vals;
1459 }
1460 self.from = new_from;
1461 self.to = new_to;
1462 self.rebuild_indexes()?;
1463 self.cache.invalidate_all();
1464 Ok(())
1465 }
1466
1467 /// Remove the given vertices and all their incident edges.
1468 ///
1469 /// `vertices` may repeat ids freely. Surviving vertices get
1470 /// renumbered so the new id space is `0..new_vcount` in their
1471 /// previous relative order. Returns
1472 /// [`IgraphError::VertexOutOfRange`] if any id is `>= vcount()`;
1473 /// on error the graph is left unchanged.
1474 ///
1475 /// Counterpart of `igraph_delete_vertices`
1476 /// (`references/igraph/src/graph/type_indexededgelist.c:540`).
1477 ///
1478 /// # Examples
1479 ///
1480 /// ```
1481 /// use rust_igraph::Graph;
1482 ///
1483 /// let mut g = Graph::with_vertices(4);
1484 /// g.add_edge(0, 1).unwrap();
1485 /// g.add_edge(1, 2).unwrap();
1486 /// g.add_edge(2, 3).unwrap();
1487 /// g.delete_vertices(&[1]).unwrap();
1488 /// assert_eq!(g.vcount(), 3);
1489 /// assert_eq!(g.ecount(), 1); // only edge 2-3 survives (renumbered)
1490 /// ```
1491 pub fn delete_vertices(&mut self, vertices: &[VertexId]) -> IgraphResult<()> {
1492 self.delete_vertices_map(vertices).map(|_| ())
1493 }
1494
1495 /// Like [`delete_vertices`](Self::delete_vertices), but also returns
1496 /// the old↔new vertex id mappings.
1497 ///
1498 /// Returns `(map, invmap)` where:
1499 /// - `map[old_id] == Some(new_id)` if the vertex was retained, else
1500 /// `None`. Length is the *original* vertex count.
1501 /// - `invmap[new_id] == old_id`. Length is the *new* vertex count.
1502 ///
1503 /// Counterpart of `igraph_delete_vertices_map`
1504 /// (`references/igraph/src/graph/type_indexededgelist.c:645`).
1505 ///
1506 /// # Examples
1507 ///
1508 /// ```
1509 /// use rust_igraph::Graph;
1510 ///
1511 /// let mut g = Graph::with_vertices(4);
1512 /// g.add_edge(0, 1).unwrap();
1513 /// g.add_edge(2, 3).unwrap();
1514 /// let (map, invmap) = g.delete_vertices_map(&[1, 2]).unwrap();
1515 /// assert_eq!(g.vcount(), 2);
1516 /// assert_eq!(map, vec![Some(0), None, None, Some(1)]);
1517 /// assert_eq!(invmap, vec![0, 3]);
1518 /// ```
1519 pub fn delete_vertices_map(
1520 &mut self,
1521 vertices: &[VertexId],
1522 ) -> IgraphResult<(Vec<Option<VertexId>>, Vec<VertexId>)> {
1523 let n_u32 = self.n;
1524 let n = n_u32 as usize;
1525
1526 // Validate first.
1527 for &vid in vertices {
1528 if vid >= n_u32 {
1529 return Err(IgraphError::VertexOutOfRange { id: vid, n: n_u32 });
1530 }
1531 }
1532
1533 let mut remove = vec![false; n];
1534 for &vid in vertices {
1535 remove[vid as usize] = true;
1536 }
1537
1538 // Build map (old → new) and invmap (new → old).
1539 let mut map: Vec<Option<VertexId>> = vec![None; n];
1540 let mut invmap: Vec<VertexId> = Vec::new();
1541 let mut next_new: u32 = 0;
1542 for (i, &is_removed) in remove.iter().enumerate() {
1543 if !is_removed {
1544 let i_u32 = u32::try_from(i)
1545 .map_err(|_| IgraphError::Internal("vertex index exceeds u32::MAX"))?;
1546 map[i] = Some(next_new);
1547 invmap.push(i_u32);
1548 next_new = next_new
1549 .checked_add(1)
1550 .ok_or(IgraphError::Internal("new vertex count overflow"))?;
1551 }
1552 }
1553
1554 // Filter edges: keep only those with both endpoints retained,
1555 // renumber endpoints via `map`.
1556 let m = self.ecount();
1557 let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
1558 let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
1559 let mut edge_keep = Vec::with_capacity(m);
1560 for (u, v) in self.from.iter().zip(self.to.iter()) {
1561 if let (Some(nu), Some(nv)) = (map[*u as usize], map[*v as usize]) {
1562 new_from.push(nu);
1563 new_to.push(nv);
1564 edge_keep.push(true);
1565 } else {
1566 edge_keep.push(false);
1567 }
1568 }
1569
1570 // Filter vertex attributes to match retained vertices.
1571 for vals in self.vertex_attrs.values_mut() {
1572 let new_vals: Vec<AttributeValue> = remove
1573 .iter()
1574 .enumerate()
1575 .filter(|&(_, is_removed)| !is_removed)
1576 .map(|(i, _)| vals[i].clone())
1577 .collect();
1578 *vals = new_vals;
1579 }
1580 // Filter edge attributes to match retained edges.
1581 for vals in self.edge_attrs.values_mut() {
1582 let new_vals: Vec<AttributeValue> = edge_keep
1583 .iter()
1584 .enumerate()
1585 .filter(|&(_, keep)| *keep)
1586 .map(|(i, _)| vals[i].clone())
1587 .collect();
1588 *vals = new_vals;
1589 }
1590
1591 self.n = next_new;
1592 self.from = new_from;
1593 self.to = new_to;
1594 self.rebuild_indexes()?;
1595 self.cache.invalidate_all();
1596
1597 Ok((map, invmap))
1598 }
1599
1600 /// Look up a cached boolean property without computing it.
1601 ///
1602 /// Returns `None` if the property has not been cached yet. Pair with
1603 /// [`Self::cache_set`] in compute functions:
1604 ///
1605 /// ```ignore
1606 /// if let Some(v) = g.cache_get(CachedProperty::IsDag) { return v; }
1607 /// let v = compute_is_dag(g);
1608 /// g.cache_set(CachedProperty::IsDag, v);
1609 /// v
1610 /// ```
1611 ///
1612 /// Counterpart of `igraph_i_property_cache_has` + `_get_bool` from
1613 /// `references/igraph/src/graph/caching.c`.
1614 ///
1615 /// ```
1616 /// use rust_igraph::{Graph, CachedProperty};
1617 ///
1618 /// let g = Graph::with_vertices(3);
1619 /// assert!(g.cache_get(CachedProperty::HasLoop).is_none());
1620 /// g.cache_set(CachedProperty::HasLoop, true);
1621 /// assert_eq!(g.cache_get(CachedProperty::HasLoop), Some(true));
1622 /// ```
1623 #[must_use]
1624 pub fn cache_get(&self, prop: CachedProperty) -> Option<bool> {
1625 self.cache.get(prop)
1626 }
1627
1628 /// Store the value of a cached boolean property.
1629 ///
1630 /// Takes `&self` (interior mutability via `Cell`) — populating the
1631 /// cache from a compute function is **not** considered a mutation of
1632 /// the graph, matching igraph C semantics where compute helpers take
1633 /// `const igraph_t *` and still write to the cache.
1634 ///
1635 /// Counterpart of `igraph_i_property_cache_set_bool`.
1636 pub fn cache_set(&self, prop: CachedProperty, value: bool) {
1637 self.cache.set(prop, value);
1638 }
1639
1640 /// Drop the cached value of a single property (no-op if not cached).
1641 ///
1642 /// Use this if you change the graph via a private path that doesn't
1643 /// go through `add_edges` / `delete_*`.
1644 ///
1645 /// Counterpart of `igraph_i_property_cache_invalidate`.
1646 ///
1647 /// ```
1648 /// use rust_igraph::{Graph, CachedProperty};
1649 ///
1650 /// let g = Graph::with_vertices(3);
1651 /// g.cache_set(CachedProperty::HasLoop, true);
1652 /// g.cache_invalidate(CachedProperty::HasLoop);
1653 /// assert!(g.cache_get(CachedProperty::HasLoop).is_none());
1654 /// ```
1655 pub fn cache_invalidate(&self, prop: CachedProperty) {
1656 self.cache.invalidate(prop);
1657 }
1658
1659 /// Drop every cached boolean property.
1660 ///
1661 /// Counterpart of `igraph_i_property_cache_invalidate_all`.
1662 ///
1663 /// ```
1664 /// use rust_igraph::{Graph, CachedProperty};
1665 ///
1666 /// let g = Graph::with_vertices(3);
1667 /// g.cache_set(CachedProperty::HasLoop, true);
1668 /// g.cache_invalidate_all();
1669 /// assert!(g.cache_get(CachedProperty::HasLoop).is_none());
1670 /// ```
1671 pub fn cache_invalidate_all(&self) {
1672 self.cache.invalidate_all();
1673 }
1674
1675 fn check_vertex(&self, v: VertexId) -> IgraphResult<()> {
1676 if v >= self.n {
1677 return Err(IgraphError::VertexOutOfRange { id: v, n: self.n });
1678 }
1679 Ok(())
1680 }
1681
1682 fn check_edge(&self, eid: EdgeId) -> IgraphResult<()> {
1683 let m = self.ecount();
1684 let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
1685 if (eid as usize) >= m {
1686 return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
1687 }
1688 Ok(())
1689 }
1690
1691 /// Recompute `oi`, `ii`, `os`, `is` from `from`/`to`. Called after
1692 /// any structural change.
1693 ///
1694 /// Each side does a stable lexicographic sort: `oi` orders edges by
1695 /// `(from[e], to[e])`, `ii` by `(to[e], from[e])`. Time complexity
1696 /// is `O(|V| + |E| log |E|)` (Rust stable sort) — same asymptotic
1697 /// as upstream's `igraph_vector_int_pair_order`.
1698 ///
1699 /// The within-bucket secondary sort matches upstream igraph; without
1700 /// it, `neighbors(v)` for an unsorted-edge-input graph diverges from
1701 /// `python-igraph`'s output and breaks DFS order parity. (Counted
1702 /// for an oracle-test failure during ALGO-TR-002 — see
1703 /// `tests/oracle.rs::dfs_small_synthetic_matches_python_igraph`.)
1704 ///
1705 /// Counterpart of `igraph_i_create_start_vectors` + the
1706 /// `igraph_vector_int_pair_order` calls in
1707 /// `type_indexededgelist.c:309-336`.
1708 fn rebuild_indexes(&mut self) -> IgraphResult<()> {
1709 let m = self.ecount();
1710 let n = self.n as usize;
1711
1712 // Build (primary_key, secondary_key, edge_id) tuples for each
1713 // side, sort them lexicographically, then extract edge ids and
1714 // the offset array.
1715
1716 // ---- Out-side: sort by (from, to). ----
1717 let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
1718 .map(|e| {
1719 Ok::<_, IgraphError>((
1720 self.from[e],
1721 self.to[e],
1722 u32::try_from(e)
1723 .map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
1724 ))
1725 })
1726 .collect::<Result<_, _>>()?;
1727 tuples.sort_unstable_by_key(|a| (a.0, a.1));
1728 self.oi = tuples.iter().map(|t| t.2).collect();
1729 // os[v] = number of entries with primary_key < v.
1730 self.os = vec![0u32; n + 1];
1731 for &(u, _, _) in &tuples {
1732 self.os[u as usize + 1] = self.os[u as usize + 1]
1733 .checked_add(1)
1734 .ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
1735 }
1736 for i in 1..=n {
1737 self.os[i] = self.os[i]
1738 .checked_add(self.os[i - 1])
1739 .ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
1740 }
1741
1742 // ---- In-side: sort by (to, from). ----
1743 let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
1744 .map(|e| {
1745 Ok::<_, IgraphError>((
1746 self.to[e],
1747 self.from[e],
1748 u32::try_from(e)
1749 .map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
1750 ))
1751 })
1752 .collect::<Result<_, _>>()?;
1753 tuples.sort_unstable_by_key(|a| (a.0, a.1));
1754 self.ii = tuples.iter().map(|t| t.2).collect();
1755 self.is = vec![0u32; n + 1];
1756 for &(v, _, _) in &tuples {
1757 self.is[v as usize + 1] = self.is[v as usize + 1]
1758 .checked_add(1)
1759 .ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
1760 }
1761 for i in 1..=n {
1762 self.is[i] = self.is[i]
1763 .checked_add(self.is[i - 1])
1764 .ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
1765 }
1766
1767 Ok(())
1768 }
1769}
1770
1771// — Convenience methods delegating to free-function algorithms —
1772
1773impl Graph {
1774 /// Compute the density of this graph.
1775 ///
1776 /// Density is the ratio of actual edges to possible edges.
1777 /// Returns `None` for graphs with fewer than 2 vertices.
1778 ///
1779 /// # Examples
1780 ///
1781 /// ```
1782 /// use rust_igraph::Graph;
1783 ///
1784 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
1785 /// let d = g.density().unwrap().unwrap();
1786 /// assert!((d - 1.0).abs() < 1e-10); // K_3 is fully connected
1787 /// ```
1788 pub fn density(&self) -> IgraphResult<Option<f64>> {
1789 crate::algorithms::properties::basic::density(self)
1790 }
1791
1792 /// Check whether the graph is connected.
1793 ///
1794 /// For directed graphs this checks weak connectivity by default.
1795 ///
1796 /// # Examples
1797 ///
1798 /// ```
1799 /// use rust_igraph::Graph;
1800 ///
1801 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
1802 /// assert!(g.is_connected().unwrap());
1803 /// ```
1804 pub fn is_connected(&self) -> IgraphResult<bool> {
1805 crate::algorithms::connectivity::is_connected::is_connected(
1806 self,
1807 crate::algorithms::connectivity::is_connected::ConnectednessMode::Weak,
1808 )
1809 }
1810
1811 /// Check whether the graph is simple (no self-loops, no multi-edges).
1812 ///
1813 /// # Examples
1814 ///
1815 /// ```
1816 /// use rust_igraph::Graph;
1817 ///
1818 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
1819 /// assert!(g.is_simple().unwrap());
1820 /// ```
1821 pub fn is_simple(&self) -> IgraphResult<bool> {
1822 crate::algorithms::properties::is_simple::is_simple(self)
1823 }
1824
1825 /// Compute connected components.
1826 ///
1827 /// # Examples
1828 ///
1829 /// ```
1830 /// use rust_igraph::Graph;
1831 ///
1832 /// let mut g = Graph::new(4, false).unwrap();
1833 /// g.add_edge(0, 1).unwrap();
1834 /// g.add_edge(2, 3).unwrap();
1835 /// let cc = g.connected_components().unwrap();
1836 /// assert_eq!(cc.count, 2);
1837 /// ```
1838 pub fn connected_components(
1839 &self,
1840 ) -> IgraphResult<crate::algorithms::connectivity::components::ConnectedComponents> {
1841 crate::algorithms::connectivity::components::connected_components(self)
1842 }
1843
1844 /// Compute `PageRank` centrality for all vertices.
1845 ///
1846 /// Uses the default damping factor (0.85).
1847 ///
1848 /// # Examples
1849 ///
1850 /// ```
1851 /// use rust_igraph::Graph;
1852 ///
1853 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
1854 /// let pr = g.pagerank().unwrap();
1855 /// assert_eq!(pr.len(), 3);
1856 /// ```
1857 pub fn pagerank(&self) -> IgraphResult<Vec<f64>> {
1858 crate::algorithms::properties::pagerank::pagerank(self)
1859 }
1860
1861 /// Compute betweenness centrality for all vertices.
1862 ///
1863 /// # Examples
1864 ///
1865 /// ```
1866 /// use rust_igraph::Graph;
1867 ///
1868 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
1869 /// let bc = g.betweenness().unwrap();
1870 /// // Middle vertices have higher betweenness
1871 /// assert!(bc[1] > bc[0]);
1872 /// ```
1873 pub fn betweenness(&self) -> IgraphResult<Vec<f64>> {
1874 crate::algorithms::properties::betweenness::betweenness(self)
1875 }
1876
1877 /// Compute closeness centrality for all vertices.
1878 ///
1879 /// For each vertex, closeness is the reciprocal of the average shortest
1880 /// path distance to all reachable vertices. Returns `None` for isolated
1881 /// vertices.
1882 ///
1883 /// # Examples
1884 ///
1885 /// ```
1886 /// use rust_igraph::Graph;
1887 ///
1888 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
1889 /// let cl = g.closeness().unwrap();
1890 /// assert_eq!(cl.len(), 4);
1891 /// // Middle vertices have higher closeness
1892 /// assert!(cl[1].unwrap() > cl[0].unwrap());
1893 /// ```
1894 pub fn closeness(&self) -> IgraphResult<Vec<Option<f64>>> {
1895 crate::algorithms::properties::closeness::closeness(self)
1896 }
1897
1898 /// Compute eigenvector centrality for all vertices.
1899 ///
1900 /// # Examples
1901 ///
1902 /// ```
1903 /// use rust_igraph::Graph;
1904 ///
1905 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
1906 /// let ec = g.eigenvector_centrality().unwrap();
1907 /// assert_eq!(ec.len(), 3);
1908 /// ```
1909 pub fn eigenvector_centrality(&self) -> IgraphResult<Vec<f64>> {
1910 crate::algorithms::properties::eigenvector::eigenvector_centrality(self)
1911 }
1912
1913 /// Compute per-vertex local clustering coefficients.
1914 ///
1915 /// Returns the fraction of actual edges between each vertex's neighbours
1916 /// out of all possible edges. Vertices with fewer than 2 neighbours
1917 /// return `None`.
1918 ///
1919 /// # Examples
1920 ///
1921 /// ```
1922 /// use rust_igraph::Graph;
1923 ///
1924 /// // A triangle: all vertices have clustering coefficient 1.0
1925 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
1926 /// let cc = g.clustering_coefficients().unwrap();
1927 /// assert!((cc[0].unwrap() - 1.0).abs() < 1e-10);
1928 /// ```
1929 pub fn clustering_coefficients(&self) -> IgraphResult<Vec<Option<f64>>> {
1930 crate::algorithms::properties::triangles::transitivity_local_undirected(self)
1931 }
1932
1933 /// Compute the complement graph.
1934 ///
1935 /// The complement has the same vertices but edges wherever the original
1936 /// does not (excluding self-loops by default).
1937 ///
1938 /// # Examples
1939 ///
1940 /// ```
1941 /// use rust_igraph::Graph;
1942 ///
1943 /// let g = Graph::from_edges(&[(0,1)], false, Some(3)).unwrap();
1944 /// let c = g.complement().unwrap();
1945 /// // K_3 has 3 edges; original has 1; complement has 2
1946 /// assert_eq!(c.ecount(), 2);
1947 /// ```
1948 pub fn complement(&self) -> IgraphResult<Graph> {
1949 crate::algorithms::operators::complementer::complementer(self, false)
1950 }
1951
1952 /// Construct the line graph L(G).
1953 ///
1954 /// The line graph has one vertex per edge of this graph. Two vertices
1955 /// in L(G) are adjacent iff the corresponding edges share an endpoint.
1956 ///
1957 /// # Examples
1958 ///
1959 /// ```
1960 /// use rust_igraph::Graph;
1961 ///
1962 /// let mut g = Graph::with_vertices(3);
1963 /// g.add_edge(0, 1).unwrap();
1964 /// g.add_edge(1, 2).unwrap();
1965 /// g.add_edge(2, 0).unwrap();
1966 /// let lg = g.line_graph().unwrap();
1967 /// assert_eq!(lg.vcount(), 3);
1968 /// assert_eq!(lg.ecount(), 3);
1969 /// ```
1970 pub fn line_graph(&self) -> IgraphResult<Graph> {
1971 crate::algorithms::operators::line_graph::line_graph(self)
1972 }
1973
1974 /// Detect communities using the Louvain algorithm.
1975 ///
1976 /// # Examples
1977 ///
1978 /// ```
1979 /// use rust_igraph::Graph;
1980 ///
1981 /// let g = Graph::from_edges(
1982 /// &[(0,1), (0,2), (1,2), (3,4), (3,5), (4,5), (2,3)],
1983 /// false, None,
1984 /// ).unwrap();
1985 /// let result = g.louvain().unwrap();
1986 /// assert!(result.modularity > 0.0);
1987 /// ```
1988 pub fn louvain(&self) -> IgraphResult<crate::algorithms::community::louvain::LouvainResult> {
1989 crate::algorithms::community::louvain::louvain(self)
1990 }
1991
1992 /// Detect communities using the Leiden algorithm.
1993 ///
1994 /// Leiden improves upon Louvain by guaranteeing well-connected communities
1995 /// and avoiding the "poorly connected community" pathology.
1996 ///
1997 /// # Examples
1998 ///
1999 /// ```
2000 /// use rust_igraph::Graph;
2001 ///
2002 /// let g = Graph::from_edges(
2003 /// &[(0,1), (0,2), (1,2), (3,4), (3,5), (4,5), (2,3)],
2004 /// false, None,
2005 /// ).unwrap();
2006 /// let result = g.leiden().unwrap();
2007 /// assert!(result.quality > 0.0);
2008 /// ```
2009 pub fn leiden(&self) -> IgraphResult<crate::algorithms::community::leiden::LeidenResult> {
2010 crate::algorithms::community::leiden::leiden(self)
2011 }
2012
2013 /// Find all bridge edges (edges whose removal disconnects the graph).
2014 ///
2015 /// # Examples
2016 ///
2017 /// ```
2018 /// use rust_igraph::Graph;
2019 ///
2020 /// // 0-1-2 with 1-2 as bridge vs. 0-1, 0-2, 1-2 (triangle, no bridges)
2021 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
2022 /// let br = g.bridges().unwrap();
2023 /// assert_eq!(br.len(), 2); // both edges are bridges in a path
2024 /// ```
2025 pub fn bridges(&self) -> IgraphResult<Vec<EdgeId>> {
2026 crate::algorithms::connectivity::bridges::bridges(self)
2027 }
2028
2029 /// Compute the k-core decomposition (coreness of each vertex).
2030 ///
2031 /// The coreness of a vertex is the largest `k` such that the vertex
2032 /// belongs to a k-core — a maximal subgraph where every vertex has
2033 /// degree at least `k`.
2034 ///
2035 /// # Examples
2036 ///
2037 /// ```
2038 /// use rust_igraph::Graph;
2039 ///
2040 /// // Triangle (0,1,2) plus pendant vertex 3 attached to 0
2041 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0), (0,3)], false, None).unwrap();
2042 /// let cores = g.coreness().unwrap();
2043 /// assert_eq!(cores[0], 2); // part of the triangle
2044 /// assert_eq!(cores[3], 1); // pendant
2045 /// ```
2046 pub fn coreness(&self) -> IgraphResult<Vec<u32>> {
2047 crate::algorithms::properties::coreness::coreness(self)
2048 }
2049
2050 /// Create the induced subgraph on the given vertex set.
2051 ///
2052 /// # Examples
2053 ///
2054 /// ```
2055 /// use rust_igraph::Graph;
2056 ///
2057 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3), (3,0)], false, None).unwrap();
2058 /// let sub = g.induced_subgraph(&[0, 1, 2]).unwrap();
2059 /// assert_eq!(sub.graph.vcount(), 3);
2060 /// assert_eq!(sub.graph.ecount(), 2); // edges 0-1 and 1-2
2061 /// ```
2062 pub fn induced_subgraph(
2063 &self,
2064 vertices: &[VertexId],
2065 ) -> IgraphResult<crate::algorithms::operators::induced_subgraph::InducedSubgraphResult> {
2066 crate::algorithms::operators::induced_subgraph::induced_subgraph(self, vertices)
2067 }
2068
2069 /// Export the graph in DOT (Graphviz) format as a string.
2070 ///
2071 /// # Examples
2072 ///
2073 /// ```
2074 /// use rust_igraph::Graph;
2075 ///
2076 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
2077 /// let dot = g.to_dot(None).unwrap();
2078 /// assert!(dot.contains("--"));
2079 /// ```
2080 pub fn to_dot(&self, labels: Option<&[String]>) -> IgraphResult<String> {
2081 let mut buf = Vec::new();
2082 crate::algorithms::io::dot::write_dot(self, labels, &mut buf)?;
2083 String::from_utf8(buf).map_err(|e| {
2084 IgraphError::InvalidArgument(format!("DOT output is not valid UTF-8: {e}"))
2085 })
2086 }
2087
2088 /// BFS traversal from a root vertex, returning visit order.
2089 ///
2090 /// # Examples
2091 ///
2092 /// ```
2093 /// use rust_igraph::Graph;
2094 ///
2095 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,3)], false, None).unwrap();
2096 /// let order = g.bfs(0).unwrap();
2097 /// assert_eq!(order[0], 0);
2098 /// assert_eq!(order.len(), 4);
2099 /// ```
2100 pub fn bfs(&self, root: VertexId) -> IgraphResult<Vec<VertexId>> {
2101 crate::algorithms::traversal::bfs::bfs(self, root)
2102 }
2103
2104 /// DFS traversal from a root vertex, returning visit order.
2105 ///
2106 /// # Examples
2107 ///
2108 /// ```
2109 /// use rust_igraph::Graph;
2110 ///
2111 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,3)], false, None).unwrap();
2112 /// let order = g.dfs(0).unwrap();
2113 /// assert_eq!(order[0], 0);
2114 /// assert_eq!(order.len(), 4);
2115 /// ```
2116 pub fn dfs(&self, root: VertexId) -> IgraphResult<Vec<VertexId>> {
2117 crate::algorithms::traversal::dfs::dfs(self, root)
2118 }
2119
2120 /// Unweighted shortest paths from a source to all reachable vertices.
2121 ///
2122 /// Returns a vector of paths (each path is a `Vec<VertexId>`).
2123 ///
2124 /// # Examples
2125 ///
2126 /// ```
2127 /// use rust_igraph::Graph;
2128 ///
2129 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
2130 /// let paths = g.shortest_paths(0).unwrap();
2131 /// assert_eq!(paths[3], vec![0, 1, 2, 3]);
2132 /// ```
2133 pub fn shortest_paths(&self, source: VertexId) -> IgraphResult<Vec<Vec<VertexId>>> {
2134 crate::algorithms::paths::shortest_paths::get_shortest_paths(self, source)
2135 }
2136
2137 /// Weighted shortest-path distances from a source (Dijkstra).
2138 ///
2139 /// Returns distances to all vertices; `None` for unreachable vertices.
2140 ///
2141 /// # Examples
2142 ///
2143 /// ```
2144 /// use rust_igraph::Graph;
2145 ///
2146 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
2147 /// let weights = vec![1.0, 2.0, 3.0];
2148 /// let dist = g.dijkstra(0, &weights).unwrap();
2149 /// assert!((dist[3].unwrap() - 6.0).abs() < 1e-10);
2150 /// ```
2151 pub fn dijkstra(&self, source: VertexId, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
2152 crate::algorithms::paths::dijkstra::dijkstra_distances(self, source, weights)
2153 }
2154
2155 /// Compute the degree sequence (degree of each vertex).
2156 ///
2157 /// # Examples
2158 ///
2159 /// ```
2160 /// use rust_igraph::Graph;
2161 ///
2162 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
2163 /// let seq = g.degree_sequence().unwrap();
2164 /// assert_eq!(seq, vec![2, 2, 2]);
2165 /// ```
2166 pub fn degree_sequence(&self) -> IgraphResult<Vec<u32>> {
2167 crate::algorithms::properties::degree::degree_sequence(
2168 self,
2169 crate::algorithms::properties::degree::DegreeMode::All,
2170 )
2171 }
2172
2173 /// Compute the graph diameter (longest shortest path).
2174 ///
2175 /// Returns `None` for graphs with zero vertices.
2176 ///
2177 /// # Examples
2178 ///
2179 /// ```
2180 /// use rust_igraph::Graph;
2181 ///
2182 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
2183 /// assert_eq!(g.diameter().unwrap(), Some(3));
2184 /// ```
2185 pub fn diameter(&self) -> IgraphResult<Option<u32>> {
2186 crate::algorithms::paths::radii::diameter(self)
2187 }
2188
2189 /// Compute the global transitivity (clustering coefficient).
2190 ///
2191 /// Returns `None` if there are no connected triples.
2192 ///
2193 /// # Examples
2194 ///
2195 /// ```
2196 /// use rust_igraph::Graph;
2197 ///
2198 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
2199 /// let t = g.transitivity().unwrap().unwrap();
2200 /// assert!((t - 1.0).abs() < 1e-10); // triangle is fully transitive
2201 /// ```
2202 pub fn transitivity(&self) -> IgraphResult<Option<f64>> {
2203 crate::algorithms::properties::triangles::transitivity_undirected(self)
2204 }
2205
2206 /// Compute the clique number (size of the largest clique).
2207 ///
2208 /// # Examples
2209 ///
2210 /// ```
2211 /// use rust_igraph::Graph;
2212 ///
2213 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0), (2,3)], false, None).unwrap();
2214 /// assert_eq!(g.clique_number().unwrap(), 3);
2215 /// ```
2216 pub fn clique_number(&self) -> IgraphResult<u32> {
2217 crate::algorithms::cliques::clique_number(self)
2218 }
2219
2220 /// Find all largest cliques (cliques of maximum size).
2221 ///
2222 /// # Examples
2223 ///
2224 /// ```
2225 /// use rust_igraph::Graph;
2226 ///
2227 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2), (2,3)], false, None).unwrap();
2228 /// let lc = g.largest_cliques().unwrap();
2229 /// assert_eq!(lc.len(), 1);
2230 /// assert_eq!(lc[0].len(), 3);
2231 /// ```
2232 pub fn largest_cliques(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
2233 crate::algorithms::cliques::largest_cliques(self)
2234 }
2235
2236 /// Count maximal cliques without enumerating them.
2237 ///
2238 /// # Examples
2239 ///
2240 /// ```
2241 /// use rust_igraph::Graph;
2242 ///
2243 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2), (2,3)], false, None).unwrap();
2244 /// assert!(g.maximal_cliques_count().unwrap() >= 2);
2245 /// ```
2246 pub fn maximal_cliques_count(&self) -> IgraphResult<u64> {
2247 crate::algorithms::cliques::maximal_cliques_count(self)
2248 }
2249
2250 /// Histogram of clique sizes in the graph.
2251 ///
2252 /// Returns a vector where entry `i` is the number of cliques of size `i`.
2253 ///
2254 /// # Examples
2255 ///
2256 /// ```
2257 /// use rust_igraph::Graph;
2258 ///
2259 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2260 /// let hist = g.clique_size_hist().unwrap();
2261 /// assert!(hist.len() >= 3);
2262 /// ```
2263 pub fn clique_size_hist(&self) -> IgraphResult<Vec<u64>> {
2264 crate::algorithms::cliques::clique_size_hist(self)
2265 }
2266
2267 /// Average local efficiency of the graph.
2268 ///
2269 /// # Examples
2270 ///
2271 /// ```
2272 /// use rust_igraph::Graph;
2273 ///
2274 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2275 /// let eff = g.average_local_efficiency().unwrap();
2276 /// assert!(eff > 0.0);
2277 /// ```
2278 pub fn average_local_efficiency(&self) -> IgraphResult<f64> {
2279 crate::algorithms::properties::efficiency::average_local_efficiency(self)
2280 }
2281
2282 /// Count mutual (reciprocal) edges in a directed graph.
2283 ///
2284 /// # Examples
2285 ///
2286 /// ```
2287 /// use rust_igraph::Graph;
2288 ///
2289 /// let g = Graph::from_edges(&[(0,1), (1,0), (1,2)], true, None).unwrap();
2290 /// let m = g.count_mutual().unwrap();
2291 /// assert_eq!(m, 1);
2292 /// ```
2293 pub fn count_mutual(&self) -> IgraphResult<usize> {
2294 crate::algorithms::properties::mutual::count_mutual(self, true)
2295 }
2296
2297 /// Find all maximal independent vertex sets.
2298 ///
2299 /// # Examples
2300 ///
2301 /// ```
2302 /// use rust_igraph::Graph;
2303 ///
2304 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
2305 /// let sets = g.maximal_independent_vertex_sets().unwrap();
2306 /// assert!(!sets.is_empty());
2307 /// ```
2308 pub fn maximal_independent_vertex_sets(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
2309 crate::algorithms::cliques::maximal_independent_vertex_sets(self)
2310 }
2311
2312 /// Find all largest independent vertex sets.
2313 ///
2314 /// # Examples
2315 ///
2316 /// ```
2317 /// use rust_igraph::Graph;
2318 ///
2319 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
2320 /// let sets = g.largest_independent_vertex_sets().unwrap();
2321 /// assert!(sets.iter().all(|s| s.len() == 2));
2322 /// ```
2323 pub fn largest_independent_vertex_sets(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
2324 crate::algorithms::cliques::largest_independent_vertex_sets(self)
2325 }
2326
2327 /// Greedy edge coloring.
2328 ///
2329 /// # Examples
2330 ///
2331 /// ```
2332 /// use rust_igraph::Graph;
2333 ///
2334 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2335 /// let colors = g.edge_coloring_greedy().unwrap();
2336 /// assert_eq!(colors.len(), 3);
2337 /// ```
2338 pub fn edge_coloring_greedy(&self) -> IgraphResult<Vec<u32>> {
2339 crate::algorithms::coloring::edge_coloring_greedy(self)
2340 }
2341
2342 /// Upper bound on the chromatic number.
2343 ///
2344 /// # Examples
2345 ///
2346 /// ```
2347 /// use rust_igraph::Graph;
2348 ///
2349 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2350 /// assert!(g.chromatic_number_upper_bound().unwrap() >= 3);
2351 /// ```
2352 pub fn chromatic_number_upper_bound(&self) -> IgraphResult<u32> {
2353 crate::algorithms::coloring::chromatic_number_upper_bound(self)
2354 }
2355
2356 /// Test whether the graph is perfect (Strong Perfect Graph Theorem).
2357 ///
2358 /// # Examples
2359 ///
2360 /// ```
2361 /// use rust_igraph::Graph;
2362 ///
2363 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2364 /// assert!(g.is_perfect().unwrap());
2365 /// ```
2366 pub fn is_perfect(&self) -> IgraphResult<bool> {
2367 crate::algorithms::properties::perfect::is_perfect(self)
2368 }
2369
2370 /// Average local transitivity (clustering coefficient).
2371 ///
2372 /// Vertices with degree < 2 are treated as having transitivity 0.
2373 ///
2374 /// # Examples
2375 ///
2376 /// ```
2377 /// use rust_igraph::Graph;
2378 ///
2379 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2380 /// let avg = g.transitivity_avglocal().unwrap();
2381 /// assert!(avg > 0.9);
2382 /// ```
2383 pub fn transitivity_avglocal(&self) -> IgraphResult<f64> {
2384 crate::algorithms::properties::triangles::transitivity_avglocal_undirected(
2385 self,
2386 crate::algorithms::properties::triangles::TransitivityMode::Zero,
2387 )
2388 }
2389
2390 /// Mean degree of all vertices.
2391 ///
2392 /// # Examples
2393 ///
2394 /// ```
2395 /// use rust_igraph::Graph;
2396 ///
2397 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2398 /// let md = g.mean_degree().unwrap();
2399 /// assert!((md.unwrap() - 2.0).abs() < 1e-10);
2400 /// ```
2401 pub fn mean_degree(&self) -> IgraphResult<Option<f64>> {
2402 crate::algorithms::properties::basic::mean_degree(self, true)
2403 }
2404
2405 /// Graph degeneracy (maximum k for which a k-core exists).
2406 ///
2407 /// # Examples
2408 ///
2409 /// ```
2410 /// use rust_igraph::Graph;
2411 ///
2412 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2413 /// assert_eq!(g.degeneracy().unwrap(), 2);
2414 /// ```
2415 pub fn degeneracy(&self) -> IgraphResult<u32> {
2416 crate::algorithms::properties::is_k_degenerate::degeneracy(self)
2417 }
2418
2419 /// Convergence degree of each edge.
2420 ///
2421 /// # Examples
2422 ///
2423 /// ```
2424 /// use rust_igraph::Graph;
2425 ///
2426 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2427 /// let cd = g.convergence_degree().unwrap();
2428 /// assert_eq!(cd.len(), g.ecount());
2429 /// ```
2430 pub fn convergence_degree(&self) -> IgraphResult<Vec<f64>> {
2431 crate::algorithms::properties::convergence_degree::convergence_degree(self)
2432 }
2433
2434 /// Count self-loops in the graph.
2435 ///
2436 /// # Examples
2437 ///
2438 /// ```
2439 /// use rust_igraph::Graph;
2440 ///
2441 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
2442 /// assert_eq!(g.count_loops().unwrap(), 0);
2443 /// ```
2444 pub fn count_loops(&self) -> IgraphResult<usize> {
2445 crate::algorithms::properties::multiplicity::count_loops(self)
2446 }
2447
2448 /// Average nearest neighbor degree for each vertex.
2449 ///
2450 /// # Examples
2451 ///
2452 /// ```
2453 /// use rust_igraph::Graph;
2454 ///
2455 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2456 /// let knn = g.avg_nearest_neighbor_degree().unwrap();
2457 /// assert_eq!(knn.len(), 3);
2458 /// ```
2459 pub fn avg_nearest_neighbor_degree(&self) -> IgraphResult<Vec<Option<f64>>> {
2460 crate::algorithms::properties::knn::avg_nearest_neighbor_degree(self)
2461 }
2462
2463 /// Bibliographic coupling scores between all vertex pairs.
2464 ///
2465 /// Returns a flat n*n matrix where entry `[i*n + j]` is the coupling
2466 /// score between vertices `i` and `j`.
2467 ///
2468 /// # Examples
2469 ///
2470 /// ```
2471 /// use rust_igraph::Graph;
2472 ///
2473 /// let g = Graph::from_edges(&[(0,2), (1,2)], true, None).unwrap();
2474 /// let n = g.vcount() as usize;
2475 /// let bc = g.bibcoupling().unwrap();
2476 /// assert_eq!(bc.len(), n * n);
2477 /// ```
2478 pub fn bibcoupling(&self) -> IgraphResult<Vec<u32>> {
2479 crate::algorithms::properties::similarity::bibcoupling(self)
2480 }
2481
2482 /// Biconnected components of the graph.
2483 ///
2484 /// # Examples
2485 ///
2486 /// ```
2487 /// use rust_igraph::Graph;
2488 ///
2489 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0), (2,3)], false, None).unwrap();
2490 /// let bc = g.biconnected_components().unwrap();
2491 /// assert_eq!(bc.components.len(), 2);
2492 /// ```
2493 pub fn biconnected_components(
2494 &self,
2495 ) -> IgraphResult<crate::algorithms::connectivity::biconnected::BiconnectedComponents> {
2496 crate::algorithms::connectivity::biconnected::biconnected_components(self)
2497 }
2498
2499 /// Find all minimum-size vertex separators.
2500 ///
2501 /// # Examples
2502 ///
2503 /// ```
2504 /// use rust_igraph::Graph;
2505 ///
2506 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,3), (2,3)], false, None).unwrap();
2507 /// let seps = g.minimum_size_separators().unwrap();
2508 /// assert!(!seps.is_empty());
2509 /// ```
2510 pub fn minimum_size_separators(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
2511 crate::algorithms::connectivity::separators::minimum_size_separators(self)
2512 }
2513
2514 /// Find all minimal s-t separators.
2515 ///
2516 /// # Examples
2517 ///
2518 /// ```
2519 /// use rust_igraph::Graph;
2520 ///
2521 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,3), (2,3)], false, None).unwrap();
2522 /// let seps = g.all_minimal_st_separators().unwrap();
2523 /// assert!(!seps.is_empty());
2524 /// ```
2525 pub fn all_minimal_st_separators(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
2526 crate::algorithms::connectivity::separators::all_minimal_st_separators(self)
2527 }
2528
2529 /// Graph adhesion (minimum edge connectivity over all pairs).
2530 ///
2531 /// # Examples
2532 ///
2533 /// ```
2534 /// use rust_igraph::Graph;
2535 ///
2536 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
2537 /// assert_eq!(g.adhesion().unwrap(), 2);
2538 /// ```
2539 pub fn adhesion(&self) -> IgraphResult<i64> {
2540 crate::algorithms::flow::edge_connectivity::adhesion(self, true)
2541 }
2542
2543 /// Graph cohesion (minimum vertex connectivity over all pairs).
2544 ///
2545 /// # Examples
2546 ///
2547 /// ```
2548 /// use rust_igraph::Graph;
2549 ///
2550 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
2551 /// assert_eq!(g.cohesion().unwrap(), 2);
2552 /// ```
2553 pub fn cohesion(&self) -> IgraphResult<i64> {
2554 crate::algorithms::flow::vertex_connectivity::cohesion(self, true)
2555 }
2556
2557 /// BFS tree rooted at `root`.
2558 ///
2559 /// # Examples
2560 ///
2561 /// ```
2562 /// use rust_igraph::Graph;
2563 ///
2564 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2565 /// let tree = g.bfs_tree(0).unwrap();
2566 /// assert_eq!(tree.order.len(), 3);
2567 /// ```
2568 pub fn bfs_tree(
2569 &self,
2570 root: VertexId,
2571 ) -> IgraphResult<crate::algorithms::traversal::bfs::BfsTree> {
2572 crate::algorithms::traversal::bfs::bfs_tree(self, root)
2573 }
2574
2575 /// DFS tree rooted at `root`.
2576 ///
2577 /// # Examples
2578 ///
2579 /// ```
2580 /// use rust_igraph::Graph;
2581 ///
2582 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
2583 /// let tree = g.dfs_tree(0).unwrap();
2584 /// assert_eq!(tree.order.len(), 3);
2585 /// ```
2586 pub fn dfs_tree(
2587 &self,
2588 root: VertexId,
2589 ) -> IgraphResult<crate::algorithms::traversal::dfs::DfsTree> {
2590 crate::algorithms::traversal::dfs::dfs_tree(self, root)
2591 }
2592
2593 /// Find all articulation points (cut vertices).
2594 ///
2595 /// # Examples
2596 ///
2597 /// ```
2598 /// use rust_igraph::Graph;
2599 ///
2600 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3), (1,3)], false, None).unwrap();
2601 /// let ap = g.articulation_points().unwrap();
2602 /// assert_eq!(ap, vec![1]); // vertex 1 is the only cut vertex
2603 /// ```
2604 pub fn articulation_points(&self) -> IgraphResult<Vec<VertexId>> {
2605 crate::algorithms::connectivity::articulation::articulation_points(self)
2606 }
2607
2608 /// Topological sort (DAG only, returns error for cyclic graphs).
2609 ///
2610 /// # Examples
2611 ///
2612 /// ```
2613 /// use rust_igraph::Graph;
2614 ///
2615 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], true, None).unwrap();
2616 /// let order = g.topological_sort().unwrap();
2617 /// assert_eq!(order[0], 0); // source comes first
2618 /// ```
2619 pub fn topological_sort(&self) -> IgraphResult<Vec<VertexId>> {
2620 crate::algorithms::properties::topological_sorting::topological_sorting(
2621 self,
2622 crate::algorithms::paths::dijkstra::DijkstraMode::Out,
2623 )
2624 }
2625
2626 /// Compute a minimum spanning tree (unweighted).
2627 ///
2628 /// Returns the edge ids forming the MST.
2629 ///
2630 /// # Examples
2631 ///
2632 /// ```
2633 /// use rust_igraph::Graph;
2634 ///
2635 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0), (2,3)], false, None).unwrap();
2636 /// let mst_edges = g.minimum_spanning_tree().unwrap();
2637 /// assert_eq!(mst_edges.len(), 3); // n-1 edges for connected graph
2638 /// ```
2639 pub fn minimum_spanning_tree(&self) -> IgraphResult<Vec<EdgeId>> {
2640 crate::algorithms::spanning::mst::minimum_spanning_tree(
2641 self,
2642 None,
2643 crate::algorithms::spanning::mst::MstAlgorithm::Automatic,
2644 )
2645 }
2646
2647 /// Compute a quick structural summary of the graph.
2648 ///
2649 /// # Examples
2650 ///
2651 /// ```
2652 /// use rust_igraph::Graph;
2653 ///
2654 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
2655 /// let s = g.summary().unwrap();
2656 /// assert_eq!(s.vcount, 3);
2657 /// assert!(s.connected);
2658 /// ```
2659 pub fn summary(&self) -> IgraphResult<crate::algorithms::properties::summary::GraphSummary> {
2660 crate::algorithms::properties::summary::graph_summary(self)
2661 }
2662
2663 /// Compute the maximum flow value between two vertices.
2664 ///
2665 /// # Examples
2666 ///
2667 /// ```
2668 /// use rust_igraph::Graph;
2669 ///
2670 /// let g = Graph::from_edges(
2671 /// &[(0,1), (0,2), (1,3), (2,3)], true, None
2672 /// ).unwrap();
2673 /// let flow = g.max_flow(0, 3).unwrap();
2674 /// assert!((flow - 2.0).abs() < 1e-10);
2675 /// ```
2676 pub fn max_flow(&self, source: VertexId, target: VertexId) -> IgraphResult<f64> {
2677 crate::algorithms::flow::max_flow::max_flow_value(self, source, target, None)
2678 }
2679
2680 /// Decompose the graph into its connected components as separate graphs.
2681 ///
2682 /// # Examples
2683 ///
2684 /// ```
2685 /// use rust_igraph::Graph;
2686 ///
2687 /// let g = Graph::from_edges(&[(0,1), (2,3)], false, None).unwrap();
2688 /// let components = g.decompose().unwrap();
2689 /// assert_eq!(components.len(), 2);
2690 /// ```
2691 pub fn decompose(&self) -> IgraphResult<Vec<Graph>> {
2692 crate::algorithms::connectivity::decompose::decompose(self)
2693 }
2694
2695 /// Check whether the graph is biconnected.
2696 ///
2697 /// # Examples
2698 ///
2699 /// ```
2700 /// use rust_igraph::Graph;
2701 ///
2702 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
2703 /// assert!(g.is_biconnected().unwrap());
2704 /// ```
2705 pub fn is_biconnected(&self) -> IgraphResult<bool> {
2706 crate::algorithms::connectivity::is_biconnected::is_biconnected(self)
2707 }
2708
2709 /// Run label propagation community detection.
2710 ///
2711 /// # Examples
2712 ///
2713 /// ```
2714 /// use rust_igraph::Graph;
2715 ///
2716 /// let g = Graph::from_edges(
2717 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3)], false, None
2718 /// ).unwrap();
2719 /// let result = g.label_propagation().unwrap();
2720 /// assert!(result.membership.len() == 6);
2721 /// ```
2722 pub fn label_propagation(
2723 &self,
2724 ) -> IgraphResult<crate::algorithms::community::label_propagation::LpaResult> {
2725 crate::algorithms::community::label_propagation::label_propagation(self)
2726 }
2727
2728 /// Run Walktrap community detection.
2729 ///
2730 /// # Examples
2731 ///
2732 /// ```
2733 /// use rust_igraph::Graph;
2734 ///
2735 /// let g = Graph::from_edges(
2736 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (2,3)], false, None
2737 /// ).unwrap();
2738 /// let result = g.walktrap().unwrap();
2739 /// assert!(result.membership.len() == 6);
2740 /// ```
2741 pub fn walktrap(&self) -> IgraphResult<crate::algorithms::community::walktrap::WalktrapResult> {
2742 crate::algorithms::community::walktrap::walktrap(self)
2743 }
2744
2745 /// Run fast greedy modularity community detection.
2746 ///
2747 /// # Examples
2748 ///
2749 /// ```
2750 /// use rust_igraph::Graph;
2751 ///
2752 /// let g = Graph::from_edges(
2753 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (2,3)], false, None
2754 /// ).unwrap();
2755 /// let result = g.fast_greedy().unwrap();
2756 /// assert!(result.membership.len() == 6);
2757 /// ```
2758 pub fn fast_greedy(
2759 &self,
2760 ) -> IgraphResult<crate::algorithms::community::fast_greedy_modularity::FastGreedyResult> {
2761 crate::algorithms::community::fast_greedy_modularity::fast_greedy_modularity(self)
2762 }
2763
2764 /// Compute hub and authority scores (HITS algorithm).
2765 ///
2766 /// # Examples
2767 ///
2768 /// ```
2769 /// use rust_igraph::Graph;
2770 ///
2771 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], true, None).unwrap();
2772 /// let hits = g.hits().unwrap();
2773 /// assert_eq!(hits.hub.len(), 3);
2774 /// assert_eq!(hits.authority.len(), 3);
2775 /// ```
2776 pub fn hits(&self) -> IgraphResult<crate::algorithms::properties::hits::HitsScores> {
2777 crate::algorithms::properties::hits::hub_and_authority_scores(self)
2778 }
2779
2780 /// Compute Katz centrality for all vertices.
2781 ///
2782 /// # Examples
2783 ///
2784 /// ```
2785 /// use rust_igraph::Graph;
2786 ///
2787 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
2788 /// let katz = g.katz_centrality(0.1, 1.0).unwrap();
2789 /// assert_eq!(katz.len(), 3);
2790 /// ```
2791 pub fn katz_centrality(&self, alpha: f64, beta: f64) -> IgraphResult<Vec<f64>> {
2792 crate::algorithms::properties::katz_centrality::katz_centrality(
2793 self, alpha, beta, None, None,
2794 )
2795 }
2796
2797 /// Compute degree assortativity of the graph.
2798 ///
2799 /// # Examples
2800 ///
2801 /// ```
2802 /// use rust_igraph::Graph;
2803 ///
2804 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
2805 /// let a = g.assortativity().unwrap();
2806 /// assert!(a.is_some());
2807 /// ```
2808 pub fn assortativity(&self) -> IgraphResult<Option<f64>> {
2809 crate::algorithms::properties::assortativity::assortativity_degree(self)
2810 }
2811
2812 /// Read a graph from a file, auto-detecting the format from the extension.
2813 ///
2814 /// Supported extensions: `.gml`, `.graphml`, `.dot`, `.net` (Pajek),
2815 /// `.ncol`, `.lgl`, `.leda`, `.dl`, `.edges`/`.edgelist`/`.txt`.
2816 ///
2817 /// # Examples
2818 ///
2819 /// ```no_run
2820 /// use rust_igraph::Graph;
2821 ///
2822 /// let g = Graph::from_file("network.gml").unwrap();
2823 /// println!("{}", g.vcount());
2824 /// ```
2825 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
2826 let p = path.as_ref();
2827 let ext = p
2828 .extension()
2829 .and_then(|e| e.to_str())
2830 .unwrap_or("")
2831 .to_ascii_lowercase();
2832 match ext.as_str() {
2833 "gml" => Self::from_gml_file(p),
2834 "graphml" | "xml" => Self::from_graphml_file(p),
2835 "dot" | "gv" => Self::from_dot_file(p),
2836 "net" | "pajek" => Self::from_pajek_file(p),
2837 "ncol" => Self::from_ncol_file(p),
2838 "lgl" => Self::from_lgl_file(p),
2839 "leda" | "lgr" => Self::from_leda_file(p),
2840 "dl" => Self::from_dl_file(p),
2841 "edges" | "edgelist" | "txt" | "csv" => Self::from_edgelist_file(p),
2842 _ => Err(IgraphError::InvalidArgument(format!(
2843 "cannot detect graph format from extension \".{ext}\"; \
2844 use a format-specific method like from_gml_file()"
2845 ))),
2846 }
2847 }
2848
2849 /// Write a graph to a file, auto-detecting the format from the extension.
2850 ///
2851 /// Supported extensions: `.gml`, `.graphml`, `.dot`, `.net` (Pajek),
2852 /// `.ncol`, `.lgl`, `.leda`, `.dl`, `.edges`/`.edgelist`/`.txt`.
2853 ///
2854 /// # Examples
2855 ///
2856 /// ```no_run
2857 /// use rust_igraph::Graph;
2858 ///
2859 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
2860 /// g.to_file("output.gml").unwrap();
2861 /// ```
2862 pub fn to_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
2863 let p = path.as_ref();
2864 let ext = p
2865 .extension()
2866 .and_then(|e| e.to_str())
2867 .unwrap_or("")
2868 .to_ascii_lowercase();
2869 match ext.as_str() {
2870 "gml" => self.to_gml_file(p),
2871 "graphml" | "xml" => self.to_graphml_file(p),
2872 "dot" | "gv" => self.to_dot_file(p),
2873 "net" | "pajek" => self.to_pajek_file(p),
2874 "ncol" => self.to_ncol_file(p),
2875 "lgl" => self.to_lgl_file(p),
2876 "leda" | "lgr" => self.to_leda_file(p),
2877 "dl" => self.to_dl_file(p),
2878 "edges" | "edgelist" | "txt" | "csv" => self.to_edgelist_file(p),
2879 _ => Err(IgraphError::InvalidArgument(format!(
2880 "cannot detect graph format from extension \".{ext}\"; \
2881 use a format-specific method like to_gml_file()"
2882 ))),
2883 }
2884 }
2885
2886 /// Read a graph from an edge list file.
2887 ///
2888 /// Each line should contain two space-separated vertex ids.
2889 ///
2890 /// # Examples
2891 ///
2892 /// ```no_run
2893 /// use rust_igraph::Graph;
2894 ///
2895 /// let g = Graph::from_edgelist_file("my_graph.edges").unwrap();
2896 /// println!("{}", g.vcount());
2897 /// ```
2898 pub fn from_edgelist_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
2899 let file = std::fs::File::open(path.as_ref())
2900 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
2901 crate::algorithms::io::edgelist::read_edgelist(std::io::BufReader::new(file))
2902 }
2903
2904 /// Write the graph to a file in edge list format.
2905 ///
2906 /// # Examples
2907 ///
2908 /// ```no_run
2909 /// use rust_igraph::Graph;
2910 ///
2911 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
2912 /// g.to_edgelist_file("output.edges").unwrap();
2913 /// ```
2914 pub fn to_edgelist_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
2915 let mut file = std::fs::File::create(path.as_ref())
2916 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
2917 crate::algorithms::io::edgelist::write_edgelist(self, &mut file)
2918 }
2919
2920 /// Read a graph from a GML file.
2921 ///
2922 /// # Examples
2923 ///
2924 /// ```no_run
2925 /// use rust_igraph::Graph;
2926 ///
2927 /// let g = Graph::from_gml_file("network.gml").unwrap();
2928 /// ```
2929 pub fn from_gml_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
2930 let file = std::fs::File::open(path.as_ref())
2931 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
2932 crate::algorithms::io::gml::read_gml(std::io::BufReader::new(file))
2933 }
2934
2935 /// Write the graph to a file in GML format.
2936 ///
2937 /// # Examples
2938 ///
2939 /// ```no_run
2940 /// use rust_igraph::Graph;
2941 ///
2942 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
2943 /// g.to_gml_file("output.gml").unwrap();
2944 /// ```
2945 pub fn to_gml_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
2946 let mut file = std::fs::File::create(path.as_ref())
2947 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
2948 crate::algorithms::io::gml::write_gml(self, &mut file)
2949 }
2950
2951 /// Read a graph from a `GraphML` file.
2952 ///
2953 /// # Examples
2954 ///
2955 /// ```no_run
2956 /// use rust_igraph::Graph;
2957 ///
2958 /// let g = Graph::from_graphml_file("network.graphml").unwrap();
2959 /// ```
2960 pub fn from_graphml_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
2961 let file = std::fs::File::open(path.as_ref())
2962 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
2963 let result = crate::algorithms::io::graphml::read_graphml(std::io::BufReader::new(file))?;
2964 Ok(result.graph)
2965 }
2966
2967 /// Write the graph to a file in `GraphML` format.
2968 ///
2969 /// # Examples
2970 ///
2971 /// ```no_run
2972 /// use rust_igraph::Graph;
2973 ///
2974 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
2975 /// g.to_graphml_file("output.graphml").unwrap();
2976 /// ```
2977 pub fn to_graphml_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
2978 let mut file = std::fs::File::create(path.as_ref())
2979 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
2980 crate::algorithms::io::graphml::write_graphml(self, None, &mut file)
2981 }
2982
2983 /// Read a graph from a DOT (Graphviz) file.
2984 ///
2985 /// # Examples
2986 ///
2987 /// ```no_run
2988 /// use rust_igraph::Graph;
2989 ///
2990 /// let g = Graph::from_dot_file("network.dot").unwrap();
2991 /// ```
2992 pub fn from_dot_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
2993 let file = std::fs::File::open(path.as_ref())
2994 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
2995 let result = crate::algorithms::io::dot::read_dot(std::io::BufReader::new(file))?;
2996 Ok(result.graph)
2997 }
2998
2999 /// Write the graph to a file in DOT (Graphviz) format.
3000 ///
3001 /// # Examples
3002 ///
3003 /// ```no_run
3004 /// use rust_igraph::Graph;
3005 ///
3006 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3007 /// g.to_dot_file("output.dot").unwrap();
3008 /// ```
3009 pub fn to_dot_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
3010 let mut file = std::fs::File::create(path.as_ref())
3011 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
3012 crate::algorithms::io::dot::write_dot(self, None, &mut file)
3013 }
3014
3015 /// Read a graph from a Pajek (.net) file.
3016 ///
3017 /// # Examples
3018 ///
3019 /// ```no_run
3020 /// use rust_igraph::Graph;
3021 ///
3022 /// let g = Graph::from_pajek_file("network.net").unwrap();
3023 /// ```
3024 pub fn from_pajek_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
3025 let file = std::fs::File::open(path.as_ref())
3026 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
3027 let result = crate::algorithms::io::pajek::read_pajek(std::io::BufReader::new(file))?;
3028 Ok(result.graph)
3029 }
3030
3031 /// Write the graph to a file in Pajek (.net) format.
3032 ///
3033 /// # Examples
3034 ///
3035 /// ```no_run
3036 /// use rust_igraph::Graph;
3037 ///
3038 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3039 /// g.to_pajek_file("output.net").unwrap();
3040 /// ```
3041 pub fn to_pajek_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
3042 let mut file = std::fs::File::create(path.as_ref())
3043 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
3044 crate::algorithms::io::pajek::write_pajek(self, None, None, &mut file)
3045 }
3046
3047 /// Read a graph from an NCOL file (Large Graph Layout edge list format).
3048 ///
3049 /// Returns just the graph; use [`read_ncol`](crate::read_ncol) directly
3050 /// to also obtain vertex names and edge weights.
3051 ///
3052 /// # Examples
3053 ///
3054 /// ```no_run
3055 /// use rust_igraph::Graph;
3056 ///
3057 /// let g = Graph::from_ncol_file("network.ncol").unwrap();
3058 /// ```
3059 pub fn from_ncol_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
3060 let file = std::fs::File::open(path.as_ref())
3061 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
3062 let result = crate::algorithms::io::ncol::read_ncol(std::io::BufReader::new(file))?;
3063 Ok(result.graph)
3064 }
3065
3066 /// Write the graph to a file in NCOL format.
3067 ///
3068 /// Writes vertex indices as names (no custom names or weights).
3069 /// Use [`write_ncol`](crate::write_ncol) for full control.
3070 ///
3071 /// # Examples
3072 ///
3073 /// ```no_run
3074 /// use rust_igraph::Graph;
3075 ///
3076 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3077 /// g.to_ncol_file("output.ncol").unwrap();
3078 /// ```
3079 pub fn to_ncol_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
3080 let mut file = std::fs::File::create(path.as_ref())
3081 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
3082 crate::algorithms::io::ncol::write_ncol(self, None, None, &mut file)
3083 }
3084
3085 /// Read a graph from an LGL file (Large Graph Layout adjacency list).
3086 ///
3087 /// Returns just the graph; use [`read_lgl`](crate::read_lgl) directly
3088 /// to also obtain vertex names and edge weights.
3089 ///
3090 /// # Examples
3091 ///
3092 /// ```no_run
3093 /// use rust_igraph::Graph;
3094 ///
3095 /// let g = Graph::from_lgl_file("network.lgl").unwrap();
3096 /// ```
3097 pub fn from_lgl_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
3098 let file = std::fs::File::open(path.as_ref())
3099 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
3100 let result = crate::algorithms::io::lgl::read_lgl(std::io::BufReader::new(file))?;
3101 Ok(result.graph)
3102 }
3103
3104 /// Write the graph to a file in LGL format.
3105 ///
3106 /// Writes vertex indices as names (no custom names or weights).
3107 /// Use [`write_lgl`](crate::write_lgl) for full control.
3108 ///
3109 /// # Examples
3110 ///
3111 /// ```no_run
3112 /// use rust_igraph::Graph;
3113 ///
3114 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3115 /// g.to_lgl_file("output.lgl").unwrap();
3116 /// ```
3117 pub fn to_lgl_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
3118 let mut file = std::fs::File::create(path.as_ref())
3119 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
3120 crate::algorithms::io::lgl::write_lgl(self, None, None, &mut file)
3121 }
3122
3123 /// Read a graph from a LEDA native graph file.
3124 ///
3125 /// Returns just the graph; use [`read_leda`](crate::read_leda) directly
3126 /// to also obtain vertex labels and edge weights.
3127 ///
3128 /// # Examples
3129 ///
3130 /// ```no_run
3131 /// use rust_igraph::Graph;
3132 ///
3133 /// let g = Graph::from_leda_file("network.lgr").unwrap();
3134 /// ```
3135 pub fn from_leda_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
3136 let file = std::fs::File::open(path.as_ref())
3137 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
3138 let result = crate::algorithms::io::leda::read_leda(std::io::BufReader::new(file))?;
3139 Ok(result.graph)
3140 }
3141
3142 /// Write the graph to a file in LEDA native graph format.
3143 ///
3144 /// Writes without vertex labels or edge weights.
3145 /// Use [`write_leda`](crate::write_leda) for full control.
3146 ///
3147 /// # Examples
3148 ///
3149 /// ```no_run
3150 /// use rust_igraph::Graph;
3151 ///
3152 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3153 /// g.to_leda_file("output.lgr").unwrap();
3154 /// ```
3155 pub fn to_leda_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
3156 let mut file = std::fs::File::create(path.as_ref())
3157 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
3158 crate::algorithms::io::leda::write_leda(self, None, None, &mut file)
3159 }
3160
3161 /// Read a graph from a UCINET DL file.
3162 ///
3163 /// Reads as undirected by default. Use [`read_dl`](crate::read_dl)
3164 /// directly for directed graphs or to obtain vertex labels and edge
3165 /// weights.
3166 ///
3167 /// # Examples
3168 ///
3169 /// ```no_run
3170 /// use rust_igraph::Graph;
3171 ///
3172 /// let g = Graph::from_dl_file("network.dl").unwrap();
3173 /// ```
3174 pub fn from_dl_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
3175 let file = std::fs::File::open(path.as_ref())
3176 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
3177 let result = crate::algorithms::io::dl::read_dl(std::io::BufReader::new(file), false)?;
3178 Ok(result.graph)
3179 }
3180
3181 /// Write the graph to a file in UCINET DL format.
3182 ///
3183 /// Writes without vertex labels or edge weights.
3184 /// Use [`write_dl`](crate::write_dl) for full control.
3185 ///
3186 /// # Examples
3187 ///
3188 /// ```no_run
3189 /// use rust_igraph::Graph;
3190 ///
3191 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3192 /// g.to_dl_file("output.dl").unwrap();
3193 /// ```
3194 pub fn to_dl_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
3195 let mut file = std::fs::File::create(path.as_ref())
3196 .map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
3197 crate::algorithms::io::dl::write_dl(self, None, None, &mut file)
3198 }
3199
3200 /// Read a graph from a DIMACS file.
3201 ///
3202 /// Reads as directed by default (flow problems). Returns just the graph;
3203 /// use [`read_dimacs`](crate::read_dimacs) directly to also obtain
3204 /// source/target, capacities, or labels.
3205 ///
3206 /// # Examples
3207 ///
3208 /// ```no_run
3209 /// use rust_igraph::Graph;
3210 ///
3211 /// let g = Graph::from_dimacs_file("network.dimacs").unwrap();
3212 /// ```
3213 pub fn from_dimacs_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
3214 let file = std::fs::File::open(path.as_ref())
3215 .map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
3216 let result =
3217 crate::algorithms::io::dimacs::read_dimacs(std::io::BufReader::new(file), true)?;
3218 Ok(result.graph)
3219 }
3220
3221 /// Generate an Erdos-Renyi G(n, p) random graph.
3222 ///
3223 /// Each possible edge exists independently with probability `p`.
3224 ///
3225 /// # Examples
3226 ///
3227 /// ```
3228 /// use rust_igraph::Graph;
3229 ///
3230 /// let g = Graph::erdos_renyi(100, 0.05, 42).unwrap();
3231 /// assert_eq!(g.vcount(), 100);
3232 /// assert!(!g.is_directed());
3233 /// ```
3234 pub fn erdos_renyi(n: u32, p: f64, seed: u64) -> IgraphResult<Self> {
3235 crate::algorithms::games::erdos_renyi::erdos_renyi_gnp(n, p, false, false, seed)
3236 }
3237
3238 /// Generate a Barabasi-Albert preferential attachment graph.
3239 ///
3240 /// Starts with one vertex and adds `n - 1` vertices, each connecting
3241 /// to `m` existing vertices chosen with probability proportional to degree.
3242 ///
3243 /// # Examples
3244 ///
3245 /// ```
3246 /// use rust_igraph::Graph;
3247 ///
3248 /// let g = Graph::barabasi_albert(100, 2, 42).unwrap();
3249 /// assert_eq!(g.vcount(), 100);
3250 /// assert!(!g.is_directed());
3251 /// ```
3252 pub fn barabasi_albert(n: u32, m: u32, seed: u64) -> IgraphResult<Self> {
3253 crate::algorithms::games::barabasi::barabasi_game_bag(n, m, true, false, seed)
3254 }
3255
3256 /// Generate a Watts-Strogatz small-world graph.
3257 ///
3258 /// Creates a ring lattice with `n` vertices where each vertex is connected
3259 /// to its `k` nearest neighbours (must be even), then rewires each edge
3260 /// with probability `p`. This produces graphs with both high clustering
3261 /// and short path lengths — the "small-world" property.
3262 ///
3263 /// # Examples
3264 ///
3265 /// ```
3266 /// use rust_igraph::Graph;
3267 ///
3268 /// let g = Graph::watts_strogatz(20, 4, 0.3, 42).unwrap();
3269 /// assert_eq!(g.vcount(), 20);
3270 /// assert_eq!(g.ecount(), 40); // n * k / 2 = 20 * 4 / 2
3271 /// ```
3272 pub fn watts_strogatz(n: u32, k: u32, p: f64, seed: u64) -> IgraphResult<Self> {
3273 crate::algorithms::games::watts::watts_strogatz_game(n, k / 2, p, false, false, seed)
3274 }
3275
3276 /// Compute strongly connected components (directed graphs).
3277 ///
3278 /// For undirected graphs, this is equivalent to connected components.
3279 ///
3280 /// # Examples
3281 ///
3282 /// ```
3283 /// use rust_igraph::Graph;
3284 ///
3285 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0), (2,3)], true, None).unwrap();
3286 /// let scc = g.strongly_connected_components().unwrap();
3287 /// assert_eq!(scc.count, 2);
3288 /// ```
3289 pub fn strongly_connected_components(
3290 &self,
3291 ) -> IgraphResult<crate::algorithms::connectivity::components::ConnectedComponents> {
3292 crate::algorithms::connectivity::strong::strongly_connected_components(self)
3293 }
3294
3295 /// Find the shortest path between two vertices.
3296 ///
3297 /// Uses BFS for unweighted graphs, Dijkstra/Bellman-Ford for weighted.
3298 /// Returns the vertex and edge sequences along the path.
3299 ///
3300 /// # Examples
3301 ///
3302 /// ```
3303 /// use rust_igraph::Graph;
3304 ///
3305 /// let g = Graph::from_edges(
3306 /// &[(0,1), (1,2), (2,3), (0,3)], false, None
3307 /// ).unwrap();
3308 /// let path = g.shortest_path_to(0, 3, None).unwrap();
3309 /// assert_eq!(path.vertices, vec![0, 3]);
3310 /// ```
3311 pub fn shortest_path_to(
3312 &self,
3313 source: VertexId,
3314 target: VertexId,
3315 weights: Option<&[f64]>,
3316 ) -> IgraphResult<crate::algorithms::paths::get_shortest_path::ShortestPath> {
3317 use crate::algorithms::paths::dijkstra::DijkstraMode;
3318 let mode = if self.directed {
3319 DijkstraMode::Out
3320 } else {
3321 DijkstraMode::All
3322 };
3323 crate::algorithms::paths::get_shortest_path::get_shortest_path(
3324 self, source, target, weights, mode,
3325 )
3326 }
3327
3328 /// Compute the average path length of the graph.
3329 ///
3330 /// Returns the mean shortest-path distance over all reachable vertex pairs.
3331 /// Unreachable pairs are excluded. Returns `None` if the graph has fewer
3332 /// than 2 vertices or no reachable pairs exist.
3333 ///
3334 /// # Examples
3335 ///
3336 /// ```
3337 /// use rust_igraph::Graph;
3338 ///
3339 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3340 /// let apl = g.average_path_length().unwrap().unwrap();
3341 /// assert!((apl - 5.0 / 3.0).abs() < 1e-10); // (1+2+3+1+2+1)/6
3342 /// ```
3343 pub fn average_path_length(&self) -> IgraphResult<Option<f64>> {
3344 crate::algorithms::properties::basic::mean_distance(self)
3345 }
3346
3347 /// Check if the graph is bipartite and return the partition if so.
3348 ///
3349 /// # Examples
3350 ///
3351 /// ```
3352 /// use rust_igraph::Graph;
3353 ///
3354 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3355 /// let result = g.is_bipartite().unwrap();
3356 /// assert!(result.is_bipartite);
3357 /// ```
3358 pub fn is_bipartite(
3359 &self,
3360 ) -> IgraphResult<crate::algorithms::properties::is_bipartite::BipartiteResult> {
3361 crate::algorithms::properties::is_bipartite::is_bipartite(self)
3362 }
3363
3364 /// Remove self-loops and/or multi-edges from the graph.
3365 ///
3366 /// Returns a new simplified graph.
3367 ///
3368 /// # Examples
3369 ///
3370 /// ```
3371 /// use rust_igraph::Graph;
3372 ///
3373 /// let mut g = Graph::with_vertices(3);
3374 /// g.add_edge(0, 1).unwrap();
3375 /// g.add_edge(0, 1).unwrap(); // multi-edge
3376 /// g.add_edge(1, 1).unwrap(); // self-loop
3377 /// let simple = g.simplify(true, true).unwrap();
3378 /// assert_eq!(simple.ecount(), 1);
3379 /// ```
3380 pub fn simplify(&self, remove_multiple: bool, remove_loops: bool) -> IgraphResult<Graph> {
3381 crate::algorithms::operators::simplify::simplify(self, remove_multiple, remove_loops)
3382 }
3383
3384 /// Reverse all edge directions (directed graphs only).
3385 ///
3386 /// For undirected graphs, returns a copy of the graph unchanged.
3387 ///
3388 /// # Examples
3389 ///
3390 /// ```
3391 /// use rust_igraph::Graph;
3392 ///
3393 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
3394 /// let r = g.reverse().unwrap();
3395 /// assert_eq!(r.neighbors(2).unwrap(), vec![1]);
3396 /// ```
3397 pub fn reverse(&self) -> IgraphResult<Graph> {
3398 crate::algorithms::operators::reverse::reverse(self)
3399 }
3400
3401 /// Convert an undirected graph to directed.
3402 ///
3403 /// In `Mutual` mode each undirected edge becomes two directed edges
3404 /// (u→v and v→u). In `Arbitrary` mode each edge gets one direction
3405 /// (smaller → larger vertex id). Already-directed graphs are copied
3406 /// unchanged.
3407 ///
3408 /// # Examples
3409 ///
3410 /// ```
3411 /// use rust_igraph::{Graph, ToDirectedMode};
3412 ///
3413 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3414 /// let d = g.to_directed(ToDirectedMode::Mutual).unwrap();
3415 /// assert!(d.is_directed());
3416 /// assert_eq!(d.ecount(), 4);
3417 /// ```
3418 pub fn to_directed(
3419 &self,
3420 mode: crate::algorithms::operators::to_directed::ToDirectedMode,
3421 ) -> IgraphResult<Graph> {
3422 crate::algorithms::operators::to_directed::to_directed(self, mode)
3423 }
3424
3425 /// Convert a directed graph to undirected.
3426 ///
3427 /// `Each` keeps every directed edge as undirected. `Collapse` merges
3428 /// mutual pairs into one edge. `Mutual` keeps only edges that exist
3429 /// in both directions. Already-undirected graphs are copied unchanged.
3430 ///
3431 /// # Examples
3432 ///
3433 /// ```
3434 /// use rust_igraph::{Graph, ToUndirectedMode};
3435 ///
3436 /// let g = Graph::from_edges(&[(0,1), (1,0), (1,2)], true, None).unwrap();
3437 /// let u = g.to_undirected(ToUndirectedMode::Collapse).unwrap();
3438 /// assert!(!u.is_directed());
3439 /// assert_eq!(u.ecount(), 2);
3440 /// ```
3441 pub fn to_undirected(
3442 &self,
3443 mode: crate::algorithms::operators::to_undirected::ToUndirectedMode,
3444 ) -> IgraphResult<Graph> {
3445 crate::algorithms::operators::to_undirected::to_undirected(self, mode)
3446 }
3447
3448 /// Contract vertices according to a mapping.
3449 ///
3450 /// `mapping[v]` specifies the new vertex id for vertex `v`. Vertices
3451 /// with the same mapping value are merged into one vertex.
3452 ///
3453 /// # Examples
3454 ///
3455 /// ```
3456 /// use rust_igraph::Graph;
3457 ///
3458 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3459 /// // Merge vertices 0,1 → 0 and 2,3 → 1
3460 /// let contracted = g.contract_vertices(&[0, 0, 1, 1]).unwrap();
3461 /// assert_eq!(contracted.vcount(), 2);
3462 /// ```
3463 pub fn contract_vertices(&self, mapping: &[VertexId]) -> IgraphResult<Graph> {
3464 crate::algorithms::operators::contract_vertices::contract_vertices(self, mapping)
3465 }
3466
3467 /// Perform a random walk starting from a given vertex.
3468 ///
3469 /// Returns the sequence of visited vertex ids (length = `steps + 1`
3470 /// including the starting vertex, or shorter if the walk gets stuck).
3471 ///
3472 /// # Examples
3473 ///
3474 /// ```
3475 /// use rust_igraph::Graph;
3476 ///
3477 /// let g = Graph::from_edges(
3478 /// &[(0,1), (1,2), (2,3), (3,0)], false, None
3479 /// ).unwrap();
3480 /// let (vertices, edges) = g.random_walk(0, 10, 42).unwrap();
3481 /// assert_eq!(vertices[0], 0);
3482 /// assert!(vertices.len() <= 11);
3483 /// assert_eq!(edges.len(), vertices.len() - 1);
3484 /// ```
3485 pub fn random_walk(
3486 &self,
3487 start: VertexId,
3488 steps: u32,
3489 seed: u64,
3490 ) -> IgraphResult<(Vec<VertexId>, Vec<EdgeId>)> {
3491 use crate::algorithms::paths::dijkstra::DijkstraMode;
3492 let mode = if self.directed {
3493 DijkstraMode::Out
3494 } else {
3495 DijkstraMode::All
3496 };
3497 crate::algorithms::paths::random_walk::random_walk(self, None, start, mode, steps, seed)
3498 }
3499
3500 // ── Graph properties ─────────────────────────────────────────────
3501
3502 /// Compute the radius (minimum eccentricity) of the graph.
3503 ///
3504 /// Returns `None` for the empty graph.
3505 ///
3506 /// # Examples
3507 ///
3508 /// ```
3509 /// use rust_igraph::Graph;
3510 ///
3511 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3512 /// assert_eq!(g.radius().unwrap(), Some(2));
3513 /// ```
3514 pub fn radius(&self) -> IgraphResult<Option<u32>> {
3515 crate::algorithms::paths::radii::radius(self)
3516 }
3517
3518 /// Compute the eccentricity of every vertex.
3519 ///
3520 /// `result[v]` is the maximum shortest-path distance from vertex `v`.
3521 ///
3522 /// # Examples
3523 ///
3524 /// ```
3525 /// use rust_igraph::Graph;
3526 ///
3527 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3528 /// assert_eq!(g.eccentricity().unwrap(), vec![2, 1, 2]);
3529 /// ```
3530 pub fn eccentricity(&self) -> IgraphResult<Vec<u32>> {
3531 crate::algorithms::paths::radii::eccentricity(self)
3532 }
3533
3534 /// Compute the girth (length of the shortest cycle) of the graph.
3535 ///
3536 /// Returns `None` if the graph is acyclic.
3537 ///
3538 /// # Examples
3539 ///
3540 /// ```
3541 /// use rust_igraph::Graph;
3542 ///
3543 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3544 /// assert_eq!(g.girth().unwrap(), Some(3));
3545 /// ```
3546 pub fn girth(&self) -> IgraphResult<Option<u32>> {
3547 crate::algorithms::properties::girth::girth(self)
3548 }
3549
3550 /// Check whether the graph is a tree.
3551 ///
3552 /// Returns `Some(root)` where `root` is the first root vertex found,
3553 /// or `None` if the graph is not a tree. The `mode` parameter controls
3554 /// how edges are followed for directed graphs.
3555 ///
3556 /// # Examples
3557 ///
3558 /// ```
3559 /// use rust_igraph::{Graph, DijkstraMode};
3560 ///
3561 /// let g = Graph::from_edges(&[(0,1), (1,2), (1,3)], false, None).unwrap();
3562 /// assert!(g.is_tree(DijkstraMode::All).unwrap().is_some());
3563 /// ```
3564 pub fn is_tree(
3565 &self,
3566 mode: crate::algorithms::paths::dijkstra::DijkstraMode,
3567 ) -> IgraphResult<Option<VertexId>> {
3568 crate::algorithms::properties::is_tree::is_tree(self, mode)
3569 }
3570
3571 /// Check whether the directed graph is a DAG.
3572 ///
3573 /// Returns `false` for undirected graphs.
3574 ///
3575 /// # Examples
3576 ///
3577 /// ```
3578 /// use rust_igraph::Graph;
3579 ///
3580 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
3581 /// assert!(g.is_dag());
3582 /// ```
3583 pub fn is_dag(&self) -> bool {
3584 crate::algorithms::properties::is_dag::is_dag(self)
3585 }
3586
3587 /// Count the total number of triangles in the graph.
3588 ///
3589 /// # Examples
3590 ///
3591 /// ```
3592 /// use rust_igraph::Graph;
3593 ///
3594 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3595 /// assert_eq!(g.count_triangles().unwrap(), 1);
3596 /// ```
3597 pub fn count_triangles(&self) -> IgraphResult<u64> {
3598 crate::algorithms::properties::triangles::count_triangles(self)
3599 }
3600
3601 /// Compute the harmonic centrality of all vertices.
3602 ///
3603 /// Harmonic centrality of `v` is the sum of inverse distances
3604 /// from `v` to all other reachable vertices.
3605 ///
3606 /// # Examples
3607 ///
3608 /// ```
3609 /// use rust_igraph::Graph;
3610 ///
3611 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3612 /// let h = g.harmonic_centrality().unwrap();
3613 /// assert_eq!(h.len(), 3);
3614 /// ```
3615 pub fn harmonic_centrality(&self) -> IgraphResult<Vec<f64>> {
3616 crate::algorithms::properties::harmonic::harmonic_centrality(self)
3617 }
3618
3619 /// Compute the k-hop neighborhood size for every vertex.
3620 ///
3621 /// `result[v]` is the number of vertices within distance `order` from
3622 /// `v` (including `v` itself).
3623 ///
3624 /// # Examples
3625 ///
3626 /// ```
3627 /// use rust_igraph::Graph;
3628 ///
3629 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3630 /// let sizes = g.neighborhood_size(1).unwrap();
3631 /// assert_eq!(sizes, vec![2, 3, 3, 2]);
3632 /// ```
3633 pub fn neighborhood_size(&self, order: i32) -> IgraphResult<Vec<u32>> {
3634 crate::algorithms::properties::neighborhood::neighborhood_size(self, order)
3635 }
3636
3637 // ── Connectivity ─────────────────────────────────────────────────
3638
3639 /// Compute the vertex connectivity (minimum vertex cut) of the graph.
3640 ///
3641 /// # Examples
3642 ///
3643 /// ```
3644 /// use rust_igraph::Graph;
3645 ///
3646 /// let g = Graph::from_edges(
3647 /// &[(0,1), (0,2), (1,3), (2,3)], false, None,
3648 /// ).unwrap();
3649 /// assert_eq!(g.vertex_connectivity().unwrap(), 2);
3650 /// ```
3651 pub fn vertex_connectivity(&self) -> IgraphResult<i64> {
3652 crate::algorithms::flow::vertex_connectivity::vertex_connectivity(self, true)
3653 }
3654
3655 /// Compute the edge connectivity (minimum edge cut) of the graph.
3656 ///
3657 /// # Examples
3658 ///
3659 /// ```
3660 /// use rust_igraph::Graph;
3661 ///
3662 /// let g = Graph::from_edges(
3663 /// &[(0,1), (0,2), (1,3), (2,3)], false, None,
3664 /// ).unwrap();
3665 /// assert_eq!(g.edge_connectivity().unwrap(), 2);
3666 /// ```
3667 pub fn edge_connectivity(&self) -> IgraphResult<i64> {
3668 crate::algorithms::flow::edge_connectivity::edge_connectivity(self, true)
3669 }
3670
3671 /// Find all vertices reachable from `source`.
3672 ///
3673 /// # Examples
3674 ///
3675 /// ```
3676 /// use rust_igraph::{Graph, SubcomponentMode};
3677 ///
3678 /// let g = Graph::from_edges(&[(0,1), (1,2), (3,4)], false, None).unwrap();
3679 /// let comp = g.subcomponent(0, SubcomponentMode::All).unwrap();
3680 /// assert_eq!(comp.len(), 3);
3681 /// ```
3682 pub fn subcomponent(
3683 &self,
3684 source: VertexId,
3685 mode: crate::algorithms::connectivity::subcomponent::SubcomponentMode,
3686 ) -> IgraphResult<Vec<VertexId>> {
3687 crate::algorithms::connectivity::subcomponent::subcomponent(self, source, mode)
3688 }
3689
3690 // ── Cliques ──────────────────────────────────────────────────────
3691
3692 /// Find all cliques in the graph within a size range.
3693 ///
3694 /// # Examples
3695 ///
3696 /// ```
3697 /// use rust_igraph::Graph;
3698 ///
3699 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3700 /// let c = g.cliques(3, 3, None).unwrap();
3701 /// assert_eq!(c.len(), 1);
3702 /// ```
3703 pub fn cliques(
3704 &self,
3705 min_size: u32,
3706 max_size: u32,
3707 max_results: Option<usize>,
3708 ) -> IgraphResult<Vec<Vec<VertexId>>> {
3709 crate::algorithms::cliques::cliques(self, min_size, max_size, max_results)
3710 }
3711
3712 /// Find all maximal cliques in the graph.
3713 ///
3714 /// # Examples
3715 ///
3716 /// ```
3717 /// use rust_igraph::Graph;
3718 ///
3719 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3720 /// let mc = g.maximal_cliques().unwrap();
3721 /// assert_eq!(mc.len(), 1);
3722 /// assert_eq!(mc[0].len(), 3);
3723 /// ```
3724 pub fn maximal_cliques(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
3725 crate::algorithms::cliques::maximal_cliques(self)
3726 }
3727
3728 /// Compute the independence number (max independent set size).
3729 ///
3730 /// # Examples
3731 ///
3732 /// ```
3733 /// use rust_igraph::Graph;
3734 ///
3735 /// // Triangle: independence number is 1 (can pick at most 1 non-adjacent vertex pair... no, 1)
3736 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3737 /// assert_eq!(g.independence_number().unwrap(), 1);
3738 /// ```
3739 pub fn independence_number(&self) -> IgraphResult<u32> {
3740 crate::algorithms::cliques::independence_number(self)
3741 }
3742
3743 // ── Operators ────────────────────────────────────────────────────
3744
3745 /// Permute the vertices of the graph.
3746 ///
3747 /// `permutation[v]` gives the new id for vertex `v`. Returns a new
3748 /// graph with edges reconnected accordingly.
3749 ///
3750 /// # Examples
3751 ///
3752 /// ```
3753 /// use rust_igraph::Graph;
3754 ///
3755 /// // permutation[new] = old: new 0 ← old 2, new 1 ← old 0, new 2 ← old 1
3756 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3757 /// let p = g.permute_vertices(&[2, 0, 1]).unwrap();
3758 /// assert!(p.has_edge(1, 2));
3759 /// assert!(p.has_edge(2, 0));
3760 /// ```
3761 pub fn permute_vertices(&self, permutation: &[VertexId]) -> IgraphResult<Graph> {
3762 crate::algorithms::operators::permute_vertices::permute_vertices(self, permutation)
3763 }
3764
3765 // ── Layout ───────────────────────────────────────────────────────
3766
3767 /// Fruchterman-Reingold force-directed layout with default parameters.
3768 ///
3769 /// # Examples
3770 ///
3771 /// ```
3772 /// use rust_igraph::Graph;
3773 ///
3774 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3775 /// let coords = g.layout_fruchterman_reingold().unwrap();
3776 /// assert_eq!(coords.len(), 3);
3777 /// ```
3778 pub fn layout_fruchterman_reingold(&self) -> IgraphResult<Vec<(f64, f64)>> {
3779 use crate::algorithms::layout::fruchterman_reingold::FrParams;
3780 crate::algorithms::layout::fruchterman_reingold::layout_fruchterman_reingold(
3781 self,
3782 &FrParams::default(),
3783 )
3784 }
3785
3786 /// Kamada-Kawai spring-embedder layout with default parameters.
3787 ///
3788 /// # Examples
3789 ///
3790 /// ```
3791 /// use rust_igraph::Graph;
3792 ///
3793 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3794 /// let coords = g.layout_kamada_kawai().unwrap();
3795 /// assert_eq!(coords.len(), 4);
3796 /// ```
3797 pub fn layout_kamada_kawai(&self) -> IgraphResult<Vec<[f64; 2]>> {
3798 use crate::algorithms::layout::kamada_kawai::KkParams;
3799 let params = KkParams::default_for(self.vcount() as usize);
3800 crate::algorithms::layout::kamada_kawai::layout_kamada_kawai(self, None, ¶ms, None)
3801 }
3802
3803 /// `DrL` (Distributed Recursive Layout) with default options.
3804 ///
3805 /// # Examples
3806 ///
3807 /// ```
3808 /// use rust_igraph::Graph;
3809 ///
3810 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3811 /// let coords = g.layout_drl().unwrap();
3812 /// assert_eq!(coords.len(), 3);
3813 /// ```
3814 pub fn layout_drl(&self) -> IgraphResult<Vec<[f64; 2]>> {
3815 use crate::algorithms::layout::drl::DrlOptions;
3816 crate::algorithms::layout::drl::layout_drl(self, None, &DrlOptions::default(), None)
3817 }
3818
3819 /// Circular layout.
3820 ///
3821 /// # Examples
3822 ///
3823 /// ```
3824 /// use rust_igraph::Graph;
3825 ///
3826 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3827 /// let coords = g.layout_circle();
3828 /// assert_eq!(coords.len(), 3);
3829 /// ```
3830 pub fn layout_circle(&self) -> Vec<(f64, f64)> {
3831 crate::algorithms::layout::simple::layout_circle(self, None)
3832 }
3833
3834 /// Random layout with the given RNG seed.
3835 ///
3836 /// # Examples
3837 ///
3838 /// ```
3839 /// use rust_igraph::Graph;
3840 ///
3841 /// let g = Graph::with_vertices(5);
3842 /// let coords = g.layout_random(42);
3843 /// assert_eq!(coords.len(), 5);
3844 /// ```
3845 pub fn layout_random(&self, seed: u64) -> Vec<(f64, f64)> {
3846 crate::algorithms::layout::simple::layout_random(self, seed)
3847 }
3848
3849 /// Grid layout.
3850 ///
3851 /// `width` specifies the number of columns. Pass 0 to auto-compute
3852 /// (ceil of square root of vertex count).
3853 ///
3854 /// # Examples
3855 ///
3856 /// ```
3857 /// use rust_igraph::Graph;
3858 ///
3859 /// let g = Graph::with_vertices(9);
3860 /// let coords = g.layout_grid(3);
3861 /// assert_eq!(coords.len(), 9);
3862 /// ```
3863 pub fn layout_grid(&self, width: i32) -> Vec<(f64, f64)> {
3864 crate::algorithms::layout::simple::layout_grid(self, width)
3865 }
3866
3867 // ── Triangle / local clustering ──────────────────────────────────
3868
3869 /// Per-vertex triangle count.
3870 ///
3871 /// `result[v]` is the number of triangles incident to vertex `v`.
3872 ///
3873 /// # Examples
3874 ///
3875 /// ```
3876 /// use rust_igraph::Graph;
3877 ///
3878 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0),(2,3)], false, None).unwrap();
3879 /// let t = g.count_adjacent_triangles().unwrap();
3880 /// assert_eq!(t[0], 1);
3881 /// assert_eq!(t[3], 0);
3882 /// ```
3883 pub fn count_adjacent_triangles(&self) -> IgraphResult<Vec<u64>> {
3884 crate::algorithms::properties::triangles::count_adjacent_triangles(self)
3885 }
3886
3887 /// Per-vertex local clustering coefficient (local transitivity).
3888 ///
3889 /// `result[v]` is `None` for vertices with degree < 2.
3890 ///
3891 /// # Examples
3892 ///
3893 /// ```
3894 /// use rust_igraph::Graph;
3895 ///
3896 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0),(2,3)], false, None).unwrap();
3897 /// let lcc = g.transitivity_local_undirected().unwrap();
3898 /// assert!(lcc[0].unwrap() > 0.9); // vertex 0 in triangle
3899 /// assert!(lcc[3].is_none()); // degree 1
3900 /// ```
3901 pub fn transitivity_local_undirected(&self) -> IgraphResult<Vec<Option<f64>>> {
3902 crate::algorithms::properties::triangles::transitivity_local_undirected(self)
3903 }
3904
3905 // ── Network metrics ──────────────────────────────────────────────
3906
3907 /// Reciprocity of a directed graph.
3908 ///
3909 /// Returns the fraction of edges that are reciprocated, or `None` for
3910 /// empty graphs.
3911 ///
3912 /// # Examples
3913 ///
3914 /// ```
3915 /// use rust_igraph::Graph;
3916 ///
3917 /// let g = Graph::from_edges(&[(0,1),(1,0),(1,2)], true, None).unwrap();
3918 /// let r = g.reciprocity().unwrap().unwrap();
3919 /// assert!((r - 2.0/3.0).abs() < 1e-12);
3920 /// ```
3921 pub fn reciprocity(&self) -> IgraphResult<Option<f64>> {
3922 crate::algorithms::properties::reciprocity::reciprocity(self)
3923 }
3924
3925 /// Burt's constraint for each vertex.
3926 ///
3927 /// # Examples
3928 ///
3929 /// ```
3930 /// use rust_igraph::Graph;
3931 ///
3932 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,2)], false, None).unwrap();
3933 /// let c = g.constraint(None).unwrap();
3934 /// assert_eq!(c.len(), 3);
3935 /// ```
3936 pub fn constraint(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
3937 crate::algorithms::properties::constraint::constraint(self, weights)
3938 }
3939
3940 /// Whether the graph has multi-edges.
3941 ///
3942 /// # Examples
3943 ///
3944 /// ```
3945 /// use rust_igraph::Graph;
3946 ///
3947 /// let g = Graph::from_edges(&[(0,1),(0,1)], false, None).unwrap();
3948 /// assert!(g.has_multiple().unwrap());
3949 /// ```
3950 pub fn has_multiple(&self) -> IgraphResult<bool> {
3951 crate::algorithms::properties::multiplicity::has_multiple(self)
3952 }
3953
3954 /// Per-edge multiplicity count.
3955 ///
3956 /// # Examples
3957 ///
3958 /// ```
3959 /// use rust_igraph::Graph;
3960 ///
3961 /// let g = Graph::from_edges(&[(0,1),(0,1),(1,2)], false, None).unwrap();
3962 /// let mc = g.count_multiple().unwrap();
3963 /// assert_eq!(mc[0], 2);
3964 /// assert_eq!(mc[2], 1);
3965 /// ```
3966 pub fn count_multiple(&self) -> IgraphResult<Vec<usize>> {
3967 crate::algorithms::properties::multiplicity::count_multiple(self)
3968 }
3969
3970 /// Test whether two vertices are adjacent.
3971 ///
3972 /// # Examples
3973 ///
3974 /// ```
3975 /// use rust_igraph::Graph;
3976 ///
3977 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
3978 /// assert!(g.are_adjacent(0, 1).unwrap());
3979 /// assert!(!g.are_adjacent(0, 2).unwrap());
3980 /// ```
3981 pub fn are_adjacent(&self, v1: VertexId, v2: VertexId) -> IgraphResult<bool> {
3982 crate::algorithms::properties::are_adjacent::are_adjacent(self, v1, v2)
3983 }
3984
3985 // ── Motifs ───────────────────────────────────────────────────────
3986
3987 /// Triad census of a directed graph.
3988 ///
3989 /// # Examples
3990 ///
3991 /// ```
3992 /// use rust_igraph::{Graph, TriadType};
3993 ///
3994 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], true, None).unwrap();
3995 /// let tc = g.triad_census().unwrap();
3996 /// assert!(tc.get(TriadType::T030C) > 0.0);
3997 /// ```
3998 pub fn triad_census(
3999 &self,
4000 ) -> IgraphResult<crate::algorithms::motifs::triad_census::TriadCensus> {
4001 crate::algorithms::motifs::triad_census::triad_census(self)
4002 }
4003
4004 /// Dyad census of a directed graph.
4005 ///
4006 /// # Examples
4007 ///
4008 /// ```
4009 /// use rust_igraph::Graph;
4010 ///
4011 /// let g = Graph::from_edges(&[(0,1),(1,0),(1,2)], true, None).unwrap();
4012 /// let dc = g.dyad_census().unwrap();
4013 /// assert!((dc.mutual - 1.0).abs() < 1e-12);
4014 /// ```
4015 pub fn dyad_census(&self) -> IgraphResult<crate::algorithms::motifs::DyadCensus> {
4016 crate::algorithms::motifs::dyad_census(self)
4017 }
4018
4019 // ── Similarity ───────────────────────────────────────────────────
4020
4021 /// Jaccard similarity between all pairs of vertices.
4022 ///
4023 /// Returns a flattened `n × n` matrix (row-major).
4024 ///
4025 /// # Examples
4026 ///
4027 /// ```
4028 /// use rust_igraph::Graph;
4029 ///
4030 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,2)], false, None).unwrap();
4031 /// let sim = g.similarity_jaccard().unwrap();
4032 /// assert_eq!(sim.len(), 9); // 3×3 matrix
4033 /// ```
4034 pub fn similarity_jaccard(&self) -> IgraphResult<Vec<f64>> {
4035 crate::algorithms::properties::similarity::similarity_jaccard(self)
4036 }
4037
4038 /// Co-citation scores for all vertex pairs.
4039 ///
4040 /// # Examples
4041 ///
4042 /// ```
4043 /// use rust_igraph::Graph;
4044 ///
4045 /// let g = Graph::from_edges(&[(0,2),(1,2)], true, None).unwrap();
4046 /// let cc = g.cocitation().unwrap();
4047 /// assert!(!cc.is_empty());
4048 /// ```
4049 pub fn cocitation(&self) -> IgraphResult<Vec<u32>> {
4050 crate::algorithms::properties::similarity::cocitation(self)
4051 }
4052
4053 // ── Graph structure recognizers ─────────────────────────────────
4054
4055 /// Check whether the graph is a cograph.
4056 ///
4057 /// # Examples
4058 ///
4059 /// ```
4060 /// use rust_igraph::Graph;
4061 ///
4062 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
4063 /// assert!(g.is_cograph().unwrap());
4064 /// ```
4065 pub fn is_cograph(&self) -> IgraphResult<bool> {
4066 crate::algorithms::properties::is_cograph::is_cograph(self)
4067 }
4068
4069 /// Check whether the graph is series-parallel.
4070 ///
4071 /// # Examples
4072 ///
4073 /// ```
4074 /// use rust_igraph::Graph;
4075 ///
4076 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4077 /// assert!(g.is_series_parallel().unwrap());
4078 /// ```
4079 pub fn is_series_parallel(&self) -> IgraphResult<bool> {
4080 crate::algorithms::properties::is_series_parallel::is_series_parallel(self)
4081 }
4082
4083 /// Check whether the graph is outerplanar.
4084 ///
4085 /// # Examples
4086 ///
4087 /// ```
4088 /// use rust_igraph::Graph;
4089 ///
4090 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4091 /// assert!(g.is_outerplanar().unwrap());
4092 /// ```
4093 pub fn is_outerplanar(&self) -> IgraphResult<bool> {
4094 crate::algorithms::properties::is_outerplanar::is_outerplanar(self)
4095 }
4096
4097 /// Check whether the graph is chordal.
4098 ///
4099 /// # Examples
4100 ///
4101 /// ```
4102 /// use rust_igraph::Graph;
4103 ///
4104 /// let g = Graph::from_edges(
4105 /// &[(0,1), (1,2), (2,0), (0,3), (1,3), (2,3)], false, None
4106 /// ).unwrap();
4107 /// assert!(g.is_chordal().unwrap());
4108 /// ```
4109 pub fn is_chordal(&self) -> IgraphResult<bool> {
4110 let result = crate::algorithms::chordality::is_chordal(self, None)?;
4111 Ok(result.chordal)
4112 }
4113
4114 /// Check whether the graph is a forest (acyclic).
4115 ///
4116 /// # Examples
4117 ///
4118 /// ```
4119 /// use rust_igraph::Graph;
4120 ///
4121 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4122 /// assert!(g.is_forest().unwrap());
4123 /// ```
4124 pub fn is_forest(&self) -> IgraphResult<bool> {
4125 Ok(crate::algorithms::properties::is_forest::is_forest(
4126 self,
4127 crate::algorithms::paths::dijkstra::DijkstraMode::Out,
4128 )?
4129 .is_some())
4130 }
4131
4132 // ── Cycles and motifs ───────────────────────────────────────────
4133
4134 /// Compute a fundamental cycle basis of the graph.
4135 ///
4136 /// # Examples
4137 ///
4138 /// ```
4139 /// use rust_igraph::Graph;
4140 ///
4141 /// let g = Graph::from_edges(
4142 /// &[(0,1), (1,2), (2,0)], false, None
4143 /// ).unwrap();
4144 /// let cycles = g.fundamental_cycles().unwrap();
4145 /// assert_eq!(cycles.len(), 1);
4146 /// ```
4147 pub fn fundamental_cycles(&self) -> IgraphResult<Vec<Vec<u32>>> {
4148 crate::algorithms::fundamental_cycles::fundamental_cycles(self, None, None)
4149 }
4150
4151 /// Compute a minimum weight cycle basis.
4152 ///
4153 /// # Examples
4154 ///
4155 /// ```
4156 /// use rust_igraph::Graph;
4157 ///
4158 /// let g = Graph::from_edges(
4159 /// &[(0,1), (1,2), (2,0), (1,3), (3,0)], false, None
4160 /// ).unwrap();
4161 /// let basis = g.minimum_cycle_basis().unwrap();
4162 /// assert_eq!(basis.len(), 2);
4163 /// ```
4164 pub fn minimum_cycle_basis(&self) -> IgraphResult<Vec<Vec<u32>>> {
4165 crate::algorithms::minimum_cycle_basis::minimum_cycle_basis(self, None, false)
4166 }
4167
4168 // ── Cuts, covers, and sets ──────────────────────────────────────
4169
4170 /// Find a minimum feedback arc set.
4171 ///
4172 /// Returns edges whose removal makes the graph acyclic.
4173 ///
4174 /// # Examples
4175 ///
4176 /// ```
4177 /// use rust_igraph::Graph;
4178 ///
4179 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], true, None).unwrap();
4180 /// let fas = g.feedback_arc_set().unwrap();
4181 /// assert!(!fas.is_empty());
4182 /// ```
4183 pub fn feedback_arc_set(&self) -> IgraphResult<Vec<u32>> {
4184 crate::algorithms::feedback_arc_set::feedback_arc_set(
4185 self,
4186 None,
4187 crate::algorithms::feedback_arc_set::FasAlgorithm::EadesLinSmyth,
4188 )
4189 }
4190
4191 /// Find the maximum cut of the graph.
4192 ///
4193 /// # Examples
4194 ///
4195 /// ```
4196 /// use rust_igraph::Graph;
4197 ///
4198 /// let g = Graph::from_edges(
4199 /// &[(0,1), (1,2), (2,3)], false, None
4200 /// ).unwrap();
4201 /// let result = g.maximum_cut().unwrap();
4202 /// assert!(result.cut_value > 0);
4203 /// ```
4204 pub fn maximum_cut(&self) -> IgraphResult<crate::algorithms::max_cut::MaxCutResult> {
4205 crate::algorithms::max_cut::maximum_cut(self)
4206 }
4207
4208 /// Find a minimum vertex cover.
4209 ///
4210 /// # Examples
4211 ///
4212 /// ```
4213 /// use rust_igraph::Graph;
4214 ///
4215 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4216 /// let cover = g.minimum_vertex_cover().unwrap();
4217 /// assert!(cover.contains(&1));
4218 /// ```
4219 pub fn minimum_vertex_cover(&self) -> IgraphResult<Vec<u32>> {
4220 crate::algorithms::vertex_cover::minimum_vertex_cover(self)
4221 }
4222
4223 /// Find a minimum edge cover.
4224 ///
4225 /// # Examples
4226 ///
4227 /// ```
4228 /// use rust_igraph::Graph;
4229 ///
4230 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4231 /// let cover = g.minimum_edge_cover().unwrap();
4232 /// assert!(!cover.is_empty());
4233 /// ```
4234 pub fn minimum_edge_cover(&self) -> IgraphResult<Vec<u32>> {
4235 crate::algorithms::edge_cover::minimum_edge_cover(self)
4236 }
4237
4238 /// Find a maximum independent set.
4239 ///
4240 /// # Examples
4241 ///
4242 /// ```
4243 /// use rust_igraph::Graph;
4244 ///
4245 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4246 /// let mis = g.maximum_independent_set().unwrap();
4247 /// assert_eq!(mis.len(), 2);
4248 /// ```
4249 pub fn maximum_independent_set(&self) -> IgraphResult<Vec<u32>> {
4250 crate::algorithms::independent_set::maximum_independent_set(self)
4251 }
4252
4253 // ── Coloring ────────────────────────────────────────────────────
4254
4255 /// Greedy vertex coloring.
4256 ///
4257 /// # Examples
4258 ///
4259 /// ```
4260 /// use rust_igraph::Graph;
4261 ///
4262 /// let g = Graph::from_edges(
4263 /// &[(0,1), (1,2), (2,0)], false, None
4264 /// ).unwrap();
4265 /// let colors = g.vertex_coloring().unwrap();
4266 /// assert_eq!(colors.len(), 3);
4267 /// // Adjacent vertices must have different colors
4268 /// assert_ne!(colors[0], colors[1]);
4269 /// ```
4270 pub fn vertex_coloring(&self) -> IgraphResult<Vec<u32>> {
4271 crate::algorithms::coloring::vertex_coloring_greedy(
4272 self,
4273 crate::algorithms::coloring::GreedyColoringHeuristic::ColoredNeighbors,
4274 )
4275 }
4276
4277 // ── Spanning trees ──────────────────────────────────────────────
4278
4279 /// Sample a random spanning tree.
4280 ///
4281 /// # Examples
4282 ///
4283 /// ```
4284 /// use rust_igraph::Graph;
4285 ///
4286 /// let g = Graph::from_edges(
4287 /// &[(0,1), (1,2), (2,0), (1,3)], false, None
4288 /// ).unwrap();
4289 /// let edges = g.random_spanning_tree(42).unwrap();
4290 /// assert_eq!(edges.len(), 3);
4291 /// ```
4292 pub fn random_spanning_tree(&self, seed: u64) -> IgraphResult<Vec<u32>> {
4293 crate::algorithms::spanning::random_spanning_tree::random_spanning_tree(self, None, seed)
4294 }
4295
4296 // ── Community detection (extended) ──────────────────────────────
4297
4298 /// Edge betweenness community detection.
4299 ///
4300 /// # Examples
4301 ///
4302 /// ```
4303 /// use rust_igraph::Graph;
4304 ///
4305 /// let g = Graph::from_edges(
4306 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (2,3)],
4307 /// false, None
4308 /// ).unwrap();
4309 /// let result = g.edge_betweenness_community().unwrap();
4310 /// assert!(!result.membership.is_empty());
4311 /// ```
4312 pub fn edge_betweenness_community(
4313 &self,
4314 ) -> IgraphResult<crate::algorithms::community::edge_betweenness_community::EdgeBetweennessResult>
4315 {
4316 crate::algorithms::community::edge_betweenness_community::edge_betweenness_community(self)
4317 }
4318
4319 // ── Isomorphism (canonical / BLISS) ─────────────────────────────
4320
4321 /// Compute a canonical vertex permutation.
4322 ///
4323 /// # Examples
4324 ///
4325 /// ```
4326 /// use rust_igraph::Graph;
4327 ///
4328 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4329 /// let perm = g.canonical_permutation().unwrap();
4330 /// assert_eq!(perm.len(), 3);
4331 /// ```
4332 pub fn canonical_permutation(&self) -> IgraphResult<Vec<u32>> {
4333 crate::algorithms::isomorphism::canonical::canonical_permutation::canonical_permutation(
4334 self, None,
4335 )
4336 }
4337
4338 /// Count automorphisms of the graph.
4339 ///
4340 /// # Examples
4341 ///
4342 /// ```
4343 /// use rust_igraph::Graph;
4344 ///
4345 /// // K3 has 3! = 6 automorphisms
4346 /// let g = Graph::from_edges(
4347 /// &[(0,1), (1,2), (2,0)], false, None
4348 /// ).unwrap();
4349 /// let count = g.count_automorphisms().unwrap();
4350 /// assert!((count - 6.0).abs() < 1e-10);
4351 /// ```
4352 pub fn count_automorphisms(&self) -> IgraphResult<f64> {
4353 crate::algorithms::isomorphism::canonical::count_automorphisms::count_automorphisms(
4354 self, None,
4355 )
4356 }
4357
4358 /// Compute a generating set for the automorphism group.
4359 ///
4360 /// # Examples
4361 ///
4362 /// ```
4363 /// use rust_igraph::Graph;
4364 ///
4365 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4366 /// let gens = g.automorphism_group().unwrap();
4367 /// assert!(!gens.is_empty());
4368 /// ```
4369 pub fn automorphism_group(&self) -> IgraphResult<Vec<Vec<u32>>> {
4370 crate::algorithms::isomorphism::canonical::automorphism_group::automorphism_group(
4371 self, None,
4372 )
4373 }
4374
4375 // ── Epidemics ───────────────────────────────────────────────────
4376
4377 /// Run a single SIR (Susceptible-Infected-Recovered) simulation.
4378 ///
4379 /// `beta` is the infection rate, `gamma` is the recovery rate.
4380 ///
4381 /// # Examples
4382 ///
4383 /// ```
4384 /// use rust_igraph::Graph;
4385 ///
4386 /// let g = Graph::from_edges(
4387 /// &[(0,1), (1,2), (2,3), (3,4)], false, None
4388 /// ).unwrap();
4389 /// let result = g.sir(0.5, 0.1, 1, 42).unwrap();
4390 /// assert!(!result.is_empty());
4391 /// ```
4392 pub fn sir(
4393 &self,
4394 beta: f64,
4395 gamma: f64,
4396 no_sim: usize,
4397 seed: u64,
4398 ) -> IgraphResult<Vec<crate::algorithms::epidemics::Sir>> {
4399 crate::algorithms::epidemics::sir(self, beta, gamma, no_sim, seed)
4400 }
4401
4402 // ── Spanner ─────────────────────────────────────────────────────
4403
4404 /// Compute a graph spanner with the given stretch factor.
4405 ///
4406 /// Returns edge indices forming the spanner subgraph.
4407 ///
4408 /// # Examples
4409 ///
4410 /// ```
4411 /// use rust_igraph::Graph;
4412 ///
4413 /// let g = Graph::from_edges(
4414 /// &[(0,1), (1,2), (2,0), (1,3)], false, None
4415 /// ).unwrap();
4416 /// let edges = g.spanner(3.0).unwrap();
4417 /// assert!(!edges.is_empty());
4418 /// ```
4419 pub fn spanner(&self, stretch: f64) -> IgraphResult<Vec<u32>> {
4420 crate::algorithms::paths::spanner::spanner(self, stretch, None)
4421 }
4422
4423 // ── Graph recognizers ─────────────────────────────────────────
4424
4425 /// Check whether this graph is acyclic (a DAG for directed, forest for undirected).
4426 ///
4427 /// # Examples
4428 ///
4429 /// ```
4430 /// use rust_igraph::Graph;
4431 ///
4432 /// let tree = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4433 /// assert!(tree.is_acyclic());
4434 /// ```
4435 pub fn is_acyclic(&self) -> bool {
4436 crate::algorithms::properties::is_acyclic::is_acyclic(self)
4437 }
4438
4439 /// Check whether this graph is an apex forest.
4440 ///
4441 /// # Examples
4442 ///
4443 /// ```
4444 /// use rust_igraph::Graph;
4445 ///
4446 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4447 /// assert!(g.is_apex_forest().unwrap());
4448 /// ```
4449 pub fn is_apex_forest(&self) -> IgraphResult<bool> {
4450 crate::algorithms::properties::is_apex_forest::is_apex_forest(self)
4451 }
4452
4453 /// Check whether this graph is an apex tree.
4454 ///
4455 /// # Examples
4456 ///
4457 /// ```
4458 /// use rust_igraph::Graph;
4459 ///
4460 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4461 /// assert!(g.is_apex_tree().unwrap());
4462 /// ```
4463 pub fn is_apex_tree(&self) -> IgraphResult<bool> {
4464 crate::algorithms::properties::is_apex_tree::is_apex_tree(self)
4465 }
4466
4467 /// Check whether this graph is banner-free.
4468 ///
4469 /// # Examples
4470 ///
4471 /// ```
4472 /// use rust_igraph::Graph;
4473 ///
4474 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4475 /// assert!(g.is_banner_free().unwrap());
4476 /// ```
4477 pub fn is_banner_free(&self) -> IgraphResult<bool> {
4478 crate::algorithms::properties::is_banner_free::is_banner_free(self)
4479 }
4480
4481 /// Check whether this graph is a biclique.
4482 ///
4483 /// # Examples
4484 ///
4485 /// ```
4486 /// use rust_igraph::Graph;
4487 ///
4488 /// let g = Graph::from_edges(&[(0,2), (0,3), (1,2), (1,3)], false, None).unwrap();
4489 /// assert!(g.is_biclique().unwrap());
4490 /// ```
4491 pub fn is_biclique(&self) -> IgraphResult<bool> {
4492 crate::algorithms::properties::is_biclique::is_biclique(self)
4493 }
4494
4495 /// Check whether this graph is biregular.
4496 ///
4497 /// # Examples
4498 ///
4499 /// ```
4500 /// use rust_igraph::Graph;
4501 ///
4502 /// let g = Graph::from_edges(&[(0,2), (0,3), (1,2), (1,3)], false, None).unwrap();
4503 /// assert!(g.is_biregular().unwrap());
4504 /// ```
4505 pub fn is_biregular(&self) -> IgraphResult<bool> {
4506 crate::algorithms::properties::is_biregular::is_biregular(self)
4507 }
4508
4509 /// Check whether this graph is a block graph.
4510 ///
4511 /// # Examples
4512 ///
4513 /// ```
4514 /// use rust_igraph::Graph;
4515 ///
4516 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4517 /// assert!(g.is_block_graph().unwrap());
4518 /// ```
4519 pub fn is_block_graph(&self) -> IgraphResult<bool> {
4520 crate::algorithms::properties::is_block::is_block_graph(self)
4521 }
4522
4523 /// Check whether this graph is bowtie-free.
4524 ///
4525 /// # Examples
4526 ///
4527 /// ```
4528 /// use rust_igraph::Graph;
4529 ///
4530 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4531 /// assert!(g.is_bowtie_free().unwrap());
4532 /// ```
4533 pub fn is_bowtie_free(&self) -> IgraphResult<bool> {
4534 crate::algorithms::properties::is_bowtie_free::is_bowtie_free(self)
4535 }
4536
4537 /// Check whether this graph is bull-free.
4538 ///
4539 /// # Examples
4540 ///
4541 /// ```
4542 /// use rust_igraph::Graph;
4543 ///
4544 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4545 /// assert!(g.is_bull_free().unwrap());
4546 /// ```
4547 pub fn is_bull_free(&self) -> IgraphResult<bool> {
4548 crate::algorithms::properties::is_bull_free::is_bull_free(self)
4549 }
4550
4551 /// Check whether this graph is C4-free (contains no 4-cycle).
4552 ///
4553 /// # Examples
4554 ///
4555 /// ```
4556 /// use rust_igraph::Graph;
4557 ///
4558 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4559 /// assert!(g.is_c4_free().unwrap());
4560 /// ```
4561 pub fn is_c4_free(&self) -> IgraphResult<bool> {
4562 crate::algorithms::properties::is_c4_free::is_c4_free(self)
4563 }
4564
4565 /// Check whether this graph is C5-free.
4566 ///
4567 /// # Examples
4568 ///
4569 /// ```
4570 /// use rust_igraph::Graph;
4571 ///
4572 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4573 /// assert!(g.is_c5_free().unwrap());
4574 /// ```
4575 pub fn is_c5_free(&self) -> IgraphResult<bool> {
4576 crate::algorithms::properties::is_c5_free::is_c5_free(self)
4577 }
4578
4579 /// Check whether this graph is a cactus graph.
4580 ///
4581 /// # Examples
4582 ///
4583 /// ```
4584 /// use rust_igraph::Graph;
4585 ///
4586 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4587 /// assert!(g.is_cactus_graph().unwrap());
4588 /// ```
4589 pub fn is_cactus_graph(&self) -> IgraphResult<bool> {
4590 crate::algorithms::properties::is_cactus::is_cactus_graph(self)
4591 }
4592
4593 /// Check whether this graph is a caterpillar.
4594 ///
4595 /// # Examples
4596 ///
4597 /// ```
4598 /// use rust_igraph::Graph;
4599 ///
4600 /// let g = Graph::from_edges(&[(0,1), (1,2), (1,3)], false, None).unwrap();
4601 /// assert!(g.is_caterpillar().unwrap());
4602 /// ```
4603 pub fn is_caterpillar(&self) -> IgraphResult<bool> {
4604 crate::algorithms::properties::is_caterpillar::is_caterpillar(self)
4605 }
4606
4607 /// Check whether this graph is a chain graph.
4608 ///
4609 /// # Examples
4610 ///
4611 /// ```
4612 /// use rust_igraph::Graph;
4613 ///
4614 /// let g = Graph::from_edges(&[(0,2), (0,3), (1,3)], false, None).unwrap();
4615 /// assert!(g.is_chain_graph().unwrap());
4616 /// ```
4617 pub fn is_chain_graph(&self) -> IgraphResult<bool> {
4618 crate::algorithms::properties::is_chain_graph::is_chain_graph(self)
4619 }
4620
4621 /// Check whether this graph is chordal bipartite.
4622 ///
4623 /// # Examples
4624 ///
4625 /// ```
4626 /// use rust_igraph::Graph;
4627 ///
4628 /// let g = Graph::from_edges(&[(0,2), (1,2)], false, None).unwrap();
4629 /// assert!(g.is_chordal_bipartite().unwrap());
4630 /// ```
4631 pub fn is_chordal_bipartite(&self) -> IgraphResult<bool> {
4632 crate::algorithms::properties::is_chordal_bipartite::is_chordal_bipartite(self)
4633 }
4634
4635 /// Check whether this graph is claw-free.
4636 ///
4637 /// # Examples
4638 ///
4639 /// ```
4640 /// use rust_igraph::Graph;
4641 ///
4642 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4643 /// assert!(g.is_claw_free().unwrap());
4644 /// ```
4645 pub fn is_claw_free(&self) -> IgraphResult<bool> {
4646 crate::algorithms::properties::is_claw_free::is_claw_free(self)
4647 }
4648
4649 /// Check whether this graph is a cluster graph (disjoint union of cliques).
4650 ///
4651 /// # Examples
4652 ///
4653 /// ```
4654 /// use rust_igraph::Graph;
4655 ///
4656 /// let g = Graph::from_edges(&[(0,1)], false, None).unwrap();
4657 /// assert!(g.is_cluster_graph().unwrap());
4658 /// ```
4659 pub fn is_cluster_graph(&self) -> IgraphResult<bool> {
4660 crate::algorithms::properties::is_cluster::is_cluster_graph(self)
4661 }
4662
4663 /// Check whether this graph is co-bipartite.
4664 ///
4665 /// # Examples
4666 ///
4667 /// ```
4668 /// use rust_igraph::Graph;
4669 ///
4670 /// let g = Graph::from_edges(&[(0,1)], false, None).unwrap();
4671 /// assert!(g.is_co_bipartite().unwrap());
4672 /// ```
4673 pub fn is_co_bipartite(&self) -> IgraphResult<bool> {
4674 crate::algorithms::properties::is_co_bipartite::is_co_bipartite(self)
4675 }
4676
4677 /// Check whether this graph is co-chordal.
4678 ///
4679 /// # Examples
4680 ///
4681 /// ```
4682 /// use rust_igraph::Graph;
4683 ///
4684 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4685 /// assert!(g.is_co_chordal().unwrap());
4686 /// ```
4687 pub fn is_co_chordal(&self) -> IgraphResult<bool> {
4688 crate::algorithms::properties::is_co_chordal::is_co_chordal(self)
4689 }
4690
4691 /// Check whether this graph is a complete graph.
4692 ///
4693 /// # Examples
4694 ///
4695 /// ```
4696 /// use rust_igraph::Graph;
4697 ///
4698 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4699 /// assert!(g.is_complete().unwrap());
4700 /// ```
4701 pub fn is_complete(&self) -> IgraphResult<bool> {
4702 crate::algorithms::properties::is_complete::is_complete(self)
4703 }
4704
4705 /// Check whether this graph is a complete bipartite graph.
4706 ///
4707 /// # Examples
4708 ///
4709 /// ```
4710 /// use rust_igraph::Graph;
4711 ///
4712 /// let g = Graph::from_edges(&[(0,2), (0,3), (1,2), (1,3)], false, None).unwrap();
4713 /// assert!(g.is_complete_bipartite().unwrap());
4714 /// ```
4715 pub fn is_complete_bipartite(&self) -> IgraphResult<bool> {
4716 crate::algorithms::properties::is_complete_bipartite::is_complete_bipartite(self)
4717 }
4718
4719 /// Check whether this graph is cricket-free.
4720 ///
4721 /// # Examples
4722 ///
4723 /// ```
4724 /// use rust_igraph::Graph;
4725 ///
4726 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4727 /// assert!(g.is_cricket_free().unwrap());
4728 /// ```
4729 pub fn is_cricket_free(&self) -> IgraphResult<bool> {
4730 crate::algorithms::properties::is_cricket_free::is_cricket_free(self)
4731 }
4732
4733 /// Check whether this graph is cubic (3-regular).
4734 ///
4735 /// # Examples
4736 ///
4737 /// ```
4738 /// use rust_igraph::{Graph, full_graph};
4739 ///
4740 /// let g = full_graph(4, false, false).unwrap();
4741 /// assert!(g.is_cubic().unwrap());
4742 /// ```
4743 pub fn is_cubic(&self) -> IgraphResult<bool> {
4744 crate::algorithms::properties::is_cubic::is_cubic(self)
4745 }
4746
4747 /// Check whether this graph is a cycle.
4748 ///
4749 /// # Examples
4750 ///
4751 /// ```
4752 /// use rust_igraph::{Graph, cycle_graph};
4753 ///
4754 /// let g = cycle_graph(5, false, false).unwrap();
4755 /// assert!(g.is_cycle().unwrap());
4756 /// ```
4757 pub fn is_cycle(&self) -> IgraphResult<bool> {
4758 crate::algorithms::properties::is_cycle::is_cycle(self)
4759 }
4760
4761 /// Check whether this graph is dart-free.
4762 ///
4763 /// # Examples
4764 ///
4765 /// ```
4766 /// use rust_igraph::Graph;
4767 ///
4768 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4769 /// assert!(g.is_dart_free().unwrap());
4770 /// ```
4771 pub fn is_dart_free(&self) -> IgraphResult<bool> {
4772 crate::algorithms::properties::is_dart_free::is_dart_free(self)
4773 }
4774
4775 /// Check whether this graph is diamond-free.
4776 ///
4777 /// # Examples
4778 ///
4779 /// ```
4780 /// use rust_igraph::Graph;
4781 ///
4782 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4783 /// assert!(g.is_diamond_free().unwrap());
4784 /// ```
4785 pub fn is_diamond_free(&self) -> IgraphResult<bool> {
4786 crate::algorithms::properties::is_diamond_free::is_diamond_free(self)
4787 }
4788
4789 /// Check whether this graph is distance-hereditary.
4790 ///
4791 /// # Examples
4792 ///
4793 /// ```
4794 /// use rust_igraph::Graph;
4795 ///
4796 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4797 /// assert!(g.is_distance_hereditary().unwrap());
4798 /// ```
4799 pub fn is_distance_hereditary(&self) -> IgraphResult<bool> {
4800 crate::algorithms::properties::is_distance_hereditary::is_distance_hereditary(self)
4801 }
4802
4803 /// Check whether this graph is Eulerian.
4804 ///
4805 /// # Examples
4806 ///
4807 /// ```
4808 /// use rust_igraph::Graph;
4809 ///
4810 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4811 /// let class = g.is_eulerian().unwrap();
4812 /// assert!(class.has_cycle);
4813 /// ```
4814 pub fn is_eulerian(
4815 &self,
4816 ) -> IgraphResult<crate::algorithms::paths::eulerian::EulerianClassification> {
4817 crate::algorithms::paths::eulerian::is_eulerian(self)
4818 }
4819
4820 /// Check whether this graph is fork-free.
4821 ///
4822 /// # Examples
4823 ///
4824 /// ```
4825 /// use rust_igraph::Graph;
4826 ///
4827 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4828 /// assert!(g.is_fork_free().unwrap());
4829 /// ```
4830 pub fn is_fork_free(&self) -> IgraphResult<bool> {
4831 crate::algorithms::properties::is_fork_free::is_fork_free(self)
4832 }
4833
4834 /// Check whether this graph is gem-free.
4835 ///
4836 /// # Examples
4837 ///
4838 /// ```
4839 /// use rust_igraph::Graph;
4840 ///
4841 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4842 /// assert!(g.is_gem_free().unwrap());
4843 /// ```
4844 pub fn is_gem_free(&self) -> IgraphResult<bool> {
4845 crate::algorithms::properties::is_gem_free::is_gem_free(self)
4846 }
4847
4848 /// Check whether this graph is geodetic.
4849 ///
4850 /// # Examples
4851 ///
4852 /// ```
4853 /// use rust_igraph::Graph;
4854 ///
4855 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4856 /// assert!(g.is_geodetic().unwrap());
4857 /// ```
4858 pub fn is_geodetic(&self) -> IgraphResult<bool> {
4859 crate::algorithms::properties::is_geodetic::is_geodetic(self)
4860 }
4861
4862 /// Check whether this graph is house-free.
4863 ///
4864 /// # Examples
4865 ///
4866 /// ```
4867 /// use rust_igraph::Graph;
4868 ///
4869 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4870 /// assert!(g.is_house_free().unwrap());
4871 /// ```
4872 pub fn is_house_free(&self) -> IgraphResult<bool> {
4873 crate::algorithms::properties::is_house_free::is_house_free(self)
4874 }
4875
4876 /// Check whether this graph is k-degenerate.
4877 ///
4878 /// # Examples
4879 ///
4880 /// ```
4881 /// use rust_igraph::Graph;
4882 ///
4883 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4884 /// assert!(g.is_k_degenerate(1).unwrap());
4885 /// ```
4886 pub fn is_k_degenerate(&self, k: u32) -> IgraphResult<bool> {
4887 crate::algorithms::properties::is_k_degenerate::is_k_degenerate(self, k)
4888 }
4889
4890 /// Check whether this graph is a lobster.
4891 ///
4892 /// # Examples
4893 ///
4894 /// ```
4895 /// use rust_igraph::Graph;
4896 ///
4897 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4898 /// assert!(g.is_lobster().unwrap());
4899 /// ```
4900 pub fn is_lobster(&self) -> IgraphResult<bool> {
4901 crate::algorithms::properties::is_lobster::is_lobster(self)
4902 }
4903
4904 /// Check whether this graph is net-free.
4905 ///
4906 /// # Examples
4907 ///
4908 /// ```
4909 /// use rust_igraph::Graph;
4910 ///
4911 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4912 /// assert!(g.is_net_free().unwrap());
4913 /// ```
4914 pub fn is_net_free(&self) -> IgraphResult<bool> {
4915 crate::algorithms::properties::is_net_free::is_net_free(self)
4916 }
4917
4918 /// Check whether this graph is P5-free.
4919 ///
4920 /// # Examples
4921 ///
4922 /// ```
4923 /// use rust_igraph::Graph;
4924 ///
4925 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4926 /// assert!(g.is_p5_free().unwrap());
4927 /// ```
4928 pub fn is_p5_free(&self) -> IgraphResult<bool> {
4929 crate::algorithms::properties::is_p5_free::is_p5_free(self)
4930 }
4931
4932 /// Check whether this graph is a path.
4933 ///
4934 /// # Examples
4935 ///
4936 /// ```
4937 /// use rust_igraph::Graph;
4938 ///
4939 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4940 /// assert!(g.is_path().unwrap());
4941 /// ```
4942 pub fn is_path(&self) -> IgraphResult<bool> {
4943 crate::algorithms::properties::is_path::is_path(self)
4944 }
4945
4946 /// Check whether this graph is paw-free.
4947 ///
4948 /// # Examples
4949 ///
4950 /// ```
4951 /// use rust_igraph::Graph;
4952 ///
4953 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4954 /// assert!(g.is_paw_free().unwrap());
4955 /// ```
4956 pub fn is_paw_free(&self) -> IgraphResult<bool> {
4957 crate::algorithms::properties::is_paw_free::is_paw_free(self)
4958 }
4959
4960 /// Check whether this graph is a proper interval graph.
4961 ///
4962 /// # Examples
4963 ///
4964 /// ```
4965 /// use rust_igraph::Graph;
4966 ///
4967 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4968 /// assert!(g.is_proper_interval().unwrap());
4969 /// ```
4970 pub fn is_proper_interval(&self) -> IgraphResult<bool> {
4971 crate::algorithms::properties::is_proper_interval::is_proper_interval(self)
4972 }
4973
4974 /// Check whether this graph is a pseudo-forest.
4975 ///
4976 /// # Examples
4977 ///
4978 /// ```
4979 /// use rust_igraph::Graph;
4980 ///
4981 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4982 /// assert!(g.is_pseudo_forest().unwrap());
4983 /// ```
4984 pub fn is_pseudo_forest(&self) -> IgraphResult<bool> {
4985 crate::algorithms::properties::is_pseudo_forest::is_pseudo_forest(self)
4986 }
4987
4988 /// Check whether this graph is Ptolemaic.
4989 ///
4990 /// # Examples
4991 ///
4992 /// ```
4993 /// use rust_igraph::Graph;
4994 ///
4995 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4996 /// assert!(g.is_ptolemaic().unwrap());
4997 /// ```
4998 pub fn is_ptolemaic(&self) -> IgraphResult<bool> {
4999 crate::algorithms::properties::is_ptolemaic::is_ptolemaic(self)
5000 }
5001
5002 /// Check whether this graph is self-complementary.
5003 ///
5004 /// # Examples
5005 ///
5006 /// ```
5007 /// use rust_igraph::Graph;
5008 ///
5009 /// // P_4 (path on 4 vertices) is self-complementary
5010 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5011 /// assert!(g.is_self_complementary().unwrap());
5012 /// ```
5013 pub fn is_self_complementary(&self) -> IgraphResult<bool> {
5014 crate::algorithms::properties::is_self_complementary::is_self_complementary(self)
5015 }
5016
5017 /// Check whether this graph is semicomplete.
5018 ///
5019 /// # Examples
5020 ///
5021 /// ```
5022 /// use rust_igraph::Graph;
5023 ///
5024 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], true, None).unwrap();
5025 /// assert!(g.is_semicomplete().unwrap());
5026 /// ```
5027 pub fn is_semicomplete(&self) -> IgraphResult<bool> {
5028 crate::algorithms::properties::is_semicomplete::is_semicomplete(self)
5029 }
5030
5031 /// Check whether this graph is a spider.
5032 ///
5033 /// # Examples
5034 ///
5035 /// ```
5036 /// use rust_igraph::Graph;
5037 ///
5038 /// let g = Graph::from_edges(&[(0,1), (0,2), (0,3)], false, None).unwrap();
5039 /// assert!(g.is_spider().unwrap());
5040 /// ```
5041 pub fn is_spider(&self) -> IgraphResult<bool> {
5042 crate::algorithms::properties::is_spider::is_spider(self)
5043 }
5044
5045 /// Check whether this graph is a split graph.
5046 ///
5047 /// # Examples
5048 ///
5049 /// ```
5050 /// use rust_igraph::Graph;
5051 ///
5052 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2), (2,3)], false, None).unwrap();
5053 /// assert!(g.is_split_graph().unwrap());
5054 /// ```
5055 pub fn is_split_graph(&self) -> IgraphResult<bool> {
5056 crate::algorithms::properties::is_split::is_split_graph(self)
5057 }
5058
5059 /// Check whether this graph is a star.
5060 ///
5061 /// # Examples
5062 ///
5063 /// ```
5064 /// use rust_igraph::Graph;
5065 ///
5066 /// let g = Graph::from_edges(&[(0,1), (0,2), (0,3)], false, None).unwrap();
5067 /// assert!(g.is_star().unwrap());
5068 /// ```
5069 pub fn is_star(&self) -> IgraphResult<bool> {
5070 crate::algorithms::properties::is_star::is_star(self)
5071 }
5072
5073 /// Check whether this graph is strongly chordal.
5074 ///
5075 /// # Examples
5076 ///
5077 /// ```
5078 /// use rust_igraph::Graph;
5079 ///
5080 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5081 /// assert!(g.is_strongly_chordal().unwrap());
5082 /// ```
5083 pub fn is_strongly_chordal(&self) -> IgraphResult<bool> {
5084 crate::algorithms::properties::is_strongly_chordal::is_strongly_chordal(self)
5085 }
5086
5087 /// Check whether this graph is a threshold graph.
5088 ///
5089 /// # Examples
5090 ///
5091 /// ```
5092 /// use rust_igraph::Graph;
5093 ///
5094 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
5095 /// assert!(g.is_threshold_graph().unwrap());
5096 /// ```
5097 pub fn is_threshold_graph(&self) -> IgraphResult<bool> {
5098 crate::algorithms::properties::is_threshold::is_threshold_graph(self)
5099 }
5100
5101 /// Check whether this graph is a tournament.
5102 ///
5103 /// # Examples
5104 ///
5105 /// ```
5106 /// use rust_igraph::Graph;
5107 ///
5108 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], true, None).unwrap();
5109 /// assert!(g.is_tournament().unwrap());
5110 /// ```
5111 pub fn is_tournament(&self) -> IgraphResult<bool> {
5112 crate::algorithms::properties::is_tournament::is_tournament(self)
5113 }
5114
5115 /// Check whether this graph is triangle-free.
5116 ///
5117 /// # Examples
5118 ///
5119 /// ```
5120 /// use rust_igraph::Graph;
5121 ///
5122 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5123 /// assert!(g.is_triangle_free().unwrap());
5124 /// ```
5125 pub fn is_triangle_free(&self) -> IgraphResult<bool> {
5126 crate::algorithms::properties::is_triangle_free::is_triangle_free(self)
5127 }
5128
5129 /// Check whether this graph is trivially perfect.
5130 ///
5131 /// # Examples
5132 ///
5133 /// ```
5134 /// use rust_igraph::Graph;
5135 ///
5136 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
5137 /// assert!(g.is_trivially_perfect().unwrap());
5138 /// ```
5139 pub fn is_trivially_perfect(&self) -> IgraphResult<bool> {
5140 crate::algorithms::properties::is_trivially_perfect::is_trivially_perfect(self)
5141 }
5142
5143 /// Check whether this graph is unicyclic.
5144 ///
5145 /// # Examples
5146 ///
5147 /// ```
5148 /// use rust_igraph::Graph;
5149 ///
5150 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5151 /// assert!(g.is_unicyclic().unwrap());
5152 /// ```
5153 pub fn is_unicyclic(&self) -> IgraphResult<bool> {
5154 crate::algorithms::properties::is_unicyclic::is_unicyclic(self)
5155 }
5156
5157 /// Check whether this graph is a wheel.
5158 ///
5159 /// # Examples
5160 ///
5161 /// ```
5162 /// use rust_igraph::Graph;
5163 ///
5164 /// // W_4: center 0 connected to rim {1,2,3}, rim forms a cycle
5165 /// let g = Graph::from_edges(
5166 /// &[(0,1), (0,2), (0,3), (1,2), (2,3), (3,1)],
5167 /// false, None
5168 /// ).unwrap();
5169 /// assert!(g.is_wheel().unwrap());
5170 /// ```
5171 pub fn is_wheel(&self) -> IgraphResult<bool> {
5172 crate::algorithms::properties::is_wheel::is_wheel(self)
5173 }
5174
5175 /// Check whether the graph is regular (all vertices have the same degree).
5176 ///
5177 /// # Examples
5178 ///
5179 /// ```
5180 /// use rust_igraph::{Graph, full_graph};
5181 ///
5182 /// let g = full_graph(4, false, false).unwrap();
5183 /// assert!(g.is_regular().unwrap());
5184 /// ```
5185 pub fn is_regular(&self) -> IgraphResult<bool> {
5186 crate::algorithms::properties::is_regular::is_regular(self)
5187 }
5188
5189 /// Check whether the graph is strongly regular, returning parameters if so.
5190 ///
5191 /// # Examples
5192 ///
5193 /// ```
5194 /// use rust_igraph::{Graph, cycle_graph};
5195 ///
5196 /// let g = cycle_graph(5, false, false).unwrap();
5197 /// let result = g.is_strongly_regular().unwrap();
5198 /// assert!(result.is_some());
5199 /// ```
5200 pub fn is_strongly_regular(
5201 &self,
5202 ) -> IgraphResult<
5203 Option<crate::algorithms::properties::is_strongly_regular::StronglyRegularParams>,
5204 > {
5205 crate::algorithms::properties::is_strongly_regular::is_strongly_regular(self)
5206 }
5207
5208 /// Check whether the graph is weakly chordal.
5209 ///
5210 /// # Examples
5211 ///
5212 /// ```
5213 /// use rust_igraph::Graph;
5214 ///
5215 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
5216 /// assert!(g.is_weakly_chordal().unwrap());
5217 /// ```
5218 pub fn is_weakly_chordal(&self) -> IgraphResult<bool> {
5219 crate::algorithms::properties::is_weakly_chordal::is_weakly_chordal(self)
5220 }
5221
5222 /// Check whether the graph is well-covered.
5223 ///
5224 /// # Examples
5225 ///
5226 /// ```
5227 /// use rust_igraph::{Graph, full_graph};
5228 ///
5229 /// let g = full_graph(4, false, false).unwrap();
5230 /// assert!(g.is_well_covered().unwrap());
5231 /// ```
5232 pub fn is_well_covered(&self) -> IgraphResult<bool> {
5233 crate::algorithms::properties::is_well_covered::is_well_covered(self)
5234 }
5235
5236 /// Check whether the graph is a windmill graph, returning (k, n) if so.
5237 ///
5238 /// # Examples
5239 ///
5240 /// ```
5241 /// use rust_igraph::{Graph, full_graph};
5242 ///
5243 /// let g = full_graph(3, false, false).unwrap();
5244 /// let result = g.is_windmill().unwrap();
5245 /// assert!(result.is_some());
5246 /// ```
5247 pub fn is_windmill(&self) -> IgraphResult<Option<(u32, u32)>> {
5248 crate::algorithms::properties::is_windmill::is_windmill(self)
5249 }
5250
5251 /// Check whether the graph is complete multipartite, returning
5252 /// partition sizes if so.
5253 ///
5254 /// # Examples
5255 ///
5256 /// ```
5257 /// use rust_igraph::{Graph, full_graph};
5258 ///
5259 /// let g = full_graph(3, false, false).unwrap();
5260 /// let result = g.is_complete_multipartite().unwrap();
5261 /// assert!(result.is_some());
5262 /// ```
5263 pub fn is_complete_multipartite(&self) -> IgraphResult<Option<Vec<u32>>> {
5264 crate::algorithms::properties::is_complete_multipartite::is_complete_multipartite(self)
5265 }
5266
5267 /// Check whether this graph satisfies Dirac's condition for Hamiltonicity.
5268 ///
5269 /// # Examples
5270 ///
5271 /// ```
5272 /// use rust_igraph::{Graph, full_graph};
5273 ///
5274 /// let g = full_graph(5, false, false).unwrap();
5275 /// assert!(g.satisfies_dirac().unwrap());
5276 /// ```
5277 pub fn satisfies_dirac(&self) -> IgraphResult<bool> {
5278 crate::algorithms::properties::satisfies_dirac::satisfies_dirac(self)
5279 }
5280
5281 /// Check whether this graph satisfies Ore's condition for Hamiltonicity.
5282 ///
5283 /// # Examples
5284 ///
5285 /// ```
5286 /// use rust_igraph::{Graph, full_graph};
5287 ///
5288 /// let g = full_graph(5, false, false).unwrap();
5289 /// assert!(g.satisfies_ore().unwrap());
5290 /// ```
5291 pub fn satisfies_ore(&self) -> IgraphResult<bool> {
5292 crate::algorithms::properties::satisfies_ore::satisfies_ore(self)
5293 }
5294
5295 // ── Centrality variants ───────────────────────────────────────
5296
5297 /// Compute edge betweenness centrality.
5298 ///
5299 /// # Examples
5300 ///
5301 /// ```
5302 /// use rust_igraph::Graph;
5303 ///
5304 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5305 /// let eb = g.edge_betweenness().unwrap();
5306 /// assert_eq!(eb.len(), 3);
5307 /// ```
5308 pub fn edge_betweenness(&self) -> IgraphResult<Vec<f64>> {
5309 crate::algorithms::properties::edge_betweenness::edge_betweenness(self)
5310 }
5311
5312 /// Compute betweenness centrality with a distance cutoff.
5313 ///
5314 /// # Examples
5315 ///
5316 /// ```
5317 /// use rust_igraph::Graph;
5318 ///
5319 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5320 /// let bc = g.betweenness_cutoff(2).unwrap();
5321 /// assert_eq!(bc.len(), 4);
5322 /// ```
5323 pub fn betweenness_cutoff(&self, cutoff: u32) -> IgraphResult<Vec<f64>> {
5324 crate::algorithms::properties::betweenness_cutoff::betweenness_cutoff(self, cutoff)
5325 }
5326
5327 /// Compute weighted betweenness centrality.
5328 ///
5329 /// # Examples
5330 ///
5331 /// ```
5332 /// use rust_igraph::Graph;
5333 ///
5334 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5335 /// let bc = g.betweenness_weighted(&[1.0, 2.0]).unwrap();
5336 /// assert_eq!(bc.len(), 3);
5337 /// ```
5338 pub fn betweenness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
5339 crate::algorithms::properties::betweenness_weighted::betweenness_weighted(self, weights)
5340 }
5341
5342 /// Compute weighted closeness centrality.
5343 ///
5344 /// # Examples
5345 ///
5346 /// ```
5347 /// use rust_igraph::Graph;
5348 ///
5349 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5350 /// let cc = g.closeness_weighted(&[1.0, 2.0]).unwrap();
5351 /// assert_eq!(cc.len(), 3);
5352 /// ```
5353 pub fn closeness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
5354 crate::algorithms::properties::closeness_weighted::closeness_weighted(self, weights)
5355 }
5356
5357 /// Compute edge betweenness centrality with a distance cutoff.
5358 ///
5359 /// # Examples
5360 ///
5361 /// ```
5362 /// use rust_igraph::Graph;
5363 ///
5364 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5365 /// let eb = g.edge_betweenness_cutoff(2).unwrap();
5366 /// assert_eq!(eb.len(), 3);
5367 /// ```
5368 pub fn edge_betweenness_cutoff(&self, cutoff: u32) -> IgraphResult<Vec<f64>> {
5369 crate::algorithms::properties::edge_betweenness_cutoff::edge_betweenness_cutoff(
5370 self, cutoff,
5371 )
5372 }
5373
5374 /// Compute weighted edge betweenness centrality.
5375 ///
5376 /// # Examples
5377 ///
5378 /// ```
5379 /// use rust_igraph::Graph;
5380 ///
5381 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5382 /// let eb = g.edge_betweenness_weighted(&[1.0, 2.0]).unwrap();
5383 /// assert_eq!(eb.len(), 2);
5384 /// ```
5385 pub fn edge_betweenness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
5386 crate::algorithms::properties::edge_betweenness_weighted::edge_betweenness_weighted(
5387 self, weights,
5388 )
5389 }
5390
5391 /// Compute weighted harmonic centrality.
5392 ///
5393 /// # Examples
5394 ///
5395 /// ```
5396 /// use rust_igraph::Graph;
5397 ///
5398 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5399 /// let hc = g.harmonic_centrality_weighted(&[1.0, 2.0]).unwrap();
5400 /// assert_eq!(hc.len(), 3);
5401 /// ```
5402 pub fn harmonic_centrality_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
5403 crate::algorithms::properties::harmonic_weighted::harmonic_centrality_weighted(
5404 self, weights,
5405 )
5406 }
5407
5408 /// Compute weighted `PageRank`.
5409 ///
5410 /// # Examples
5411 ///
5412 /// ```
5413 /// use rust_igraph::Graph;
5414 ///
5415 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5416 /// let pr = g.pagerank_weighted(&[1.0, 2.0]).unwrap();
5417 /// assert_eq!(pr.len(), 3);
5418 /// ```
5419 pub fn pagerank_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
5420 crate::algorithms::properties::pagerank_weighted::pagerank_weighted(self, weights)
5421 }
5422
5423 // ── Connectivity & structural ─────────────────────────────────
5424
5425 /// Compute the cohesive block structure.
5426 ///
5427 /// # Examples
5428 ///
5429 /// ```
5430 /// use rust_igraph::Graph;
5431 ///
5432 /// let g = Graph::from_edges(
5433 /// &[(0,1), (1,2), (2,0), (2,3), (3,4), (4,5), (5,3)],
5434 /// false, None
5435 /// ).unwrap();
5436 /// let blocks = g.cohesive_blocks().unwrap();
5437 /// assert!(!blocks.blocks.is_empty());
5438 /// ```
5439 pub fn cohesive_blocks(
5440 &self,
5441 ) -> IgraphResult<crate::algorithms::connectivity::cohesive_blocks::CohesiveBlocks> {
5442 crate::algorithms::connectivity::cohesive_blocks::cohesive_blocks(self)
5443 }
5444
5445 /// Count reachable vertices from each vertex.
5446 ///
5447 /// # Examples
5448 ///
5449 /// ```
5450 /// use rust_igraph::Graph;
5451 ///
5452 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5453 /// let counts = g.count_reachable().unwrap();
5454 /// assert_eq!(counts, vec![3, 3, 3]);
5455 /// ```
5456 pub fn count_reachable(&self) -> IgraphResult<Vec<u32>> {
5457 crate::algorithms::connectivity::reachability::count_reachable(self)
5458 }
5459
5460 /// Compute the reachability matrix.
5461 ///
5462 /// # Examples
5463 ///
5464 /// ```
5465 /// use rust_igraph::Graph;
5466 ///
5467 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5468 /// let mat = g.reachability_matrix().unwrap();
5469 /// assert!(mat[0][2]);
5470 /// assert!(!mat[2][0]);
5471 /// ```
5472 pub fn reachability_matrix(&self) -> IgraphResult<Vec<Vec<bool>>> {
5473 crate::algorithms::connectivity::reachability_matrix::reachability_matrix(self)
5474 }
5475
5476 /// Compute the transitive closure.
5477 ///
5478 /// # Examples
5479 ///
5480 /// ```
5481 /// use rust_igraph::Graph;
5482 ///
5483 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5484 /// let tc = g.transitive_closure().unwrap();
5485 /// assert!(tc.has_edge(0, 2));
5486 /// ```
5487 pub fn transitive_closure(&self) -> IgraphResult<Graph> {
5488 crate::algorithms::connectivity::transitive_closure::transitive_closure(self)
5489 }
5490
5491 // ── Flow & cuts ───────────────────────────────────────────────
5492
5493 /// Compute the global minimum cut.
5494 ///
5495 /// # Examples
5496 ///
5497 /// ```
5498 /// use rust_igraph::Graph;
5499 ///
5500 /// let g = Graph::from_edges(
5501 /// &[(0,1), (0,2), (1,3), (2,3)], false, None
5502 /// ).unwrap();
5503 /// let mc = g.mincut(None).unwrap();
5504 /// assert!(mc.value >= 2.0 - 1e-10);
5505 /// ```
5506 pub fn mincut(
5507 &self,
5508 capacity: Option<&[f64]>,
5509 ) -> IgraphResult<crate::algorithms::flow::mincut::Mincut> {
5510 crate::algorithms::flow::mincut::mincut(self, capacity)
5511 }
5512
5513 /// Compute the global minimum cut value.
5514 ///
5515 /// # Examples
5516 ///
5517 /// ```
5518 /// use rust_igraph::Graph;
5519 ///
5520 /// let g = Graph::from_edges(
5521 /// &[(0,1), (0,2), (1,3), (2,3)], false, None
5522 /// ).unwrap();
5523 /// let val = g.mincut_value(None).unwrap();
5524 /// assert!(val >= 2.0 - 1e-10);
5525 /// ```
5526 pub fn mincut_value(&self, capacity: Option<&[f64]>) -> IgraphResult<f64> {
5527 crate::algorithms::flow::mincut_value::mincut_value(self, capacity)
5528 }
5529
5530 /// Compute the Gomory-Hu tree.
5531 ///
5532 /// # Examples
5533 ///
5534 /// ```
5535 /// use rust_igraph::Graph;
5536 ///
5537 /// let g = Graph::from_edges(
5538 /// &[(0,1), (0,2), (1,2), (1,3)], false, None
5539 /// ).unwrap();
5540 /// let tree = g.gomory_hu_tree(None).unwrap();
5541 /// assert_eq!(tree.tree.vcount(), 4);
5542 /// ```
5543 pub fn gomory_hu_tree(
5544 &self,
5545 capacity: Option<&[f64]>,
5546 ) -> IgraphResult<crate::algorithms::flow::gomory_hu_tree::GomoryHuTree> {
5547 crate::algorithms::flow::gomory_hu_tree::gomory_hu_tree(self, capacity)
5548 }
5549
5550 /// Enumerate all minimum s-t cuts.
5551 ///
5552 /// # Examples
5553 ///
5554 /// ```
5555 /// use rust_igraph::Graph;
5556 ///
5557 /// let g = Graph::from_edges(
5558 /// &[(0,1), (0,2), (1,3), (2,3)], true, None
5559 /// ).unwrap();
5560 /// let cuts = g.all_st_cuts(0, 3).unwrap();
5561 /// assert!(!cuts.cuts.is_empty());
5562 /// ```
5563 pub fn all_st_cuts(
5564 &self,
5565 source: VertexId,
5566 target: VertexId,
5567 ) -> IgraphResult<crate::algorithms::flow::all_st_cuts::StCuts> {
5568 crate::algorithms::flow::all_st_cuts::all_st_cuts(self, source, target)
5569 }
5570
5571 /// Count edge-disjoint paths between two vertices.
5572 ///
5573 /// # Examples
5574 ///
5575 /// ```
5576 /// use rust_igraph::Graph;
5577 ///
5578 /// let g = Graph::from_edges(
5579 /// &[(0,1), (0,2), (1,3), (2,3)], false, None
5580 /// ).unwrap();
5581 /// let count = g.edge_disjoint_paths(0, 3).unwrap();
5582 /// assert_eq!(count, 2);
5583 /// ```
5584 pub fn edge_disjoint_paths(&self, source: VertexId, target: VertexId) -> IgraphResult<i64> {
5585 crate::algorithms::flow::edge_disjoint_paths::edge_disjoint_paths(self, source, target)
5586 }
5587
5588 // ── Paths & distances ─────────────────────────────────────────
5589
5590 /// Compute BFS distances from a source vertex.
5591 ///
5592 /// # Examples
5593 ///
5594 /// ```
5595 /// use rust_igraph::Graph;
5596 ///
5597 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5598 /// let dist = g.distances(0).unwrap();
5599 /// assert_eq!(dist[3], Some(3));
5600 /// ```
5601 pub fn distances(&self, source: VertexId) -> IgraphResult<Vec<Option<u32>>> {
5602 crate::algorithms::paths::distances::distances(self, source)
5603 }
5604
5605 /// Compute an Eulerian path if one exists.
5606 ///
5607 /// # Examples
5608 ///
5609 /// ```
5610 /// use rust_igraph::Graph;
5611 ///
5612 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5613 /// let path = g.eulerian_path().unwrap();
5614 /// assert!(path.is_some());
5615 /// ```
5616 pub fn eulerian_path(&self) -> IgraphResult<Option<Vec<u32>>> {
5617 crate::algorithms::paths::eulerian_construct::eulerian_path(self)
5618 }
5619
5620 /// Compute mean geodesic distance.
5621 ///
5622 /// # Examples
5623 ///
5624 /// ```
5625 /// use rust_igraph::Graph;
5626 ///
5627 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5628 /// let d = g.mean_distance().unwrap();
5629 /// assert!(d.is_some());
5630 /// ```
5631 pub fn mean_distance(&self) -> IgraphResult<Option<f64>> {
5632 crate::algorithms::properties::basic::mean_distance(self)
5633 }
5634
5635 /// Topological sort of a directed graph.
5636 ///
5637 /// # Examples
5638 ///
5639 /// ```
5640 /// use rust_igraph::Graph;
5641 ///
5642 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5643 /// let order = g.topological_sorting().unwrap();
5644 /// assert_eq!(order, vec![0, 1, 2]);
5645 /// ```
5646 pub fn topological_sorting(&self) -> IgraphResult<Vec<VertexId>> {
5647 crate::algorithms::properties::topological_sorting::topological_sorting(
5648 self,
5649 crate::algorithms::paths::dijkstra::DijkstraMode::Out,
5650 )
5651 }
5652
5653 /// List all triangles in the graph.
5654 ///
5655 /// # Examples
5656 ///
5657 /// ```
5658 /// use rust_igraph::Graph;
5659 ///
5660 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5661 /// let tris = g.list_triangles().unwrap();
5662 /// assert_eq!(tris.len(), 1);
5663 /// ```
5664 pub fn list_triangles(&self) -> IgraphResult<Vec<(u32, u32, u32)>> {
5665 crate::algorithms::properties::list_triangles::list_triangles(self)
5666 }
5667
5668 /// Compute the degree distribution.
5669 ///
5670 /// # Examples
5671 ///
5672 /// ```
5673 /// use rust_igraph::Graph;
5674 ///
5675 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5676 /// let dist = g.degree_distribution().unwrap();
5677 /// assert!(!dist.is_empty());
5678 /// ```
5679 pub fn degree_distribution(&self) -> IgraphResult<Vec<u32>> {
5680 crate::algorithms::properties::degree_distribution::degree_distribution(
5681 self,
5682 crate::algorithms::properties::degree::DegreeMode::All,
5683 )
5684 }
5685
5686 /// Get the edge list as (source, target) pairs.
5687 ///
5688 /// # Examples
5689 ///
5690 /// ```
5691 /// use rust_igraph::Graph;
5692 ///
5693 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5694 /// let edges = g.get_edgelist().unwrap();
5695 /// assert_eq!(edges.len(), 2);
5696 /// ```
5697 pub fn get_edgelist(&self) -> IgraphResult<Vec<(VertexId, VertexId)>> {
5698 crate::algorithms::properties::edgelist::get_edgelist(self)
5699 }
5700
5701 /// Compute the graph's regularity (degree if regular, None otherwise).
5702 ///
5703 /// # Examples
5704 ///
5705 /// ```
5706 /// use rust_igraph::Graph;
5707 ///
5708 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5709 /// assert_eq!(g.regularity().unwrap(), Some(2));
5710 /// ```
5711 pub fn regularity(&self) -> IgraphResult<Option<u32>> {
5712 crate::algorithms::properties::is_regular::regularity(self)
5713 }
5714
5715 /// Find a cycle in the graph.
5716 ///
5717 /// # Examples
5718 ///
5719 /// ```
5720 /// use rust_igraph::Graph;
5721 ///
5722 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5723 /// let result = g.find_cycle().unwrap();
5724 /// assert!(!result.vertices.is_empty());
5725 /// ```
5726 pub fn find_cycle(&self) -> IgraphResult<crate::algorithms::cycles::CycleResult> {
5727 crate::algorithms::cycles::find_cycle(self, crate::algorithms::cycles::CycleMode::All)
5728 }
5729
5730 /// Compute the joint degree matrix.
5731 ///
5732 /// # Examples
5733 ///
5734 /// ```
5735 /// use rust_igraph::Graph;
5736 ///
5737 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5738 /// let jdm = g.joint_degree_matrix(None).unwrap();
5739 /// assert!(!jdm.is_empty());
5740 /// ```
5741 pub fn joint_degree_matrix(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<Vec<f64>>> {
5742 crate::algorithms::properties::joint_degree_matrix::joint_degree_matrix(self, weights)
5743 }
5744
5745 /// Compute the minimum dominating set.
5746 ///
5747 /// # Examples
5748 ///
5749 /// ```
5750 /// use rust_igraph::Graph;
5751 ///
5752 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5753 /// let dom = g.minimum_dominating_set().unwrap();
5754 /// assert!(!dom.is_empty());
5755 /// ```
5756 pub fn minimum_dominating_set(&self) -> IgraphResult<Vec<VertexId>> {
5757 crate::algorithms::dominating_set::minimum_dominating_set(self)
5758 }
5759
5760 /// Compute the k-th power of this graph.
5761 ///
5762 /// # Examples
5763 ///
5764 /// ```
5765 /// use rust_igraph::Graph;
5766 ///
5767 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5768 /// let g2 = g.graph_power(2).unwrap();
5769 /// assert!(g2.has_edge(0, 2));
5770 /// ```
5771 pub fn graph_power(&self, order: u32) -> IgraphResult<Graph> {
5772 crate::algorithms::operators::connect_neighborhood::graph_power(self, order)
5773 }
5774
5775 /// Compute the trussness of each edge.
5776 ///
5777 /// # Examples
5778 ///
5779 /// ```
5780 /// use rust_igraph::Graph;
5781 ///
5782 /// let g = Graph::from_edges(
5783 /// &[(0,1), (1,2), (2,0), (2,3)], false, None
5784 /// ).unwrap();
5785 /// let tr = g.trussness().unwrap();
5786 /// assert_eq!(tr.len(), g.ecount());
5787 /// ```
5788 pub fn trussness(&self) -> IgraphResult<Vec<u32>> {
5789 crate::algorithms::properties::trussness::trussness(self)
5790 }
5791
5792 // ── Graph operators ──────────────────────────────────────────────
5793
5794 /// Union of two graphs on the same vertex set.
5795 ///
5796 /// ```
5797 /// use rust_igraph::Graph;
5798 ///
5799 /// let a = Graph::from_edges(&[(0,1)], false, None).unwrap();
5800 /// let b = Graph::from_edges(&[(1,2)], false, None).unwrap();
5801 /// let u = a.union(&b).unwrap();
5802 /// assert_eq!(u.ecount(), 2);
5803 /// ```
5804 pub fn union(&self, other: &Graph) -> IgraphResult<Graph> {
5805 crate::algorithms::operators::union::union(self, other)
5806 }
5807
5808 /// Intersection of two graphs on the same vertex set.
5809 ///
5810 /// ```
5811 /// use rust_igraph::Graph;
5812 ///
5813 /// let a = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5814 /// let b = Graph::from_edges(&[(1,2), (2,3)], false, None).unwrap();
5815 /// let i = a.intersection(&b).unwrap();
5816 /// assert_eq!(i.ecount(), 1);
5817 /// ```
5818 pub fn intersection(&self, other: &Graph) -> IgraphResult<Graph> {
5819 crate::algorithms::operators::intersection::intersection(self, other)
5820 }
5821
5822 /// Edge difference: edges in `self` but not in `other`.
5823 ///
5824 /// ```
5825 /// use rust_igraph::Graph;
5826 ///
5827 /// let a = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5828 /// let b = Graph::from_edges(&[(1,2)], false, None).unwrap();
5829 /// let d = a.difference(&b).unwrap();
5830 /// assert_eq!(d.ecount(), 1);
5831 /// ```
5832 pub fn difference(&self, other: &Graph) -> IgraphResult<Graph> {
5833 crate::algorithms::operators::difference::difference(self, other)
5834 }
5835
5836 /// Disjoint union: concatenate vertex sets, then concatenate edge sets.
5837 ///
5838 /// ```
5839 /// use rust_igraph::Graph;
5840 ///
5841 /// let a = Graph::from_edges(&[(0,1)], false, None).unwrap();
5842 /// let b = Graph::from_edges(&[(0,1)], false, None).unwrap();
5843 /// let d = a.disjoint_union(&b).unwrap();
5844 /// assert_eq!(d.vcount(), 4);
5845 /// assert_eq!(d.ecount(), 2);
5846 /// ```
5847 pub fn disjoint_union(&self, other: &Graph) -> IgraphResult<Graph> {
5848 crate::algorithms::operators::disjoint_union::disjoint_union(self, other)
5849 }
5850
5851 /// Join: disjoint union plus all edges between the two vertex sets.
5852 ///
5853 /// ```
5854 /// use rust_igraph::Graph;
5855 ///
5856 /// let a = Graph::with_vertices(2);
5857 /// let b = Graph::with_vertices(2);
5858 /// let j = a.join(&b).unwrap();
5859 /// assert_eq!(j.vcount(), 4);
5860 /// assert_eq!(j.ecount(), 4);
5861 /// ```
5862 pub fn join(&self, other: &Graph) -> IgraphResult<Graph> {
5863 crate::algorithms::operators::join::join(self, other)
5864 }
5865
5866 /// Compose two graphs (relational composition).
5867 ///
5868 /// ```
5869 /// use rust_igraph::Graph;
5870 ///
5871 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5872 /// let c = g.compose(&g).unwrap();
5873 /// assert!(c.ecount() > 0);
5874 /// ```
5875 pub fn compose(&self, other: &Graph) -> IgraphResult<Graph> {
5876 crate::algorithms::operators::compose::compose(self, other)
5877 }
5878
5879 /// Cartesian product of two graphs.
5880 ///
5881 /// ```
5882 /// use rust_igraph::Graph;
5883 ///
5884 /// let p2 = Graph::from_edges(&[(0,1)], false, None).unwrap();
5885 /// let grid = p2.cartesian_product(&p2).unwrap();
5886 /// assert_eq!(grid.vcount(), 4);
5887 /// ```
5888 pub fn cartesian_product(&self, other: &Graph) -> IgraphResult<Graph> {
5889 crate::algorithms::operators::products::cartesian_product(self, other)
5890 }
5891
5892 /// Tensor (categorical/direct) product of two graphs.
5893 ///
5894 /// ```
5895 /// use rust_igraph::Graph;
5896 ///
5897 /// let p2 = Graph::from_edges(&[(0,1)], false, None).unwrap();
5898 /// let t = p2.tensor_product(&p2).unwrap();
5899 /// assert_eq!(t.vcount(), 4);
5900 /// ```
5901 pub fn tensor_product(&self, other: &Graph) -> IgraphResult<Graph> {
5902 crate::algorithms::operators::products::tensor_product(self, other)
5903 }
5904
5905 /// Strong product of two graphs.
5906 ///
5907 /// ```
5908 /// use rust_igraph::Graph;
5909 ///
5910 /// let p2 = Graph::from_edges(&[(0,1)], false, None).unwrap();
5911 /// let s = p2.strong_product(&p2).unwrap();
5912 /// assert_eq!(s.vcount(), 4);
5913 /// ```
5914 pub fn strong_product(&self, other: &Graph) -> IgraphResult<Graph> {
5915 crate::algorithms::operators::products::strong_product(self, other)
5916 }
5917
5918 /// Lexicographic product of two graphs.
5919 ///
5920 /// ```
5921 /// use rust_igraph::Graph;
5922 ///
5923 /// let a = Graph::from_edges(&[(0,1)], false, None).unwrap();
5924 /// let b = Graph::with_vertices(2);
5925 /// let l = a.lexicographic_product(&b).unwrap();
5926 /// assert_eq!(l.vcount(), 4);
5927 /// ```
5928 pub fn lexicographic_product(&self, other: &Graph) -> IgraphResult<Graph> {
5929 crate::algorithms::operators::products::lexicographic_product(self, other)
5930 }
5931
5932 /// Connect each vertex to all vertices within distance `order`.
5933 ///
5934 /// ```
5935 /// use rust_igraph::Graph;
5936 ///
5937 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5938 /// let c = g.connect_neighborhood(2).unwrap();
5939 /// assert!(c.ecount() > g.ecount());
5940 /// ```
5941 pub fn connect_neighborhood(&self, order: u32) -> IgraphResult<Graph> {
5942 crate::algorithms::operators::connect_neighborhood::connect_neighborhood(self, order)
5943 }
5944
5945 /// Rewire edges while preserving the degree sequence.
5946 ///
5947 /// ```
5948 /// use rust_igraph::Graph;
5949 ///
5950 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3), (3,0)], false, None).unwrap();
5951 /// let r = g.rewire(100, false, 42).unwrap();
5952 /// assert_eq!(r.ecount(), g.ecount());
5953 /// ```
5954 pub fn rewire(&self, num_trials: usize, loops: bool, seed: u64) -> IgraphResult<Graph> {
5955 crate::algorithms::operators::rewire::rewire(self, num_trials, loops, seed)
5956 }
5957
5958 /// Randomly rewire each edge with probability `prob`.
5959 ///
5960 /// ```
5961 /// use rust_igraph::Graph;
5962 ///
5963 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5964 /// let r = g.rewire_edges(0.5, false, 42).unwrap();
5965 /// assert_eq!(r.vcount(), g.vcount());
5966 /// ```
5967 pub fn rewire_edges(&self, prob: f64, loops: bool, seed: u64) -> IgraphResult<Graph> {
5968 crate::algorithms::operators::rewire_edges::rewire_edges(self, prob, loops, seed)
5969 }
5970
5971 /// Extract a subgraph induced by the given edge ids.
5972 ///
5973 /// ```
5974 /// use rust_igraph::Graph;
5975 ///
5976 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5977 /// let s = g.subgraph_from_edges(&[0, 1]).unwrap();
5978 /// assert_eq!(s.graph.ecount(), 2);
5979 /// ```
5980 pub fn subgraph_from_edges(
5981 &self,
5982 eids: &[u32],
5983 ) -> IgraphResult<crate::algorithms::operators::subgraph_from_edges::SubgraphFromEdgesResult>
5984 {
5985 crate::algorithms::operators::subgraph_from_edges::subgraph_from_edges(self, eids, true)
5986 }
5987
5988 /// The Even-Tarjan reduction of a directed graph.
5989 ///
5990 /// ```
5991 /// use rust_igraph::Graph;
5992 ///
5993 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5994 /// let et = g.even_tarjan_reduction().unwrap();
5995 /// assert!(et.graph.vcount() > g.vcount());
5996 /// ```
5997 pub fn even_tarjan_reduction(
5998 &self,
5999 ) -> IgraphResult<crate::algorithms::operators::even_tarjan::EvenTarjanResult> {
6000 crate::algorithms::operators::even_tarjan::even_tarjan_reduction(self)
6001 }
6002
6003 /// Bipartite projection onto one vertex type.
6004 ///
6005 /// `project_type` selects which side: `false` projects the `false`-typed
6006 /// vertices, `true` projects the `true`-typed vertices.
6007 ///
6008 /// ```
6009 /// use rust_igraph::Graph;
6010 ///
6011 /// let mut g = Graph::new(4, false).unwrap();
6012 /// g.add_edge(0, 2).unwrap();
6013 /// g.add_edge(0, 3).unwrap();
6014 /// g.add_edge(1, 2).unwrap();
6015 /// g.add_edge(1, 3).unwrap();
6016 /// let types = vec![false, false, true, true];
6017 /// let p = g.bipartite_projection(&types, false).unwrap();
6018 /// assert_eq!(p.graph.vcount(), 2);
6019 /// ```
6020 pub fn bipartite_projection(
6021 &self,
6022 types: &[bool],
6023 project_type: bool,
6024 ) -> IgraphResult<crate::algorithms::operators::bipartite_projection::BipartiteProjection> {
6025 crate::algorithms::operators::bipartite_projection::bipartite_projection(
6026 self,
6027 types,
6028 project_type,
6029 )
6030 }
6031
6032 // ── Paths (advanced) ─────────────────────────────────────────────
6033
6034 /// Bellman-Ford shortest-path distances from a single source.
6035 ///
6036 /// ```
6037 /// use rust_igraph::Graph;
6038 ///
6039 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
6040 /// let w = vec![1.0; g.ecount()];
6041 /// let d = g.bellman_ford_distances(0, &w).unwrap();
6042 /// assert!((d[3].unwrap() - 3.0).abs() < 1e-9);
6043 /// ```
6044 pub fn bellman_ford_distances(
6045 &self,
6046 source: VertexId,
6047 weights: &[f64],
6048 ) -> IgraphResult<Vec<Option<f64>>> {
6049 crate::algorithms::paths::bellman_ford::bellman_ford_distances(self, source, weights)
6050 }
6051
6052 /// Floyd-Warshall all-pairs shortest-path distances.
6053 ///
6054 /// ```
6055 /// use rust_igraph::Graph;
6056 ///
6057 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
6058 /// let d = g.floyd_warshall_distances(None).unwrap();
6059 /// assert!((d[0][2].unwrap() - 2.0).abs() < 1e-9);
6060 /// ```
6061 pub fn floyd_warshall_distances(
6062 &self,
6063 weights: Option<&[f64]>,
6064 ) -> IgraphResult<Vec<Vec<Option<f64>>>> {
6065 crate::algorithms::paths::floyd_warshall::floyd_warshall_distances(self, weights)
6066 }
6067
6068 /// Find the k shortest paths between two vertices.
6069 ///
6070 /// ```
6071 /// use rust_igraph::Graph;
6072 ///
6073 /// let g = Graph::from_edges(
6074 /// &[(0,1), (1,3), (0,2), (2,3)], false, None,
6075 /// ).unwrap();
6076 /// let w = vec![1.0; g.ecount()];
6077 /// let paths = g.k_shortest_paths(0, 3, &w, 2).unwrap();
6078 /// assert_eq!(paths.len(), 2);
6079 /// ```
6080 pub fn k_shortest_paths(
6081 &self,
6082 source: VertexId,
6083 target: VertexId,
6084 weights: &[f64],
6085 k: usize,
6086 ) -> IgraphResult<Vec<crate::algorithms::paths::k_shortest_paths::KShortestPath>> {
6087 use crate::algorithms::paths::dijkstra::DijkstraMode;
6088 crate::algorithms::paths::k_shortest_paths::k_shortest_paths(
6089 self,
6090 source,
6091 target,
6092 weights,
6093 k,
6094 DijkstraMode::Out,
6095 )
6096 }
6097
6098 /// Enumerate all simple paths from a source vertex.
6099 ///
6100 /// ```
6101 /// use rust_igraph::Graph;
6102 ///
6103 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
6104 /// let paths = g.all_simple_paths(0, Some(&[2]), 1, 10).unwrap();
6105 /// assert!(paths.len() >= 2);
6106 /// ```
6107 pub fn all_simple_paths(
6108 &self,
6109 from: u32,
6110 to: Option<&[u32]>,
6111 min_length: i32,
6112 max_length: i32,
6113 ) -> IgraphResult<Vec<Vec<u32>>> {
6114 crate::algorithms::paths::simple_paths::all_simple_paths(
6115 self,
6116 from,
6117 to,
6118 crate::algorithms::paths::simple_paths::SimplePathMode::Out,
6119 min_length,
6120 max_length,
6121 -1,
6122 )
6123 }
6124
6125 // ── Matching ──────────────────────────────────────────────────────
6126
6127 /// Maximum bipartite matching.
6128 ///
6129 /// ```
6130 /// use rust_igraph::Graph;
6131 ///
6132 /// let mut g = Graph::new(4, false).unwrap();
6133 /// g.add_edge(0, 2).unwrap();
6134 /// g.add_edge(0, 3).unwrap();
6135 /// g.add_edge(1, 2).unwrap();
6136 /// let types = vec![false, false, true, true];
6137 /// let m = g.maximum_bipartite_matching(&types).unwrap();
6138 /// assert_eq!(m.matching_size, 2);
6139 /// ```
6140 pub fn maximum_bipartite_matching(
6141 &self,
6142 types: &[bool],
6143 ) -> IgraphResult<crate::algorithms::matching::MatchingResult> {
6144 crate::algorithms::matching::maximum_bipartite_matching(self, types)
6145 }
6146
6147 // ── Coloring ─────────────────────────────────────────────────────
6148
6149 /// Greedy vertex coloring.
6150 ///
6151 /// ```
6152 /// use rust_igraph::{Graph, GreedyColoringHeuristic};
6153 ///
6154 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6155 /// let colors = g.vertex_coloring_greedy(GreedyColoringHeuristic::ColoredNeighbors).unwrap();
6156 /// assert_eq!(colors.len(), 3);
6157 /// ```
6158 pub fn vertex_coloring_greedy(
6159 &self,
6160 heuristic: crate::algorithms::coloring::GreedyColoringHeuristic,
6161 ) -> IgraphResult<Vec<u32>> {
6162 crate::algorithms::coloring::vertex_coloring_greedy(self, heuristic)
6163 }
6164
6165 // ── Cycles ───────────────────────────────────────────────────────
6166
6167 /// Enumerate all simple cycles.
6168 ///
6169 /// ```
6170 /// use rust_igraph::{Graph, SimpleCycleMode};
6171 ///
6172 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6173 /// let cycles = g.simple_cycles(SimpleCycleMode::All, 3, None).unwrap();
6174 /// assert!(!cycles.is_empty());
6175 /// ```
6176 pub fn simple_cycles(
6177 &self,
6178 mode: crate::algorithms::simple_cycles::SimpleCycleMode,
6179 min_length: u32,
6180 max_length: Option<u32>,
6181 ) -> IgraphResult<Vec<crate::algorithms::simple_cycles::SimpleCycle>> {
6182 crate::algorithms::simple_cycles::simple_cycles(self, mode, min_length, max_length, None)
6183 }
6184
6185 // ── Community (additional) ───────────────────────────────────────
6186
6187 /// Leading eigenvector community detection.
6188 ///
6189 /// ```
6190 /// use rust_igraph::Graph;
6191 ///
6192 /// let g = Graph::from_edges(
6193 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (2,3)],
6194 /// false, None,
6195 /// ).unwrap();
6196 /// let result = g.leading_eigenvector(None, None).unwrap();
6197 /// assert!(result.membership.len() == g.vcount() as usize);
6198 /// ```
6199 pub fn leading_eigenvector(
6200 &self,
6201 weights: Option<&[f64]>,
6202 steps: Option<u32>,
6203 ) -> IgraphResult<crate::algorithms::community::leading_eigenvector::LeadingEigenvectorResult>
6204 {
6205 crate::algorithms::community::leading_eigenvector::leading_eigenvector(self, weights, steps)
6206 }
6207
6208 /// Fluid community detection.
6209 ///
6210 /// ```
6211 /// use rust_igraph::Graph;
6212 ///
6213 /// let g = Graph::from_edges(
6214 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (2,3)],
6215 /// false, None,
6216 /// ).unwrap();
6217 /// let r = g.fluid_communities(2).unwrap();
6218 /// assert_eq!(r.membership.len(), g.vcount() as usize);
6219 /// ```
6220 pub fn fluid_communities(
6221 &self,
6222 k: u32,
6223 ) -> IgraphResult<crate::algorithms::community::fluid_communities::FluidResult> {
6224 crate::algorithms::community::fluid_communities::fluid_communities(self, k)
6225 }
6226
6227 /// Motif census (subgraph isomorphism classes of a given size).
6228 ///
6229 /// ```
6230 /// use rust_igraph::Graph;
6231 ///
6232 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], true, None).unwrap();
6233 /// let hist = g.motifs_randesu(3).unwrap();
6234 /// assert!(!hist.is_empty());
6235 /// ```
6236 pub fn motifs_randesu(&self, size: u32) -> IgraphResult<Vec<f64>> {
6237 crate::algorithms::motifs::motifs_randesu::motifs_randesu(self, size)
6238 }
6239
6240 /// Personalized `PageRank` with a custom reset distribution.
6241 ///
6242 /// ```
6243 /// use rust_igraph::Graph;
6244 ///
6245 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6246 /// let reset = vec![1.0, 0.0, 0.0];
6247 /// let pr = g.personalized_pagerank(&reset).unwrap();
6248 /// assert_eq!(pr.len(), 3);
6249 /// ```
6250 pub fn personalized_pagerank(&self, reset: &[f64]) -> IgraphResult<Vec<f64>> {
6251 crate::algorithms::properties::personalized_pagerank::personalized_pagerank_default(
6252 self, reset,
6253 )
6254 }
6255
6256 // ── Isomorphism ─────────────────────────────────────────────────
6257
6258 /// Test whether two graphs are isomorphic (VF2).
6259 ///
6260 /// ```
6261 /// use rust_igraph::Graph;
6262 ///
6263 /// let a = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6264 /// let b = Graph::from_edges(&[(0,2), (2,1), (1,0)], false, None).unwrap();
6265 /// let result = a.isomorphic_vf2(&b).unwrap();
6266 /// assert!(result.iso);
6267 /// ```
6268 pub fn isomorphic_vf2(
6269 &self,
6270 other: &Graph,
6271 ) -> IgraphResult<crate::algorithms::isomorphism::vf2::Vf2Isomorphism> {
6272 crate::algorithms::isomorphism::vf2::isomorphic_vf2(self, other, None, None, None, None)
6273 }
6274
6275 /// Quick isomorphism test (delegates to the best available method).
6276 ///
6277 /// ```
6278 /// use rust_igraph::Graph;
6279 ///
6280 /// let a = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6281 /// let b = Graph::from_edges(&[(0,2), (2,1), (1,0)], false, None).unwrap();
6282 /// assert!(a.isomorphic(&b).unwrap());
6283 /// ```
6284 pub fn isomorphic(&self, other: &Graph) -> IgraphResult<bool> {
6285 crate::algorithms::isomorphism::queries::isomorphic(self, other)
6286 }
6287
6288 /// Count the number of isomorphisms between two graphs (VF2).
6289 ///
6290 /// ```
6291 /// use rust_igraph::Graph;
6292 ///
6293 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6294 /// let count = g.count_isomorphisms_vf2(&g).unwrap();
6295 /// assert_eq!(count, 6); // C3 has 6 automorphisms
6296 /// ```
6297 pub fn count_isomorphisms_vf2(&self, other: &Graph) -> IgraphResult<u64> {
6298 crate::algorithms::isomorphism::vf2::count_isomorphisms_vf2(
6299 self, other, None, None, None, None,
6300 )
6301 }
6302
6303 // ── Cliques ─────────────────────────────────────────────────────
6304
6305 /// Find all maximal independent vertex sets.
6306 ///
6307 /// ```
6308 /// use rust_igraph::Graph;
6309 ///
6310 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
6311 /// let sets = g.independent_vertex_sets(1, 3).unwrap();
6312 /// assert!(!sets.is_empty());
6313 /// ```
6314 pub fn independent_vertex_sets(
6315 &self,
6316 min_size: u32,
6317 max_size: u32,
6318 ) -> IgraphResult<Vec<Vec<u32>>> {
6319 crate::algorithms::cliques::independent_vertex_sets(self, min_size, max_size, None)
6320 }
6321
6322 // ── Network properties ──────────────────────────────────────────
6323
6324 /// Global efficiency of the graph.
6325 ///
6326 /// ```
6327 /// use rust_igraph::Graph;
6328 ///
6329 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6330 /// let e = g.global_efficiency().unwrap();
6331 /// assert!(e.unwrap_or(0.0) > 0.0);
6332 /// ```
6333 pub fn global_efficiency(&self) -> IgraphResult<Option<f64>> {
6334 crate::algorithms::properties::efficiency::global_efficiency(self)
6335 }
6336
6337 /// Local efficiency for each vertex.
6338 ///
6339 /// ```
6340 /// use rust_igraph::Graph;
6341 ///
6342 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6343 /// let e = g.local_efficiency().unwrap();
6344 /// assert_eq!(e.len(), 3);
6345 /// ```
6346 pub fn local_efficiency(&self) -> IgraphResult<Vec<f64>> {
6347 crate::algorithms::properties::efficiency::local_efficiency(self)
6348 }
6349
6350 /// Degree assortativity coefficient.
6351 ///
6352 /// ```
6353 /// use rust_igraph::Graph;
6354 ///
6355 /// let g = Graph::from_edges(
6356 /// &[(0,1), (1,2), (2,3), (3,4)], false, None,
6357 /// ).unwrap();
6358 /// let r = g.assortativity_degree().unwrap();
6359 /// assert!(r.is_some());
6360 /// ```
6361 pub fn assortativity_degree(&self) -> IgraphResult<Option<f64>> {
6362 crate::algorithms::properties::assortativity::assortativity_degree(self)
6363 }
6364
6365 /// Diversity (entropy) of vertex edge weights.
6366 ///
6367 /// ```
6368 /// use rust_igraph::Graph;
6369 ///
6370 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
6371 /// let w = vec![1.0, 2.0, 3.0];
6372 /// let d = g.diversity(&w).unwrap();
6373 /// assert_eq!(d.len(), 3);
6374 /// ```
6375 pub fn diversity(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
6376 crate::algorithms::properties::strength::diversity(self, weights)
6377 }
6378
6379 // ---- Paths (batch 5) ----
6380
6381 /// All-pairs shortest-path distances (unweighted BFS).
6382 ///
6383 /// Returns a flat `n*n` vector in row-major order where
6384 /// `result[i*n + j]` is the distance from vertex `i` to `j`,
6385 /// or `None` if unreachable.
6386 ///
6387 /// ```
6388 /// use rust_igraph::Graph;
6389 ///
6390 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6391 /// let d = g.distances_all().unwrap();
6392 /// assert_eq!(d[0 * 3 + 2], Some(2)); // path 0→1→2
6393 /// ```
6394 pub fn distances_all(&self) -> IgraphResult<Vec<Option<u32>>> {
6395 crate::algorithms::paths::distances_all::distances_all(self)
6396 }
6397
6398 /// Shortest-path distances from a set of source vertices.
6399 ///
6400 /// Returns a flat `sources.len() * n` vector in row-major order.
6401 ///
6402 /// ```
6403 /// use rust_igraph::Graph;
6404 ///
6405 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6406 /// let d = g.distances_from(&[0]).unwrap();
6407 /// assert_eq!(d[3], Some(3)); // vertex 3 is 3 hops from vertex 0
6408 /// ```
6409 pub fn distances_from(&self, sources: &[VertexId]) -> IgraphResult<Vec<Option<u32>>> {
6410 crate::algorithms::paths::distances_from::distances_from(self, sources)
6411 }
6412
6413 /// Shortest-path trees from a source vertex (one path per target).
6414 ///
6415 /// Returns a vector of vertex sequences, one per target vertex.
6416 ///
6417 /// ```
6418 /// use rust_igraph::Graph;
6419 ///
6420 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6421 /// let paths = g.get_shortest_paths(0).unwrap();
6422 /// assert_eq!(paths[2], vec![0, 1, 2]);
6423 /// ```
6424 pub fn get_shortest_paths(&self, source: VertexId) -> IgraphResult<Vec<Vec<VertexId>>> {
6425 crate::algorithms::paths::shortest_paths::get_shortest_paths(self, source)
6426 }
6427
6428 /// All shortest paths from a source vertex.
6429 ///
6430 /// ```
6431 /// use rust_igraph::Graph;
6432 ///
6433 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,3),(2,3)], false, None).unwrap();
6434 /// let asp = g.get_all_shortest_paths(0).unwrap();
6435 /// // Two shortest paths from 0 to 3: 0-1-3 and 0-2-3
6436 /// assert_eq!(asp.paths[3].len(), 2);
6437 /// ```
6438 pub fn get_all_shortest_paths(
6439 &self,
6440 source: VertexId,
6441 ) -> IgraphResult<crate::AllShortestPaths> {
6442 crate::algorithms::paths::all_shortest_paths::get_all_shortest_paths(self, source)
6443 }
6444
6445 /// Johnson's algorithm for all-pairs shortest paths with edge weights.
6446 ///
6447 /// Handles negative weights (but not negative cycles).
6448 ///
6449 /// ```
6450 /// use rust_igraph::Graph;
6451 ///
6452 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6453 /// let d = g.johnson_distances(&[1.0, 2.0]).unwrap();
6454 /// assert!((d[0][2].unwrap() - 3.0).abs() < 1e-10);
6455 /// ```
6456 pub fn johnson_distances(&self, weights: &[f64]) -> IgraphResult<Vec<Vec<Option<f64>>>> {
6457 crate::algorithms::paths::johnson::johnson_distances(self, weights)
6458 }
6459
6460 /// Widest (bottleneck) paths from a source vertex.
6461 ///
6462 /// ```
6463 /// use rust_igraph::Graph;
6464 ///
6465 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6466 /// let wp = g.widest_paths(0, &[10.0, 5.0]).unwrap();
6467 /// assert!((wp.widths[2].unwrap() - 5.0).abs() < 1e-10);
6468 /// ```
6469 pub fn widest_paths(
6470 &self,
6471 from: VertexId,
6472 weights: &[f64],
6473 ) -> IgraphResult<crate::WidestPaths> {
6474 crate::algorithms::paths::widest_path::widest_paths(self, from, weights)
6475 }
6476
6477 /// Graph center — vertices with minimum eccentricity.
6478 ///
6479 /// ```
6480 /// use rust_igraph::{Graph, cycle_graph};
6481 ///
6482 /// let g = cycle_graph(5, false, false).unwrap();
6483 /// let center = g.graph_center().unwrap();
6484 /// assert_eq!(center.len(), 5); // all vertices equidistant in a cycle
6485 /// ```
6486 pub fn graph_center(&self) -> IgraphResult<Vec<VertexId>> {
6487 crate::algorithms::paths::graph_center::graph_center(
6488 self,
6489 crate::algorithms::paths::radii::EccMode::Out,
6490 )
6491 }
6492
6493 /// Path-length histogram of the graph.
6494 ///
6495 /// ```
6496 /// use rust_igraph::Graph;
6497 ///
6498 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6499 /// let h = g.path_length_hist(false).unwrap();
6500 /// assert!(!h.hist.is_empty());
6501 /// ```
6502 pub fn path_length_hist(&self, directed: bool) -> IgraphResult<crate::PathLengthHistResult> {
6503 crate::algorithms::paths::histogram::path_length_hist(self, directed)
6504 }
6505
6506 /// Find an Eulerian cycle (every edge visited exactly once, returning
6507 /// to start).
6508 ///
6509 /// ```
6510 /// use rust_igraph::cycle_graph;
6511 ///
6512 /// let g = cycle_graph(5, false, false).unwrap();
6513 /// let cycle = g.eulerian_cycle().unwrap();
6514 /// assert_eq!(cycle.len(), 5); // 5 edges in C5
6515 /// ```
6516 pub fn eulerian_cycle(&self) -> IgraphResult<Vec<EdgeId>> {
6517 crate::algorithms::paths::eulerian_construct::eulerian_cycle(self)
6518 }
6519
6520 // ---- Centrality / properties (batch 5) ----
6521
6522 /// HITS hub and authority scores.
6523 ///
6524 /// ```
6525 /// use rust_igraph::Graph;
6526 ///
6527 /// let g = Graph::from_edges(&[(0,1),(1,2)], true, None).unwrap();
6528 /// let hits = g.hub_and_authority_scores().unwrap();
6529 /// assert_eq!(hits.hub.len(), 3);
6530 /// ```
6531 pub fn hub_and_authority_scores(&self) -> IgraphResult<crate::HitsScores> {
6532 crate::algorithms::properties::hits::hub_and_authority_scores(self)
6533 }
6534
6535 /// Weighted vertex degree (strength).
6536 ///
6537 /// ```
6538 /// use rust_igraph::Graph;
6539 ///
6540 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,2)], false, None).unwrap();
6541 /// let s = g.strength(&[1.0, 2.0, 3.0]).unwrap();
6542 /// assert!((s[0] - 3.0).abs() < 1e-10); // edges 0-1 (w=1) + 0-2 (w=2)
6543 /// ```
6544 pub fn strength(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
6545 crate::algorithms::properties::strength::strength(self, weights)
6546 }
6547
6548 /// Average nearest-neighbor degree by degree class (knn(k)).
6549 ///
6550 /// ```
6551 /// use rust_igraph::Graph;
6552 ///
6553 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6554 /// let k = g.knnk().unwrap();
6555 /// assert!(k.len() > 0);
6556 /// ```
6557 pub fn knnk(&self) -> IgraphResult<Vec<Option<f64>>> {
6558 crate::algorithms::properties::knn::knnk(self)
6559 }
6560
6561 /// Barrat's weighted clustering coefficient per vertex.
6562 ///
6563 /// ```
6564 /// use rust_igraph::Graph;
6565 ///
6566 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6567 /// let t = g.transitivity_barrat(&[1.0, 1.0, 1.0]).unwrap();
6568 /// assert!(t[0].unwrap() > 0.0);
6569 /// ```
6570 pub fn transitivity_barrat(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
6571 crate::algorithms::properties::triangles::transitivity_barrat(self, weights)
6572 }
6573
6574 /// Local scan statistic (order 1) — triangle counts per vertex.
6575 ///
6576 /// ```
6577 /// use rust_igraph::Graph;
6578 ///
6579 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6580 /// let s = g.local_scan_1(None).unwrap();
6581 /// assert_eq!(s.len(), 3);
6582 /// ```
6583 pub fn local_scan_1(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
6584 crate::algorithms::properties::local_scan::local_scan_1(self, weights)
6585 }
6586
6587 /// Maximum cardinality search ordering.
6588 ///
6589 /// ```
6590 /// use rust_igraph::Graph;
6591 ///
6592 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6593 /// let mcs = g.maximum_cardinality_search().unwrap();
6594 /// assert_eq!(mcs.alpha.len(), 3);
6595 /// ```
6596 pub fn maximum_cardinality_search(&self) -> IgraphResult<crate::McsResult> {
6597 crate::algorithms::chordality::maximum_cardinality_search(self)
6598 }
6599
6600 /// Vertex with the highest degree.
6601 ///
6602 /// ```
6603 /// use rust_igraph::Graph;
6604 ///
6605 /// let g = Graph::from_edges(&[(0,1),(0,2),(0,3),(1,2)], false, None).unwrap();
6606 /// let v = g.max_degree_vertex().unwrap();
6607 /// assert_eq!(v, Some(0)); // vertex 0 has degree 3
6608 /// ```
6609 pub fn max_degree_vertex(&self) -> IgraphResult<Option<VertexId>> {
6610 crate::algorithms::properties::degree::max_degree_vertex(
6611 self,
6612 crate::algorithms::properties::degree::DegreeMode::All,
6613 )
6614 }
6615
6616 // ---- Graph predicates / queries (batch 5) ----
6617
6618 /// Whether the graph has at least one self-loop.
6619 ///
6620 /// ```
6621 /// use rust_igraph::Graph;
6622 ///
6623 /// let g = Graph::from_edges(&[(0,1),(1,1)], false, None).unwrap();
6624 /// assert!(g.has_loop().unwrap());
6625 /// ```
6626 pub fn has_loop(&self) -> IgraphResult<bool> {
6627 crate::algorithms::properties::multiplicity::has_loop(self)
6628 }
6629
6630 /// Whether the graph has at least one pair of mutual (reciprocal) edges.
6631 ///
6632 /// ```
6633 /// use rust_igraph::Graph;
6634 ///
6635 /// let g = Graph::from_edges(&[(0,1),(1,0)], true, None).unwrap();
6636 /// assert!(g.has_mutual(true).unwrap());
6637 /// ```
6638 pub fn has_mutual(&self, loops: bool) -> IgraphResult<bool> {
6639 crate::algorithms::properties::mutual::has_mutual(self, loops)
6640 }
6641
6642 /// Per-edge test: is each edge a multi-edge?
6643 ///
6644 /// ```
6645 /// use rust_igraph::Graph;
6646 ///
6647 /// let g = Graph::from_edges(&[(0,1),(0,1),(1,2)], false, None).unwrap();
6648 /// let m = g.is_multiple().unwrap();
6649 /// assert!(m.iter().any(|&x| x)); // at least one multi-edge
6650 /// ```
6651 pub fn is_multiple(&self) -> IgraphResult<Vec<bool>> {
6652 crate::algorithms::properties::multiplicity::is_multiple(self)
6653 }
6654
6655 /// Per-edge test: is each edge mutual (has a reciprocal)?
6656 ///
6657 /// ```
6658 /// use rust_igraph::Graph;
6659 ///
6660 /// let g = Graph::from_edges(&[(0,1),(1,0),(0,2)], true, None).unwrap();
6661 /// let m = g.is_mutual(true).unwrap();
6662 /// assert!(m[0]); // edge 0→1 has reciprocal 1→0
6663 /// ```
6664 pub fn is_mutual(&self, loops: bool) -> IgraphResult<Vec<bool>> {
6665 crate::algorithms::properties::mutual::is_mutual(self, loops)
6666 }
6667
6668 // ---- Community detection (batch 5) ----
6669
6670 /// Modularity of a given community assignment.
6671 ///
6672 /// ```
6673 /// use rust_igraph::Graph;
6674 ///
6675 /// let g = Graph::from_edges(
6676 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6677 /// false, None,
6678 /// ).unwrap();
6679 /// let q = g.modularity(&[0,0,0,1,1,1], 1.0).unwrap();
6680 /// assert!(q.unwrap() > 0.0);
6681 /// ```
6682 pub fn modularity(&self, membership: &[u32], resolution: f64) -> IgraphResult<Option<f64>> {
6683 crate::algorithms::community::modularity::modularity(self, membership, resolution)
6684 }
6685
6686 // ---- Constructors / operators (batch 5) ----
6687
6688 /// Mycielskian — triangle-free graph with increasing chromatic number.
6689 ///
6690 /// ```
6691 /// use rust_igraph::Graph;
6692 ///
6693 /// let g = Graph::from_edges(&[(0,1)], false, None).unwrap();
6694 /// let m = g.mycielskian(1).unwrap();
6695 /// assert!(m.vcount() > g.vcount());
6696 /// ```
6697 pub fn mycielskian(&self, k: u32) -> IgraphResult<Graph> {
6698 crate::algorithms::constructors::mycielskian::mycielskian(self, k)
6699 }
6700
6701 /// Prüfer sequence of a labeled tree.
6702 ///
6703 /// ```
6704 /// use rust_igraph::Graph;
6705 ///
6706 /// // Path graph 0-1-2-3 is a tree
6707 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6708 /// let seq = g.to_prufer().unwrap();
6709 /// assert_eq!(seq.len(), 2); // n-2 elements for n=4
6710 /// ```
6711 pub fn to_prufer(&self) -> IgraphResult<Vec<u32>> {
6712 crate::algorithms::constructors::prufer::to_prufer(self)
6713 }
6714
6715 // ---- Connectivity / percolation (batch 5) ----
6716
6717 /// Bond (edge) percolation — add edges one by one and track components.
6718 ///
6719 /// ```
6720 /// use rust_igraph::Graph;
6721 ///
6722 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6723 /// let p = g.bond_percolation(&[0, 1, 2]).unwrap();
6724 /// assert_eq!(p.giant_size.len(), 3); // one snapshot per step
6725 /// ```
6726 pub fn bond_percolation(
6727 &self,
6728 edge_order: &[EdgeId],
6729 ) -> IgraphResult<crate::EdgelistPercolation> {
6730 crate::algorithms::connectivity::percolation::bond_percolation(self, edge_order)
6731 }
6732
6733 /// Site (vertex) percolation — add vertices one by one and track components.
6734 ///
6735 /// ```
6736 /// use rust_igraph::Graph;
6737 ///
6738 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6739 /// let p = g.site_percolation(&[0, 1, 2, 3]).unwrap();
6740 /// assert_eq!(p.giant_size.len(), 4);
6741 /// ```
6742 pub fn site_percolation(
6743 &self,
6744 vertex_order: &[VertexId],
6745 ) -> IgraphResult<crate::SitePercolation> {
6746 crate::algorithms::connectivity::percolation::site_percolation(self, vertex_order)
6747 }
6748
6749 /// Reachability matrix (transitive closure as a boolean matrix).
6750 ///
6751 /// ```
6752 /// use rust_igraph::{Graph, ReachabilityMode};
6753 ///
6754 /// let g = Graph::from_edges(&[(0,1),(1,2)], true, None).unwrap();
6755 /// let r = g.reachability(ReachabilityMode::Out).unwrap();
6756 /// assert!(r.is_reachable(0, 2)); // 0 can reach 2 transitively
6757 /// ```
6758 pub fn reachability(
6759 &self,
6760 mode: crate::ReachabilityMode,
6761 ) -> IgraphResult<crate::ReachabilityResult> {
6762 crate::algorithms::connectivity::reachability_scc::reachability(self, mode)
6763 }
6764
6765 // ---- Neighborhoods (batch 5) ----
6766
6767 /// Induced subgraphs of k-hop neighborhoods around each vertex.
6768 ///
6769 /// ```
6770 /// use rust_igraph::Graph;
6771 ///
6772 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6773 /// let nbrs = g.neighborhood_graphs(1).unwrap();
6774 /// assert_eq!(nbrs.len(), 4); // one subgraph per vertex
6775 /// ```
6776 pub fn neighborhood_graphs(&self, order: i32) -> IgraphResult<Vec<Graph>> {
6777 crate::algorithms::properties::neighborhood::neighborhood_graphs(self, order)
6778 }
6779
6780 // ---- Cliques (batch 5) ----
6781
6782 /// Weighted clique number — maximum total weight of any clique.
6783 ///
6784 /// ```
6785 /// use rust_igraph::Graph;
6786 ///
6787 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6788 /// let wc = g.weighted_clique_number(&[1.0, 2.0, 3.0]).unwrap();
6789 /// assert!((wc - 6.0).abs() < 1e-10); // triangle, all 3 vertices
6790 /// ```
6791 pub fn weighted_clique_number(&self, vertex_weights: &[f64]) -> IgraphResult<f64> {
6792 crate::algorithms::cliques::weighted_clique_number(self, vertex_weights)
6793 }
6794
6795 // ---- Isomorphism (batch 5) ----
6796
6797 /// Isomorphism class of a small graph (up to 6 vertices).
6798 ///
6799 /// ```
6800 /// use rust_igraph::Graph;
6801 ///
6802 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6803 /// let cls = g.isoclass().unwrap();
6804 /// assert!(cls > 0);
6805 /// ```
6806 pub fn isoclass(&self) -> IgraphResult<u32> {
6807 crate::algorithms::motifs::isoclass::isoclass(self)
6808 }
6809
6810 // ---- Layout (batch 5) ----
6811
6812 /// Multidimensional scaling layout.
6813 ///
6814 /// ```
6815 /// use rust_igraph::Graph;
6816 ///
6817 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6818 /// let pos = g.layout_mds(None).unwrap();
6819 /// assert_eq!(pos.len(), 4);
6820 /// ```
6821 pub fn layout_mds(&self, dist: Option<&[Vec<f64>]>) -> IgraphResult<Vec<[f64; 2]>> {
6822 crate::algorithms::layout::mds::layout_mds(self, dist)
6823 }
6824
6825 /// Spherical layout (3D positions on a unit sphere).
6826 ///
6827 /// ```
6828 /// use rust_igraph::Graph;
6829 ///
6830 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6831 /// let pos = g.layout_sphere();
6832 /// assert_eq!(pos.len(), 3);
6833 /// ```
6834 pub fn layout_sphere(&self) -> Vec<(f64, f64, f64)> {
6835 crate::algorithms::layout::simple::layout_sphere(self)
6836 }
6837
6838 // ---- Weighted community detection (batch 6) ----
6839
6840 /// Louvain community detection with edge weights.
6841 ///
6842 /// ```
6843 /// use rust_igraph::Graph;
6844 ///
6845 /// let g = Graph::from_edges(
6846 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6847 /// false, None,
6848 /// ).unwrap();
6849 /// let r = g.louvain_weighted(&[1.0; 7]).unwrap();
6850 /// assert!(r.modularity > 0.0);
6851 /// ```
6852 pub fn louvain_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LouvainResult> {
6853 crate::algorithms::community::louvain::louvain_weighted(self, weights)
6854 }
6855
6856 /// Leiden community detection with edge weights.
6857 ///
6858 /// ```
6859 /// use rust_igraph::Graph;
6860 ///
6861 /// let g = Graph::from_edges(
6862 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6863 /// false, None,
6864 /// ).unwrap();
6865 /// let r = g.leiden_weighted(&[1.0; 7]).unwrap();
6866 /// assert!(r.quality > 0.0);
6867 /// ```
6868 pub fn leiden_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LeidenResult> {
6869 crate::algorithms::community::leiden::leiden_weighted(self, weights)
6870 }
6871
6872 /// Label propagation with edge weights.
6873 ///
6874 /// ```
6875 /// use rust_igraph::Graph;
6876 ///
6877 /// let g = Graph::from_edges(
6878 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6879 /// false, None,
6880 /// ).unwrap();
6881 /// let r = g.label_propagation_weighted(&[1.0; 7]).unwrap();
6882 /// assert_eq!(r.membership.len(), 6);
6883 /// ```
6884 pub fn label_propagation_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LpaResult> {
6885 crate::algorithms::community::label_propagation::label_propagation_weighted(self, weights)
6886 }
6887
6888 /// Walktrap community detection with edge weights.
6889 ///
6890 /// ```
6891 /// use rust_igraph::Graph;
6892 ///
6893 /// let g = Graph::from_edges(
6894 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6895 /// false, None,
6896 /// ).unwrap();
6897 /// let r = g.walktrap_weighted(&[1.0; 7]).unwrap();
6898 /// assert!(!r.modularity.is_empty());
6899 /// ```
6900 pub fn walktrap_weighted(&self, weights: &[f64]) -> IgraphResult<crate::WalktrapResult> {
6901 crate::algorithms::community::walktrap::walktrap_weighted(self, weights)
6902 }
6903
6904 // ---- Weighted distance/centrality (batch 6) ----
6905
6906 /// Weighted diameter (longest shortest-path distance).
6907 ///
6908 /// ```
6909 /// use rust_igraph::Graph;
6910 ///
6911 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6912 /// let d = g.diameter_weighted(&[1.0, 2.0]).unwrap();
6913 /// assert!((d.unwrap() - 3.0).abs() < 1e-10);
6914 /// ```
6915 pub fn diameter_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
6916 crate::algorithms::paths::radii::diameter_weighted(self, weights)
6917 }
6918
6919 /// Weighted eccentricity per vertex.
6920 ///
6921 /// ```
6922 /// use rust_igraph::Graph;
6923 ///
6924 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6925 /// let e = g.eccentricity_weighted(&[1.0, 2.0]).unwrap();
6926 /// assert_eq!(e.len(), 3);
6927 /// ```
6928 pub fn eccentricity_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
6929 crate::algorithms::paths::radii::eccentricity_weighted(self, weights)
6930 }
6931
6932 /// Weighted graph radius.
6933 ///
6934 /// ```
6935 /// use rust_igraph::Graph;
6936 ///
6937 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6938 /// let r = g.radius_weighted(&[1.0, 2.0]).unwrap();
6939 /// assert!(r.is_some());
6940 /// ```
6941 pub fn radius_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
6942 crate::algorithms::paths::radii::radius_weighted(self, weights)
6943 }
6944
6945 /// Weighted knn(k) — average nearest-neighbor degree by degree class.
6946 ///
6947 /// ```
6948 /// use rust_igraph::Graph;
6949 ///
6950 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6951 /// let k = g.knnk_weighted(&[1.0, 1.0, 1.0]).unwrap();
6952 /// assert!(!k.is_empty());
6953 /// ```
6954 pub fn knnk_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
6955 crate::algorithms::properties::knn::knnk_weighted(self, weights)
6956 }
6957
6958 /// `PageRank` via linear-system solver (alternative to power iteration).
6959 ///
6960 /// ```
6961 /// use rust_igraph::Graph;
6962 ///
6963 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], true, None).unwrap();
6964 /// let pr = g.pagerank_linsys().unwrap();
6965 /// assert_eq!(pr.len(), 3);
6966 /// ```
6967 pub fn pagerank_linsys(&self) -> IgraphResult<Vec<f64>> {
6968 crate::algorithms::properties::pagerank_linsys::pagerank_linsys(self)
6969 }
6970
6971 /// Local scan statistic of order k.
6972 ///
6973 /// ```
6974 /// use rust_igraph::Graph;
6975 ///
6976 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6977 /// let s = g.local_scan_k(1, None).unwrap();
6978 /// assert_eq!(s.len(), 3);
6979 /// ```
6980 pub fn local_scan_k(&self, k: u32, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
6981 crate::algorithms::properties::local_scan_k::local_scan_k(self, k, weights)
6982 }
6983
6984 // ---- Validators / predicates (batch 6) ----
6985
6986 /// Per-edge test: is each edge a self-loop?
6987 ///
6988 /// ```
6989 /// use rust_igraph::Graph;
6990 ///
6991 /// let g = Graph::from_edges(&[(0,1),(1,1),(2,3)], false, None).unwrap();
6992 /// let loops = g.is_loop().unwrap();
6993 /// assert!(!loops[0]); // 0-1 is not a loop
6994 /// assert!(loops[1]); // 1-1 is a loop
6995 /// ```
6996 pub fn is_loop(&self) -> IgraphResult<Vec<bool>> {
6997 crate::algorithms::properties::multiplicity::is_loop(self)
6998 }
6999
7000 /// Whether a set of vertices forms a clique.
7001 ///
7002 /// ```
7003 /// use rust_igraph::Graph;
7004 ///
7005 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7006 /// assert!(g.is_clique(&[0, 1, 2], false).unwrap());
7007 /// ```
7008 pub fn is_clique(&self, vertices: &[VertexId], directed: bool) -> IgraphResult<bool> {
7009 crate::algorithms::properties::is_clique::is_clique(self, vertices, directed)
7010 }
7011
7012 /// Whether a set of vertices forms an independent set.
7013 ///
7014 /// ```
7015 /// use rust_igraph::Graph;
7016 ///
7017 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7018 /// assert!(g.is_independent_vertex_set(&[0, 2]).unwrap());
7019 /// ```
7020 pub fn is_independent_vertex_set(&self, vertices: &[VertexId]) -> IgraphResult<bool> {
7021 crate::algorithms::properties::is_clique::is_independent_vertex_set(self, vertices)
7022 }
7023
7024 /// Whether a set of vertices is a separator (its removal disconnects the graph).
7025 ///
7026 /// ```
7027 /// use rust_igraph::Graph;
7028 ///
7029 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7030 /// assert!(g.is_separator(&[1]).unwrap());
7031 /// ```
7032 pub fn is_separator(&self, candidates: &[VertexId]) -> IgraphResult<bool> {
7033 crate::algorithms::connectivity::separators::is_separator(self, candidates)
7034 }
7035
7036 /// Whether a separator is minimal.
7037 ///
7038 /// ```
7039 /// use rust_igraph::Graph;
7040 ///
7041 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7042 /// assert!(g.is_minimal_separator(&[1]).unwrap());
7043 /// ```
7044 pub fn is_minimal_separator(&self, candidates: &[VertexId]) -> IgraphResult<bool> {
7045 crate::algorithms::connectivity::separators::is_minimal_separator(self, candidates)
7046 }
7047
7048 /// Whether a coloring is a valid vertex coloring.
7049 ///
7050 /// ```
7051 /// use rust_igraph::Graph;
7052 ///
7053 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7054 /// assert!(g.is_vertex_coloring(&[0, 1, 0]).unwrap());
7055 /// ```
7056 pub fn is_vertex_coloring(&self, colors: &[u32]) -> IgraphResult<bool> {
7057 crate::algorithms::coloring::is_vertex_coloring(self, colors)
7058 }
7059
7060 /// Whether a coloring is a valid edge coloring.
7061 ///
7062 /// ```
7063 /// use rust_igraph::Graph;
7064 ///
7065 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7066 /// assert!(g.is_edge_coloring(&[0, 1]).unwrap());
7067 /// ```
7068 pub fn is_edge_coloring(&self, colors: &[u32]) -> IgraphResult<bool> {
7069 crate::algorithms::coloring::is_edge_coloring(self, colors)
7070 }
7071
7072 /// Whether a set of vertices is a vertex cover.
7073 ///
7074 /// ```
7075 /// use rust_igraph::Graph;
7076 ///
7077 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7078 /// assert!(g.is_vertex_cover(&[1]));
7079 /// ```
7080 pub fn is_vertex_cover(&self, cover: &[VertexId]) -> bool {
7081 crate::algorithms::vertex_cover::is_vertex_cover(self, cover)
7082 }
7083
7084 /// Whether a set of edges is an edge cover.
7085 ///
7086 /// ```
7087 /// use rust_igraph::Graph;
7088 ///
7089 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7090 /// assert!(g.is_edge_cover(&[0, 1]));
7091 /// ```
7092 pub fn is_edge_cover(&self, cover: &[EdgeId]) -> bool {
7093 crate::algorithms::edge_cover::is_edge_cover(self, cover)
7094 }
7095
7096 /// Whether a set of vertices is a dominating set.
7097 ///
7098 /// ```
7099 /// use rust_igraph::Graph;
7100 ///
7101 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7102 /// assert!(g.is_dominating_set(&[1]));
7103 /// ```
7104 pub fn is_dominating_set(&self, dom_set: &[VertexId]) -> bool {
7105 crate::algorithms::dominating_set::is_dominating_set(self, dom_set)
7106 }
7107
7108 // ---- Operators (batch 6) ----
7109
7110 /// Reverse specific edges in a directed graph.
7111 ///
7112 /// ```
7113 /// use rust_igraph::Graph;
7114 ///
7115 /// let g = Graph::from_edges(&[(0,1),(1,2)], true, None).unwrap();
7116 /// let r = g.reverse_edges(&[0]).unwrap();
7117 /// assert_eq!(r.edge(0).unwrap(), (1, 0));
7118 /// ```
7119 pub fn reverse_edges(&self, eids: &[u32]) -> IgraphResult<Graph> {
7120 crate::algorithms::operators::reverse::reverse_edges(self, eids)
7121 }
7122
7123 /// Edges induced by a subset of vertices.
7124 ///
7125 /// ```
7126 /// use rust_igraph::Graph;
7127 ///
7128 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7129 /// let eids = g.induced_subgraph_edges(&[0, 1]).unwrap();
7130 /// assert_eq!(eids.len(), 1); // only edge 0-1
7131 /// ```
7132 pub fn induced_subgraph_edges(&self, vids: &[u32]) -> IgraphResult<Vec<u32>> {
7133 crate::algorithms::operators::induced_subgraph_edges::induced_subgraph_edges(self, vids)
7134 }
7135
7136 // ---- Similarity (batch 6) ----
7137
7138 /// Dice similarity between all vertex pairs (n*n flat matrix).
7139 ///
7140 /// ```
7141 /// use rust_igraph::Graph;
7142 ///
7143 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7144 /// let s = g.similarity_dice().unwrap();
7145 /// let n = g.vcount() as usize;
7146 /// assert_eq!(s.len(), n * n);
7147 /// ```
7148 pub fn similarity_dice(&self) -> IgraphResult<Vec<f64>> {
7149 crate::algorithms::properties::similarity::similarity_dice(self)
7150 }
7151
7152 /// Inverse-log-weighted similarity between all vertex pairs.
7153 ///
7154 /// ```
7155 /// use rust_igraph::Graph;
7156 ///
7157 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7158 /// let s = g.similarity_inverse_log_weighted().unwrap();
7159 /// let n = g.vcount() as usize;
7160 /// assert_eq!(s.len(), n * n);
7161 /// ```
7162 pub fn similarity_inverse_log_weighted(&self) -> IgraphResult<Vec<f64>> {
7163 crate::algorithms::properties::similarity::similarity_inverse_log_weighted(self)
7164 }
7165
7166 /// Jaccard similarity for given edges.
7167 ///
7168 /// ```
7169 /// use rust_igraph::Graph;
7170 ///
7171 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7172 /// let s = g.similarity_jaccard_es(&[0, 1]).unwrap();
7173 /// assert_eq!(s.len(), 2);
7174 /// ```
7175 pub fn similarity_jaccard_es(&self, eids: &[u32]) -> IgraphResult<Vec<f64>> {
7176 crate::algorithms::properties::similarity::similarity_jaccard_es(self, eids)
7177 }
7178
7179 // ---- Layout (batch 6) ----
7180
7181 /// Large Graph Layout (LGL).
7182 ///
7183 /// ```
7184 /// use rust_igraph::{Graph, LglParams};
7185 ///
7186 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7187 /// let pos = g.layout_lgl(&LglParams::default()).unwrap();
7188 /// assert_eq!(pos.len(), 4);
7189 /// ```
7190 pub fn layout_lgl(&self, params: &crate::LglParams) -> IgraphResult<Vec<[f64; 2]>> {
7191 crate::algorithms::layout::lgl::layout_lgl(self, params)
7192 }
7193
7194 /// Random 3D layout.
7195 ///
7196 /// ```
7197 /// use rust_igraph::Graph;
7198 ///
7199 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7200 /// let pos = g.layout_random_3d(42);
7201 /// assert_eq!(pos.len(), 3);
7202 /// ```
7203 pub fn layout_random_3d(&self, seed: u64) -> Vec<(f64, f64, f64)> {
7204 crate::algorithms::layout::simple::layout_random_3d(self, seed)
7205 }
7206
7207 /// Grid 3D layout.
7208 ///
7209 /// ```
7210 /// use rust_igraph::Graph;
7211 ///
7212 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7213 /// let pos = g.layout_grid_3d(2, 2);
7214 /// assert_eq!(pos.len(), 4);
7215 /// ```
7216 pub fn layout_grid_3d(&self, width: i32, height: i32) -> Vec<(f64, f64, f64)> {
7217 crate::algorithms::layout::simple::layout_grid_3d(self, width, height)
7218 }
7219
7220 // ---- Motifs (batch 6) ----
7221
7222 /// Count the total number of motifs of a given size.
7223 ///
7224 /// ```
7225 /// use rust_igraph::Graph;
7226 ///
7227 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7228 /// let n = g.motifs_randesu_no(3).unwrap();
7229 /// assert!((n - 1.0).abs() < 1e-10); // one triangle
7230 /// ```
7231 pub fn motifs_randesu_no(&self, size: u32) -> IgraphResult<f64> {
7232 crate::algorithms::motifs::motifs_randesu::motifs_randesu_no(self, size)
7233 }
7234
7235 // ---- Graph inspection (batch 6) ----
7236
7237 /// Structural summary of the graph.
7238 ///
7239 /// ```
7240 /// use rust_igraph::Graph;
7241 ///
7242 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7243 /// let s = g.graph_summary().unwrap();
7244 /// assert_eq!(s.vcount, 3);
7245 /// assert_eq!(s.ecount, 2);
7246 /// ```
7247 pub fn graph_summary(&self) -> IgraphResult<crate::GraphSummary> {
7248 crate::algorithms::properties::summary::graph_summary(self)
7249 }
7250
7251 /// Human-readable structural summary string.
7252 ///
7253 /// ```
7254 /// use rust_igraph::Graph;
7255 ///
7256 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7257 /// let s = g.graph_summary_string().unwrap();
7258 /// assert!(s.contains("Vertices: 3"));
7259 /// assert!(s.contains("Edges: 2"));
7260 /// ```
7261 pub fn graph_summary_string(&self) -> IgraphResult<String> {
7262 crate::graph_summary_string(self)
7263 }
7264
7265 // ---- Matrix representations (batch 7) ----
7266
7267 /// Adjacency matrix of the graph.
7268 ///
7269 /// Returns a dense `V×V` matrix. For undirected graphs with
7270 /// [`AdjacencyType::Both`](crate::AdjacencyType::Both), the result is symmetric.
7271 ///
7272 /// ```
7273 /// use rust_igraph::{Graph, AdjacencyType, LoopHandling};
7274 ///
7275 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7276 /// let m = g.get_adjacency(AdjacencyType::Both, LoopHandling::Once).unwrap();
7277 /// assert_eq!(m.len(), 3);
7278 /// assert!((m[0][1] - 1.0).abs() < 1e-10);
7279 /// assert!((m[0][2]).abs() < 1e-10);
7280 /// ```
7281 pub fn get_adjacency(
7282 &self,
7283 adj_type: crate::AdjacencyType,
7284 loops: crate::LoopHandling,
7285 ) -> IgraphResult<Vec<Vec<f64>>> {
7286 crate::algorithms::properties::adjacency::get_adjacency(self, adj_type, None, loops)
7287 }
7288
7289 /// Weighted adjacency matrix of the graph.
7290 ///
7291 /// ```
7292 /// use rust_igraph::{Graph, AdjacencyType, LoopHandling};
7293 ///
7294 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7295 /// let w = vec![2.0, 3.0];
7296 /// let m = g.get_adjacency_weighted(AdjacencyType::Both, &w, LoopHandling::Once).unwrap();
7297 /// assert!((m[0][1] - 2.0).abs() < 1e-10);
7298 /// ```
7299 pub fn get_adjacency_weighted(
7300 &self,
7301 adj_type: crate::AdjacencyType,
7302 weights: &[f64],
7303 loops: crate::LoopHandling,
7304 ) -> IgraphResult<Vec<Vec<f64>>> {
7305 crate::algorithms::properties::adjacency::get_adjacency(
7306 self,
7307 adj_type,
7308 Some(weights),
7309 loops,
7310 )
7311 }
7312
7313 /// Laplacian matrix L = D - A (unnormalized, unweighted).
7314 ///
7315 /// ```
7316 /// use rust_igraph::Graph;
7317 ///
7318 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7319 /// let lap = g.get_laplacian().unwrap();
7320 /// assert!((lap[0][0] - 1.0).abs() < 1e-10); // degree of vertex 0
7321 /// assert!((lap[1][1] - 2.0).abs() < 1e-10); // degree of vertex 1
7322 /// ```
7323 pub fn get_laplacian(&self) -> IgraphResult<Vec<Vec<f64>>> {
7324 crate::algorithms::properties::laplacian::get_laplacian(
7325 self,
7326 crate::DegreeMode::All,
7327 crate::LaplacianNormalization::Unnormalized,
7328 None,
7329 )
7330 }
7331
7332 /// Laplacian matrix with normalization and optional weights.
7333 ///
7334 /// ```
7335 /// use rust_igraph::{Graph, DegreeMode, LaplacianNormalization};
7336 ///
7337 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7338 /// let lap = g.get_laplacian_full(
7339 /// DegreeMode::All,
7340 /// LaplacianNormalization::Symmetric,
7341 /// None,
7342 /// ).unwrap();
7343 /// assert!(lap[0][0] > 0.0);
7344 /// ```
7345 pub fn get_laplacian_full(
7346 &self,
7347 mode: crate::DegreeMode,
7348 normalization: crate::LaplacianNormalization,
7349 weights: Option<&[f64]>,
7350 ) -> IgraphResult<Vec<Vec<f64>>> {
7351 crate::algorithms::properties::laplacian::get_laplacian(self, mode, normalization, weights)
7352 }
7353
7354 /// Stochastic (transition) matrix of the graph.
7355 ///
7356 /// Each row (or column, if `column_wise` is true) sums to 1.
7357 ///
7358 /// ```
7359 /// use rust_igraph::Graph;
7360 ///
7361 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,2)], false, None).unwrap();
7362 /// let s = g.get_stochastic(false).unwrap();
7363 /// let row_sum: f64 = s[0].iter().sum();
7364 /// assert!((row_sum - 1.0).abs() < 1e-10);
7365 /// ```
7366 pub fn get_stochastic(&self, column_wise: bool) -> IgraphResult<Vec<Vec<f64>>> {
7367 crate::algorithms::properties::stochastic::get_stochastic(self, column_wise, None)
7368 }
7369
7370 // ---- Spectral embedding (batch 7) ----
7371
7372 /// Adjacency spectral embedding into `no` dimensions.
7373 ///
7374 /// Embeds the graph via the leading eigenvalues/eigenvectors of the
7375 /// adjacency matrix.
7376 ///
7377 /// ```
7378 /// use rust_igraph::{Graph, SpectralWhich};
7379 ///
7380 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3),(3,0)], false, None).unwrap();
7381 /// let emb = g.adjacency_spectral_embedding(2, SpectralWhich::LargestMagnitude).unwrap();
7382 /// assert_eq!(emb.embedding.len(), 4); // one row per vertex
7383 /// ```
7384 pub fn adjacency_spectral_embedding(
7385 &self,
7386 no: usize,
7387 which: crate::SpectralWhich,
7388 ) -> IgraphResult<crate::AdjacencySpectralEmbeddingResult> {
7389 crate::algorithms::embedding::adjacency_spectral_embedding::adjacency_spectral_embedding(
7390 self, no, None, which, true, None,
7391 )
7392 }
7393
7394 /// Laplacian spectral embedding into `no` dimensions.
7395 ///
7396 /// ```
7397 /// use rust_igraph::{Graph, SpectralWhich, LaplacianType};
7398 ///
7399 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3),(3,0)], false, None).unwrap();
7400 /// let emb = g.laplacian_spectral_embedding(
7401 /// 2, SpectralWhich::SmallestAlgebraic, LaplacianType::DA,
7402 /// ).unwrap();
7403 /// assert_eq!(emb.embedding.len(), 4);
7404 /// ```
7405 pub fn laplacian_spectral_embedding(
7406 &self,
7407 no: usize,
7408 which: crate::SpectralWhich,
7409 lap_type: crate::LaplacianType,
7410 ) -> IgraphResult<crate::LaplacianSpectralEmbeddingResult> {
7411 crate::algorithms::embedding::laplacian_spectral_embedding::laplacian_spectral_embedding(
7412 self, no, None, which, lap_type, true,
7413 )
7414 }
7415
7416 /// Eigenvalues and eigenvectors of the adjacency matrix.
7417 ///
7418 /// ```
7419 /// use rust_igraph::{Graph, EigenWhich};
7420 ///
7421 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
7422 /// let eig = g.eigen_adjacency(2, EigenWhich::LargestAlgebraic).unwrap();
7423 /// assert_eq!(eig.eigenvalues.len(), 2);
7424 /// ```
7425 pub fn eigen_adjacency(
7426 &self,
7427 nev: usize,
7428 which: crate::EigenWhich,
7429 ) -> IgraphResult<crate::EigenDecomposition> {
7430 crate::algorithms::eigen::adjacency::eigen_adjacency(self, nev, which)
7431 }
7432
7433 // ---- Additional algorithms (batch 7) ----
7434
7435 /// Feedback vertex set — a minimal set of vertices whose removal
7436 /// makes the graph acyclic.
7437 ///
7438 /// ```
7439 /// use rust_igraph::{Graph, FvsAlgorithm};
7440 ///
7441 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], true, None).unwrap();
7442 /// let fvs = g.feedback_vertex_set(FvsAlgorithm::Greedy).unwrap();
7443 /// assert!(!fvs.is_empty()); // need to break the cycle
7444 /// ```
7445 pub fn feedback_vertex_set(&self, algo: crate::FvsAlgorithm) -> IgraphResult<Vec<VertexId>> {
7446 crate::algorithms::feedback_vertex_set::feedback_vertex_set(self, None, algo)
7447 }
7448
7449 /// Complement graph (all edges that are *not* in the original).
7450 ///
7451 /// Self-loops are excluded.
7452 ///
7453 /// ```
7454 /// use rust_igraph::Graph;
7455 ///
7456 /// let g = Graph::from_edges(&[(0,1)], false, Some(3)).unwrap();
7457 /// let c = g.complementer().unwrap();
7458 /// assert_eq!(c.ecount(), 2); // edges 0-2 and 1-2
7459 /// ```
7460 pub fn complementer(&self) -> IgraphResult<Graph> {
7461 crate::algorithms::operators::complementer::complementer(self, false)
7462 }
7463
7464 /// Bipartite projection sizes without building the projected graphs.
7465 ///
7466 /// `types` assigns each vertex to one of two partitions (`false`/`true`).
7467 ///
7468 /// ```
7469 /// use rust_igraph::Graph;
7470 ///
7471 /// // K_{2,3} bipartite graph
7472 /// let g = Graph::from_edges(
7473 /// &[(0,2),(0,3),(0,4),(1,2),(1,3),(1,4)], false, None
7474 /// ).unwrap();
7475 /// let types = vec![false, false, true, true, true];
7476 /// let sz = g.bipartite_projection_size(&types).unwrap();
7477 /// assert_eq!(sz.vcount1, 2);
7478 /// assert_eq!(sz.vcount2, 3);
7479 /// ```
7480 pub fn bipartite_projection_size(
7481 &self,
7482 types: &[bool],
7483 ) -> IgraphResult<crate::BipartiteProjectionSize> {
7484 crate::algorithms::operators::bipartite_projection_size::bipartite_projection_size(
7485 self, types,
7486 )
7487 }
7488
7489 /// Unfold the graph into a tree by BFS from root vertices.
7490 ///
7491 /// Returns the unfolded tree and a mapping from new to old vertex ids.
7492 ///
7493 /// ```
7494 /// use rust_igraph::{Graph, DegreeMode, DijkstraMode, UnfoldTreeResult};
7495 ///
7496 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
7497 /// let r = g.unfold_tree(&[0], DegreeMode::All).unwrap();
7498 /// assert!(r.tree.is_tree(DijkstraMode::All).unwrap().is_some());
7499 /// assert!(!r.vertex_index.is_empty());
7500 /// ```
7501 pub fn unfold_tree(
7502 &self,
7503 roots: &[VertexId],
7504 mode: crate::DegreeMode,
7505 ) -> IgraphResult<crate::UnfoldTreeResult> {
7506 crate::algorithms::properties::unfold_tree::unfold_tree(self, roots, mode)
7507 }
7508
7509 // ---- Analysis / flow / isomorphism (batch 8) ----
7510
7511 /// S-t edge connectivity between two vertices.
7512 ///
7513 /// ```
7514 /// use rust_igraph::Graph;
7515 ///
7516 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,3),(2,3)], false, None).unwrap();
7517 /// let k = g.st_edge_connectivity(0, 3).unwrap();
7518 /// assert_eq!(k, 2);
7519 /// ```
7520 pub fn st_edge_connectivity(&self, source: u32, target: u32) -> IgraphResult<i64> {
7521 crate::st_edge_connectivity(self, source, target)
7522 }
7523
7524 /// Vertex-disjoint paths between two vertices.
7525 ///
7526 /// ```
7527 /// use rust_igraph::Graph;
7528 ///
7529 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,3),(2,3)], false, None).unwrap();
7530 /// let n = g.vertex_disjoint_paths(0, 3).unwrap();
7531 /// assert_eq!(n, 2);
7532 /// ```
7533 pub fn vertex_disjoint_paths(&self, source: u32, target: u32) -> IgraphResult<i64> {
7534 crate::vertex_disjoint_paths(self, source, target)
7535 }
7536
7537 /// Test isomorphism using BLISS canonical labeling.
7538 ///
7539 /// ```
7540 /// use rust_igraph::{Graph, full_graph};
7541 ///
7542 /// let g1 = full_graph(4, false, false).unwrap();
7543 /// let g2 = full_graph(4, false, false).unwrap();
7544 /// let result = g1.isomorphic_bliss(&g2, None, None).unwrap();
7545 /// assert!(result.iso);
7546 /// ```
7547 pub fn isomorphic_bliss(
7548 &self,
7549 other: &Graph,
7550 colors1: Option<&[u32]>,
7551 colors2: Option<&[u32]>,
7552 ) -> IgraphResult<crate::Vf2Isomorphism> {
7553 crate::isomorphic_bliss(self, other, colors1, colors2)
7554 }
7555
7556 /// LAD subgraph isomorphism test.
7557 ///
7558 /// ```
7559 /// use rust_igraph::Graph;
7560 ///
7561 /// let target = Graph::from_edges(&[(0,1),(1,2),(2,0),(2,3)], false, None).unwrap();
7562 /// let pattern = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7563 /// let iso = target.subisomorphic_lad(&pattern, false, None).unwrap();
7564 /// assert!(iso.iso);
7565 /// ```
7566 pub fn subisomorphic_lad(
7567 &self,
7568 pattern: &Graph,
7569 induced: bool,
7570 domains: Option<&[Vec<u32>]>,
7571 ) -> IgraphResult<crate::LadSubisomorphism> {
7572 crate::subisomorphic_lad(pattern, self, domains, induced)
7573 }
7574
7575 /// Betweenness centrality for a subset of vertices.
7576 ///
7577 /// ```
7578 /// use rust_igraph::Graph;
7579 ///
7580 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7581 /// let bc = g.betweenness_subset(&[0, 1], &[2, 3]).unwrap();
7582 /// assert_eq!(bc.len(), 4);
7583 /// ```
7584 pub fn betweenness_subset(&self, sources: &[u32], targets: &[u32]) -> IgraphResult<Vec<f64>> {
7585 crate::betweenness_subset(self, sources, targets, self.is_directed())
7586 }
7587
7588 /// Edge betweenness centrality for a subset of vertices.
7589 ///
7590 /// ```
7591 /// use rust_igraph::Graph;
7592 ///
7593 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7594 /// let ebc = g.edge_betweenness_subset(&[0, 1], &[2, 3]).unwrap();
7595 /// assert_eq!(ebc.len(), 3);
7596 /// ```
7597 pub fn edge_betweenness_subset(
7598 &self,
7599 sources: &[u32],
7600 targets: &[u32],
7601 ) -> IgraphResult<Vec<f64>> {
7602 crate::edge_betweenness_subset(self, sources, targets, self.is_directed())
7603 }
7604
7605 /// Weighted edge betweenness community detection.
7606 ///
7607 /// ```
7608 /// use rust_igraph::Graph;
7609 ///
7610 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0),(2,3),(3,4),(4,5),(5,3)], false, None).unwrap();
7611 /// let w = vec![1.0; g.ecount() as usize];
7612 /// let result = g.edge_betweenness_community_weighted(&w).unwrap();
7613 /// assert!(!result.merges.is_empty());
7614 /// ```
7615 pub fn edge_betweenness_community_weighted(
7616 &self,
7617 weights: &[f64],
7618 ) -> IgraphResult<crate::EdgeBetweennessResult> {
7619 crate::edge_betweenness_community_weighted(self, weights)
7620 }
7621
7622 /// Modularity matrix B = A - k*k'/2m.
7623 ///
7624 /// ```
7625 /// use rust_igraph::Graph;
7626 ///
7627 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
7628 /// let b = g.modularity_matrix(None).unwrap();
7629 /// assert_eq!(b.len(), 3);
7630 /// ```
7631 pub fn modularity_matrix(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<Vec<f64>>> {
7632 crate::modularity_matrix(self, weights, 1.0, self.is_directed())
7633 }
7634
7635 /// Whether the graph is the same as another (structural equality).
7636 ///
7637 /// ```
7638 /// use rust_igraph::Graph;
7639 ///
7640 /// let g1 = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7641 /// let g2 = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7642 /// assert!(g1.is_same_graph(&g2));
7643 /// ```
7644 pub fn is_same_graph(&self, other: &Graph) -> bool {
7645 crate::is_same_graph(self, other)
7646 }
7647
7648 /// Mean distance (weighted).
7649 ///
7650 /// ```
7651 /// use rust_igraph::Graph;
7652 ///
7653 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7654 /// let w = vec![1.0, 2.0];
7655 /// let md = g.mean_distance_weighted(&w).unwrap();
7656 /// assert!(md.is_some());
7657 /// ```
7658 pub fn mean_distance_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
7659 crate::mean_distance_weighted(self, weights, self.is_directed(), true)
7660 }
7661
7662 // ---- Attribute system ----
7663
7664 /// Set a graph-level attribute.
7665 ///
7666 /// Overwrites any existing value with the same name.
7667 ///
7668 /// ```
7669 /// use rust_igraph::{Graph, AttributeValue};
7670 ///
7671 /// let mut g = Graph::with_vertices(0);
7672 /// g.set_graph_attribute("name", "test".into());
7673 /// assert_eq!(
7674 /// g.graph_attribute("name").and_then(|v| v.as_str()),
7675 /// Some("test"),
7676 /// );
7677 /// ```
7678 pub fn set_graph_attribute(&mut self, name: impl Into<String>, value: AttributeValue) {
7679 self.gattrs.insert(name.into(), value);
7680 }
7681
7682 /// Get a graph-level attribute by name.
7683 ///
7684 /// ```
7685 /// use rust_igraph::{Graph, AttributeValue};
7686 ///
7687 /// let g = Graph::with_vertices(0);
7688 /// assert!(g.graph_attribute("missing").is_none());
7689 /// ```
7690 #[must_use]
7691 pub fn graph_attribute(&self, name: &str) -> Option<&AttributeValue> {
7692 self.gattrs.get(name)
7693 }
7694
7695 /// Delete a graph-level attribute. Returns `true` if it existed.
7696 pub fn delete_graph_attribute(&mut self, name: &str) -> bool {
7697 self.gattrs.remove(name).is_some()
7698 }
7699
7700 /// Check whether a graph-level attribute exists.
7701 #[must_use]
7702 pub fn has_graph_attribute(&self, name: &str) -> bool {
7703 self.gattrs.contains_key(name)
7704 }
7705
7706 /// List all graph-level attribute names.
7707 ///
7708 /// ```
7709 /// use rust_igraph::{Graph, AttributeValue};
7710 ///
7711 /// let mut g = Graph::with_vertices(0);
7712 /// g.set_graph_attribute("name", "test".into());
7713 /// g.set_graph_attribute("year", 2024.0.into());
7714 /// let names = g.graph_attribute_names();
7715 /// assert!(names.contains(&"name"));
7716 /// assert!(names.contains(&"year"));
7717 /// ```
7718 #[must_use]
7719 pub fn graph_attribute_names(&self) -> Vec<&str> {
7720 self.gattrs.keys().map(String::as_str).collect()
7721 }
7722
7723 /// Set a vertex attribute for a single vertex.
7724 ///
7725 /// Creates the attribute vector if it doesn't exist. New entries
7726 /// for other vertices are filled with a type-appropriate default.
7727 ///
7728 /// ```
7729 /// use rust_igraph::{Graph, AttributeValue};
7730 ///
7731 /// let mut g = Graph::with_vertices(3);
7732 /// g.set_vertex_attribute("label", 0, "Alice".into()).unwrap();
7733 /// g.set_vertex_attribute("label", 1, "Bob".into()).unwrap();
7734 /// assert_eq!(
7735 /// g.vertex_attribute("label", 0).and_then(|v| v.as_str()),
7736 /// Some("Alice"),
7737 /// );
7738 /// ```
7739 pub fn set_vertex_attribute(
7740 &mut self,
7741 name: impl Into<String>,
7742 vertex: VertexId,
7743 value: AttributeValue,
7744 ) -> IgraphResult<()> {
7745 self.check_vertex(vertex)?;
7746 let n = self.n as usize;
7747 let key = name.into();
7748 let vals = self.vertex_attrs.entry(key).or_insert_with(|| {
7749 let default = value.default_for_same_type();
7750 vec![default; n]
7751 });
7752 vals[vertex as usize] = value;
7753 Ok(())
7754 }
7755
7756 /// Set a vertex attribute for all vertices at once.
7757 ///
7758 /// The `values` slice must have length equal to `vcount()`.
7759 ///
7760 /// ```
7761 /// use rust_igraph::{Graph, AttributeValue};
7762 ///
7763 /// let mut g = Graph::with_vertices(3);
7764 /// g.set_vertex_attribute_all(
7765 /// "color",
7766 /// vec![1.0.into(), 2.0.into(), 3.0.into()],
7767 /// ).unwrap();
7768 /// let colors = g.vertex_attributes("color").unwrap();
7769 /// assert_eq!(colors.len(), 3);
7770 /// ```
7771 pub fn set_vertex_attribute_all(
7772 &mut self,
7773 name: impl Into<String>,
7774 values: Vec<AttributeValue>,
7775 ) -> IgraphResult<()> {
7776 if values.len() != self.n as usize {
7777 return Err(IgraphError::InvalidArgument(format!(
7778 "attribute vector length {} does not match vcount {}",
7779 values.len(),
7780 self.n,
7781 )));
7782 }
7783 self.vertex_attrs.insert(name.into(), values);
7784 Ok(())
7785 }
7786
7787 /// Get a vertex attribute for a single vertex.
7788 #[must_use]
7789 pub fn vertex_attribute(&self, name: &str, vertex: VertexId) -> Option<&AttributeValue> {
7790 self.vertex_attrs
7791 .get(name)
7792 .and_then(|vals| vals.get(vertex as usize))
7793 }
7794
7795 /// Get the full vertex attribute vector by name.
7796 #[must_use]
7797 pub fn vertex_attributes(&self, name: &str) -> Option<&[AttributeValue]> {
7798 self.vertex_attrs.get(name).map(Vec::as_slice)
7799 }
7800
7801 /// Delete a vertex attribute. Returns `true` if it existed.
7802 pub fn delete_vertex_attribute(&mut self, name: &str) -> bool {
7803 self.vertex_attrs.remove(name).is_some()
7804 }
7805
7806 /// Check whether a vertex attribute exists.
7807 #[must_use]
7808 pub fn has_vertex_attribute(&self, name: &str) -> bool {
7809 self.vertex_attrs.contains_key(name)
7810 }
7811
7812 /// List all vertex attribute names.
7813 ///
7814 /// ```
7815 /// use rust_igraph::{Graph, AttributeValue};
7816 ///
7817 /// let mut g = Graph::with_vertices(2);
7818 /// g.set_vertex_attribute("name", 0, "A".into()).unwrap();
7819 /// assert!(g.vertex_attribute_names().contains(&"name"));
7820 /// ```
7821 #[must_use]
7822 pub fn vertex_attribute_names(&self) -> Vec<&str> {
7823 self.vertex_attrs.keys().map(String::as_str).collect()
7824 }
7825
7826 /// Set an edge attribute for a single edge.
7827 ///
7828 /// Creates the attribute vector if it doesn't exist. New entries
7829 /// for other edges are filled with a type-appropriate default.
7830 ///
7831 /// ```
7832 /// use rust_igraph::{Graph, AttributeValue};
7833 ///
7834 /// let mut g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7835 /// g.set_edge_attribute("weight", 0, 1.5.into()).unwrap();
7836 /// assert_eq!(
7837 /// g.edge_attribute("weight", 0).and_then(|v| v.as_f64()),
7838 /// Some(1.5),
7839 /// );
7840 /// ```
7841 pub fn set_edge_attribute(
7842 &mut self,
7843 name: impl Into<String>,
7844 edge: EdgeId,
7845 value: AttributeValue,
7846 ) -> IgraphResult<()> {
7847 let m = self.ecount();
7848 if (edge as usize) >= m {
7849 return Err(IgraphError::EdgeOutOfRange {
7850 id: edge,
7851 m: u32::try_from(m).unwrap_or(u32::MAX),
7852 });
7853 }
7854 let key = name.into();
7855 let vals = self.edge_attrs.entry(key).or_insert_with(|| {
7856 let default = value.default_for_same_type();
7857 vec![default; m]
7858 });
7859 vals[edge as usize] = value;
7860 Ok(())
7861 }
7862
7863 /// Set an edge attribute for all edges at once.
7864 ///
7865 /// The `values` slice must have length equal to `ecount()`.
7866 ///
7867 /// ```
7868 /// use rust_igraph::{Graph, AttributeValue};
7869 ///
7870 /// let mut g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
7871 /// g.set_edge_attribute_all(
7872 /// "weight",
7873 /// vec![1.0.into(), 2.0.into(), 3.0.into()],
7874 /// ).unwrap();
7875 /// let w = g.edge_attributes("weight").unwrap();
7876 /// assert_eq!(w.len(), 3);
7877 /// ```
7878 pub fn set_edge_attribute_all(
7879 &mut self,
7880 name: impl Into<String>,
7881 values: Vec<AttributeValue>,
7882 ) -> IgraphResult<()> {
7883 let m = self.ecount();
7884 if values.len() != m {
7885 return Err(IgraphError::InvalidArgument(format!(
7886 "attribute vector length {} does not match ecount {}",
7887 values.len(),
7888 m,
7889 )));
7890 }
7891 self.edge_attrs.insert(name.into(), values);
7892 Ok(())
7893 }
7894
7895 /// Get an edge attribute for a single edge.
7896 #[must_use]
7897 pub fn edge_attribute(&self, name: &str, edge: EdgeId) -> Option<&AttributeValue> {
7898 self.edge_attrs
7899 .get(name)
7900 .and_then(|vals| vals.get(edge as usize))
7901 }
7902
7903 /// Get the full edge attribute vector by name.
7904 #[must_use]
7905 pub fn edge_attributes(&self, name: &str) -> Option<&[AttributeValue]> {
7906 self.edge_attrs.get(name).map(Vec::as_slice)
7907 }
7908
7909 /// Delete an edge attribute. Returns `true` if it existed.
7910 pub fn delete_edge_attribute(&mut self, name: &str) -> bool {
7911 self.edge_attrs.remove(name).is_some()
7912 }
7913
7914 /// Check whether an edge attribute exists.
7915 #[must_use]
7916 pub fn has_edge_attribute(&self, name: &str) -> bool {
7917 self.edge_attrs.contains_key(name)
7918 }
7919
7920 /// List all edge attribute names.
7921 ///
7922 /// ```
7923 /// use rust_igraph::{Graph, AttributeValue};
7924 ///
7925 /// let mut g = Graph::from_edges(&[(0,1)], false, None).unwrap();
7926 /// g.set_edge_attribute("weight", 0, 1.0.into()).unwrap();
7927 /// assert!(g.edge_attribute_names().contains(&"weight"));
7928 /// ```
7929 #[must_use]
7930 pub fn edge_attribute_names(&self) -> Vec<&str> {
7931 self.edge_attrs.keys().map(String::as_str).collect()
7932 }
7933}
7934
7935impl std::fmt::Display for Graph {
7936 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7937 let kind = if self.directed {
7938 "Directed"
7939 } else {
7940 "Undirected"
7941 };
7942 write!(
7943 f,
7944 "{kind} graph with {} vertices and {} edges",
7945 self.n,
7946 self.ecount()
7947 )
7948 }
7949}
7950
7951/// Iterate over a graph's edges by reference.
7952///
7953/// Yields `(from, to)` pairs in edge-id order, enabling the idiomatic
7954/// `for (u, v) in &graph { ... }` pattern.
7955///
7956/// # Examples
7957///
7958/// ```
7959/// use rust_igraph::Graph;
7960///
7961/// let mut g = Graph::with_vertices(3);
7962/// g.add_edge(0, 1).unwrap();
7963/// g.add_edge(1, 2).unwrap();
7964///
7965/// let edges: Vec<_> = (&g).into_iter().collect();
7966/// assert_eq!(edges, vec![(0, 1), (1, 2)]);
7967/// ```
7968impl<'a> IntoIterator for &'a Graph {
7969 type Item = (VertexId, VertexId);
7970 type IntoIter = EdgeIter<'a>;
7971
7972 fn into_iter(self) -> Self::IntoIter {
7973 self.iter()
7974 }
7975}
7976
7977/// Construct an undirected graph from a slice of `(from, to)` edge pairs.
7978///
7979/// Vertex count is inferred from the maximum endpoint id (plus one).
7980/// This is a convenience for quick construction; for more control use
7981/// [`Graph::from_edges`] or [`GraphBuilder`](super::builder::GraphBuilder).
7982///
7983/// # Examples
7984///
7985/// ```
7986/// use rust_igraph::Graph;
7987///
7988/// let edges = vec![(0u32, 1), (1, 2), (2, 0)];
7989/// let g = Graph::try_from(edges.as_slice()).unwrap();
7990/// assert_eq!(g.vcount(), 3);
7991/// assert_eq!(g.ecount(), 3);
7992/// assert!(!g.is_directed());
7993/// ```
7994impl TryFrom<&[(VertexId, VertexId)]> for Graph {
7995 type Error = IgraphError;
7996
7997 fn try_from(edges: &[(VertexId, VertexId)]) -> IgraphResult<Self> {
7998 let n = match edges.iter().flat_map(|&(u, v)| [u, v]).max() {
7999 Some(m) => m
8000 .checked_add(1)
8001 .ok_or_else(|| IgraphError::InvalidArgument("vertex id overflow".to_owned()))?,
8002 None => 0,
8003 };
8004 let mut g = Self::new(n, false)?;
8005 g.add_edges(edges.to_vec())?;
8006 Ok(g)
8007 }
8008}
8009
8010/// Construct an undirected graph from a `Vec` of `(from, to)` edge pairs.
8011///
8012/// # Examples
8013///
8014/// ```
8015/// use rust_igraph::Graph;
8016///
8017/// let g = Graph::try_from(vec![(0u32, 1), (1, 2), (2, 3)]).unwrap();
8018/// assert_eq!(g.vcount(), 4);
8019/// assert_eq!(g.ecount(), 3);
8020/// ```
8021impl TryFrom<Vec<(VertexId, VertexId)>> for Graph {
8022 type Error = IgraphError;
8023
8024 fn try_from(edges: Vec<(VertexId, VertexId)>) -> IgraphResult<Self> {
8025 let n = match edges.iter().flat_map(|&(u, v)| [u, v]).max() {
8026 Some(m) => m
8027 .checked_add(1)
8028 .ok_or_else(|| IgraphError::InvalidArgument("vertex id overflow".to_owned()))?,
8029 None => 0,
8030 };
8031 let mut g = Self::new(n, false)?;
8032 g.add_edges(edges)?;
8033 Ok(g)
8034 }
8035}
8036
8037/// Collect edges from an iterator into an undirected graph.
8038///
8039/// Vertex count is inferred from the maximum endpoint id.
8040/// For directed graphs or explicit vertex counts, use [`Graph::from_edges`].
8041///
8042/// # Panics
8043///
8044/// Panics if a vertex id would overflow `u32::MAX`.
8045///
8046/// # Examples
8047///
8048/// ```
8049/// use rust_igraph::Graph;
8050///
8051/// let g: Graph = [(0u32, 1), (1, 2), (2, 0)].into_iter().collect();
8052/// assert_eq!(g.vcount(), 3);
8053/// assert_eq!(g.ecount(), 3);
8054/// ```
8055impl std::iter::FromIterator<(VertexId, VertexId)> for Graph {
8056 fn from_iter<I: IntoIterator<Item = (VertexId, VertexId)>>(iter: I) -> Self {
8057 let edges: Vec<(VertexId, VertexId)> = iter.into_iter().collect();
8058 Self::try_from(edges).expect("FromIterator: vertex id overflow or invalid edge")
8059 }
8060}
8061
8062/// Extend a graph by adding edges from an iterator.
8063///
8064/// New vertices are automatically created as needed. This enables
8065/// patterns like `graph.extend(new_edges)`.
8066///
8067/// # Panics
8068///
8069/// Panics if an edge endpoint exceeds the current vertex count and
8070/// cannot be added.
8071///
8072/// # Examples
8073///
8074/// ```
8075/// use rust_igraph::Graph;
8076///
8077/// let mut g = Graph::with_vertices(3);
8078/// g.extend([(0u32, 1), (1, 2)]);
8079/// assert_eq!(g.ecount(), 2);
8080///
8081/// // Extending with a vertex beyond current count grows the graph
8082/// g.extend([(2u32, 5)]);
8083/// assert_eq!(g.vcount(), 6);
8084/// assert_eq!(g.ecount(), 3);
8085/// ```
8086impl Extend<(VertexId, VertexId)> for Graph {
8087 fn extend<I: IntoIterator<Item = (VertexId, VertexId)>>(&mut self, iter: I) {
8088 let edges: Vec<(VertexId, VertexId)> = iter.into_iter().collect();
8089 if edges.is_empty() {
8090 return;
8091 }
8092 let max_id = edges
8093 .iter()
8094 .flat_map(|&(u, v)| [u, v])
8095 .max()
8096 .expect("non-empty edges");
8097 if max_id >= self.n {
8098 self.add_vertices(max_id - self.n + 1)
8099 .expect("Extend: failed to add vertices");
8100 }
8101 self.add_edges(edges).expect("Extend: failed to add edges");
8102 }
8103}
8104
8105/// Structural equality: two graphs are equal if they have the same
8106/// directedness, same vertex count, and the same sorted edge set.
8107///
8108/// This is *not* isomorphism — vertex ids must match exactly.
8109///
8110/// # Examples
8111///
8112/// ```
8113/// use rust_igraph::Graph;
8114///
8115/// let a = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
8116/// let b = Graph::from_edges(&[(1,2), (0,1)], false, None).unwrap();
8117/// assert_eq!(a, b); // same edges, different insertion order
8118///
8119/// let c = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
8120/// assert_ne!(a, c); // different directedness
8121/// ```
8122impl PartialEq for Graph {
8123 fn eq(&self, other: &Self) -> bool {
8124 if self.directed != other.directed || self.n != other.n || self.ecount() != other.ecount() {
8125 return false;
8126 }
8127 let mut self_edges: Vec<(VertexId, VertexId)> = self.iter().collect();
8128 let mut other_edges: Vec<(VertexId, VertexId)> = other.iter().collect();
8129 self_edges.sort_unstable();
8130 other_edges.sort_unstable();
8131 self_edges == other_edges
8132 }
8133}
8134
8135impl Eq for Graph {}
8136
8137/// Hash a graph by its structural content (directedness + vertex count +
8138/// sorted edge set).
8139///
8140/// This is consistent with the [`PartialEq`] impl: structurally equal
8141/// graphs produce the same hash.
8142impl std::hash::Hash for Graph {
8143 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
8144 self.directed.hash(state);
8145 self.n.hash(state);
8146 let mut edges: Vec<(VertexId, VertexId)> = self.iter().collect();
8147 edges.sort_unstable();
8148 edges.hash(state);
8149 }
8150}
8151
8152#[cfg(test)]
8153mod tests {
8154 use super::*;
8155
8156 #[test]
8157 fn empty_graph_counts() {
8158 let g = Graph::with_vertices(0);
8159 assert_eq!(g.vcount(), 0);
8160 assert_eq!(g.ecount(), 0);
8161 assert!(!g.is_directed());
8162 }
8163
8164 #[test]
8165 fn new_directed_flag() {
8166 let g = Graph::new(3, true).unwrap();
8167 assert!(g.is_directed());
8168 let g = Graph::new(3, false).unwrap();
8169 assert!(!g.is_directed());
8170 }
8171
8172 #[test]
8173 fn add_vertices_then_edges() {
8174 let mut g = Graph::with_vertices(3);
8175 g.add_edge(0, 1).unwrap();
8176 g.add_edge(1, 2).unwrap();
8177 assert_eq!(g.vcount(), 3);
8178 assert_eq!(g.ecount(), 2);
8179 assert_eq!(g.degree(1).unwrap(), 2);
8180 let mut nbrs = g.neighbors(1).unwrap();
8181 nbrs.sort_unstable();
8182 assert_eq!(nbrs, vec![0, 2]);
8183 }
8184
8185 #[test]
8186 fn out_of_range_vertex_errors() {
8187 let mut g = Graph::with_vertices(2);
8188 let err = g.add_edge(0, 5).unwrap_err();
8189 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 2 }));
8190 }
8191
8192 #[test]
8193 fn self_loop_counted_correctly() {
8194 let mut g = Graph::with_vertices(1);
8195 g.add_edge(0, 0).unwrap();
8196 assert_eq!(g.ecount(), 1);
8197 // Undirected self-loop: appears as both out and in, degree == 2.
8198 assert_eq!(g.degree(0).unwrap(), 2);
8199 let mut nbrs = g.neighbors(0).unwrap();
8200 nbrs.sort_unstable();
8201 assert_eq!(nbrs, vec![0, 0]);
8202 }
8203
8204 #[test]
8205 fn parallel_edges() {
8206 let mut g = Graph::with_vertices(2);
8207 g.add_edge(0, 1).unwrap();
8208 g.add_edge(0, 1).unwrap();
8209 assert_eq!(g.ecount(), 2);
8210 assert_eq!(g.degree(0).unwrap(), 2);
8211 assert_eq!(g.degree(1).unwrap(), 2);
8212 }
8213
8214 #[test]
8215 fn undirected_canonicalisation() {
8216 // Adding edges (1,0) and (0,1) — both stored canonically as (0,1).
8217 let mut g = Graph::with_vertices(2);
8218 g.add_edge(1, 0).unwrap();
8219 g.add_edge(0, 1).unwrap();
8220 assert_eq!(g.ecount(), 2);
8221 // Both vertices see each other as a neighbour twice.
8222 let mut n0 = g.neighbors(0).unwrap();
8223 let mut n1 = g.neighbors(1).unwrap();
8224 n0.sort_unstable();
8225 n1.sort_unstable();
8226 assert_eq!(n0, vec![1, 1]);
8227 assert_eq!(n1, vec![0, 0]);
8228 }
8229
8230 #[test]
8231 fn directed_neighbors_are_outgoing_only() {
8232 let mut g = Graph::new(3, true).unwrap();
8233 g.add_edge(0, 1).unwrap();
8234 g.add_edge(2, 0).unwrap();
8235 // Directed: neighbors(0) returns out-neighbours only.
8236 assert_eq!(g.neighbors(0).unwrap(), vec![1]);
8237 // Vertex 2 has out-edge to 0.
8238 assert_eq!(g.neighbors(2).unwrap(), vec![0]);
8239 // Vertex 1 has no out-edges.
8240 assert!(g.neighbors(1).unwrap().is_empty());
8241 // Degree counts both in and out for directed.
8242 assert_eq!(g.degree(0).unwrap(), 2); // out: 0->1, in: 2->0
8243 assert_eq!(g.degree(1).unwrap(), 1); // in: 0->1
8244 assert_eq!(g.degree(2).unwrap(), 1); // out: 2->0
8245 }
8246
8247 #[test]
8248 fn add_edges_batch_then_rebuild() {
8249 let mut g = Graph::with_vertices(4);
8250 g.add_edges(vec![(0, 1), (0, 2), (1, 2), (2, 3)]).unwrap();
8251 assert_eq!(g.ecount(), 4);
8252 // Degrees: 0->{1,2} d=2; 1->{0,2} d=2; 2->{0,1,3} d=3; 3->{2} d=1.
8253 assert_eq!(g.degree(0).unwrap(), 2);
8254 assert_eq!(g.degree(1).unwrap(), 2);
8255 assert_eq!(g.degree(2).unwrap(), 3);
8256 assert_eq!(g.degree(3).unwrap(), 1);
8257 }
8258
8259 #[test]
8260 fn clone_is_deep() {
8261 let mut g = Graph::with_vertices(3);
8262 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8263 let g2 = g.clone();
8264 // Mutate g; g2 must be unaffected.
8265 g.add_edge(0, 2).unwrap();
8266 assert_eq!(g.ecount(), 3);
8267 assert_eq!(g2.ecount(), 2);
8268 }
8269
8270 #[test]
8271 fn os_invariant_is_monotone() {
8272 let mut g = Graph::with_vertices(5);
8273 g.add_edges(vec![(0, 1), (0, 2), (3, 4), (1, 2)]).unwrap();
8274 // os should be non-decreasing and end at ecount.
8275 for w in g.os.windows(2) {
8276 assert!(w[0] <= w[1]);
8277 }
8278 assert_eq!(g.os[0], 0);
8279 assert_eq!(*g.os.last().unwrap() as usize, g.ecount());
8280 }
8281
8282 #[test]
8283 fn vertex_out_of_range_when_adding_edge() {
8284 let mut g = Graph::with_vertices(2);
8285 let e = g.add_edge(2, 0).unwrap_err();
8286 assert!(matches!(e, IgraphError::VertexOutOfRange { id: 2, n: 2 }));
8287 // Graph state must be unchanged after the failed add.
8288 assert_eq!(g.ecount(), 0);
8289 }
8290
8291 // -------- ALGO-CORE-001b: edge-id helpers + incident --------
8292
8293 #[test]
8294 fn edge_endpoints_round_trip() {
8295 let mut g = Graph::new(3, true).unwrap();
8296 g.add_edges(vec![(0, 1), (2, 0), (1, 2)]).unwrap();
8297 // Directed: order preserved. edge_id == position in from/to.
8298 assert_eq!(g.edge(0).unwrap(), (0, 1));
8299 assert_eq!(g.edge(1).unwrap(), (2, 0));
8300 assert_eq!(g.edge(2).unwrap(), (1, 2));
8301 assert_eq!(g.edge_source(1).unwrap(), 2);
8302 assert_eq!(g.edge_target(1).unwrap(), 0);
8303 }
8304
8305 #[test]
8306 fn edge_other_endpoint() {
8307 let mut g = Graph::with_vertices(3);
8308 g.add_edge(0, 2).unwrap();
8309 assert_eq!(g.edge_other(0, 0).unwrap(), 2);
8310 assert_eq!(g.edge_other(0, 2).unwrap(), 0);
8311 // Vertex not on the edge: error.
8312 let err = g.edge_other(0, 1).unwrap_err();
8313 assert!(matches!(err, IgraphError::InvalidArgument(_)));
8314 }
8315
8316 #[test]
8317 fn edge_out_of_range() {
8318 let mut g = Graph::with_vertices(2);
8319 g.add_edge(0, 1).unwrap();
8320 let err = g.edge(5).unwrap_err();
8321 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 1 }));
8322 }
8323
8324 #[test]
8325 fn incident_returns_edge_ids_matching_neighbors_order() {
8326 let mut g = Graph::with_vertices(4);
8327 g.add_edges(vec![(0, 1), (0, 2), (3, 0)]).unwrap();
8328 let eids = g.incident(0).unwrap();
8329 // Expect three incident edges; resolving back to neighbours
8330 // must equal `neighbors(0)` exactly (same iteration order).
8331 let resolved: Vec<u32> = eids.iter().map(|&e| g.edge_other(e, 0).unwrap()).collect();
8332 assert_eq!(resolved, g.neighbors(0).unwrap());
8333 }
8334
8335 #[test]
8336 fn incident_self_loop_appears_twice_undirected() {
8337 let mut g = Graph::with_vertices(1);
8338 g.add_edge(0, 0).unwrap();
8339 let eids = g.incident(0).unwrap();
8340 // Undirected self-loop appears once on the out side and once on
8341 // the in side — same edge id, twice. Mirrors `neighbors`.
8342 assert_eq!(eids, vec![0, 0]);
8343 assert_eq!(g.degree(0).unwrap(), 2);
8344 }
8345
8346 #[test]
8347 fn incident_directed_returns_outgoing_only() {
8348 let mut g = Graph::new(3, true).unwrap();
8349 g.add_edges(vec![(0, 1), (2, 0)]).unwrap();
8350 // Directed `incident` mirrors directed `neighbors` (out only).
8351 assert_eq!(g.incident(0).unwrap(), vec![0]);
8352 assert_eq!(g.incident(2).unwrap(), vec![1]);
8353 assert!(g.incident(1).unwrap().is_empty());
8354 }
8355
8356 #[test]
8357 fn get_eid_undirected_finds_edge_either_way() {
8358 let mut g = Graph::with_vertices(3);
8359 g.add_edge(0, 1).unwrap(); // edge 0
8360 g.add_edge(1, 2).unwrap(); // edge 1
8361 assert_eq!(g.get_eid(0, 1).unwrap(), 0);
8362 assert_eq!(g.get_eid(1, 0).unwrap(), 0);
8363 assert_eq!(g.get_eid(1, 2).unwrap(), 1);
8364 assert_eq!(g.get_eid(2, 1).unwrap(), 1);
8365 }
8366
8367 #[test]
8368 fn get_eid_directed_respects_direction() {
8369 let mut g = Graph::new(3, true).unwrap();
8370 g.add_edge(0, 1).unwrap(); // edge 0
8371 assert_eq!(g.get_eid(0, 1).unwrap(), 0);
8372 assert!(g.get_eid(1, 0).is_err()); // reverse direction has no edge
8373 }
8374
8375 #[test]
8376 fn find_eid_returns_none_for_missing() {
8377 let mut g = Graph::with_vertices(3);
8378 g.add_edge(0, 1).unwrap();
8379 assert_eq!(g.find_eid(0, 2).unwrap(), None);
8380 assert!(g.find_eid(0, 99).is_err()); // out-of-range vertex
8381 }
8382
8383 #[test]
8384 fn get_eid_self_loop() {
8385 let mut g = Graph::with_vertices(2);
8386 g.add_edge(0, 0).unwrap(); // self-loop, edge 0
8387 g.add_edge(0, 1).unwrap(); // edge 1
8388 assert_eq!(g.get_eid(0, 0).unwrap(), 0);
8389 assert_eq!(g.get_eid(0, 1).unwrap(), 1);
8390 }
8391
8392 #[test]
8393 fn get_all_eids_between_returns_all_parallel() {
8394 let mut g = Graph::with_vertices(2);
8395 g.add_edge(0, 1).unwrap(); // edge 0
8396 g.add_edge(0, 1).unwrap(); // edge 1
8397 g.add_edge(0, 1).unwrap(); // edge 2
8398 let eids = g.get_all_eids_between(0, 1).unwrap();
8399 assert_eq!(eids, vec![0, 1, 2]);
8400 // Reverse direction yields the same edges on undirected.
8401 let eids = g.get_all_eids_between(1, 0).unwrap();
8402 assert_eq!(eids, vec![0, 1, 2]);
8403 }
8404
8405 #[test]
8406 fn get_all_eids_between_directed_one_way_only() {
8407 let mut g = Graph::new(2, true).unwrap();
8408 g.add_edge(0, 1).unwrap(); // edge 0
8409 g.add_edge(0, 1).unwrap(); // edge 1 (parallel)
8410 assert_eq!(g.get_all_eids_between(0, 1).unwrap(), vec![0, 1]);
8411 // Reverse direction has no edges in directed graph.
8412 assert_eq!(g.get_all_eids_between(1, 0).unwrap(), Vec::<EdgeId>::new());
8413 }
8414
8415 #[test]
8416 fn get_eid_returns_lowest_id_for_parallel() {
8417 // Spec: with multiple edges, get_eid always returns the same
8418 // edge id (matches upstream's "ignored multi-edges" guarantee).
8419 // Our impl returns the lowest from the bucket.
8420 let mut g = Graph::with_vertices(2);
8421 g.add_edge(0, 1).unwrap(); // edge 0
8422 g.add_edge(0, 1).unwrap(); // edge 1
8423 assert_eq!(g.get_eid(0, 1).unwrap(), 0);
8424 }
8425
8426 // -------- ALGO-CORE-001c: delete_edges + delete_vertices --------
8427
8428 #[test]
8429 fn delete_edges_empty_input_is_noop() {
8430 let mut g = Graph::with_vertices(3);
8431 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8432 g.delete_edges(&[]).unwrap();
8433 assert_eq!(g.ecount(), 2);
8434 assert_eq!(g.degree(1).unwrap(), 2);
8435 }
8436
8437 #[test]
8438 fn delete_edges_single_edge_undirected() {
8439 let mut g = Graph::with_vertices(3);
8440 g.add_edges(vec![(0, 1), (1, 2), (0, 2)]).unwrap();
8441 // Remove edge id 1 (the (1,2) edge).
8442 g.delete_edges(&[1]).unwrap();
8443 assert_eq!(g.ecount(), 2);
8444 // Surviving edges renumbered to 0,1: (0,1) and (0,2).
8445 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
8446 assert_eq!(g.find_eid(0, 2).unwrap(), Some(1));
8447 assert_eq!(g.find_eid(1, 2).unwrap(), None);
8448 // Degrees consistent post-rebuild.
8449 assert_eq!(g.degree(1).unwrap(), 1);
8450 assert_eq!(g.degree(2).unwrap(), 1);
8451 }
8452
8453 #[test]
8454 fn delete_edges_duplicate_ids_tolerated() {
8455 let mut g = Graph::with_vertices(3);
8456 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8457 g.delete_edges(&[0, 0, 0]).unwrap();
8458 assert_eq!(g.ecount(), 1);
8459 assert_eq!(g.find_eid(1, 2).unwrap(), Some(0));
8460 }
8461
8462 #[test]
8463 fn delete_edges_all_edges_leaves_isolated_vertices() {
8464 let mut g = Graph::with_vertices(3);
8465 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8466 g.delete_edges(&[0, 1]).unwrap();
8467 assert_eq!(g.ecount(), 0);
8468 assert_eq!(g.vcount(), 3);
8469 for v in 0..3 {
8470 assert_eq!(g.degree(v).unwrap(), 0);
8471 }
8472 }
8473
8474 #[test]
8475 fn delete_edges_out_of_range_errors_and_preserves_state() {
8476 let mut g = Graph::with_vertices(3);
8477 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8478 let err = g.delete_edges(&[5]).unwrap_err();
8479 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 2 }));
8480 // Graph unchanged.
8481 assert_eq!(g.ecount(), 2);
8482 }
8483
8484 #[test]
8485 fn delete_edges_self_loop_directed() {
8486 let mut g = Graph::new(2, true).unwrap();
8487 g.add_edges(vec![(0, 0), (0, 1)]).unwrap();
8488 g.delete_edges(&[0]).unwrap(); // remove the self-loop
8489 assert_eq!(g.ecount(), 1);
8490 assert_eq!(g.degree(0).unwrap(), 1);
8491 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
8492 }
8493
8494 #[test]
8495 fn delete_vertices_empty_input_is_noop() {
8496 let mut g = Graph::with_vertices(3);
8497 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8498 g.delete_vertices(&[]).unwrap();
8499 assert_eq!(g.vcount(), 3);
8500 assert_eq!(g.ecount(), 2);
8501 }
8502
8503 #[test]
8504 fn delete_vertices_single_renumbers() {
8505 let mut g = Graph::with_vertices(4);
8506 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (0, 3)]).unwrap();
8507 // Remove vertex 1: edges (0,1) and (1,2) go with it. (2,3),(0,3)
8508 // survive but get renumbered: 2 → 1, 3 → 2.
8509 g.delete_vertices(&[1]).unwrap();
8510 assert_eq!(g.vcount(), 3);
8511 assert_eq!(g.ecount(), 2);
8512 // (2,3) → (1,2); (0,3) → (0,2).
8513 assert!(g.find_eid(1, 2).unwrap().is_some());
8514 assert!(g.find_eid(0, 2).unwrap().is_some());
8515 assert_eq!(g.find_eid(0, 1).unwrap(), None);
8516 }
8517
8518 #[test]
8519 fn delete_vertices_duplicate_ids_tolerated() {
8520 let mut g = Graph::with_vertices(3);
8521 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8522 g.delete_vertices(&[1, 1, 1]).unwrap();
8523 assert_eq!(g.vcount(), 2);
8524 assert_eq!(g.ecount(), 0);
8525 }
8526
8527 #[test]
8528 fn delete_vertices_all_yields_empty_graph() {
8529 let mut g = Graph::with_vertices(3);
8530 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8531 g.delete_vertices(&[0, 1, 2]).unwrap();
8532 assert_eq!(g.vcount(), 0);
8533 assert_eq!(g.ecount(), 0);
8534 }
8535
8536 #[test]
8537 fn delete_vertices_out_of_range_errors_and_preserves_state() {
8538 let mut g = Graph::with_vertices(3);
8539 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8540 let err = g.delete_vertices(&[5]).unwrap_err();
8541 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 3 }));
8542 assert_eq!(g.vcount(), 3);
8543 assert_eq!(g.ecount(), 2);
8544 }
8545
8546 #[test]
8547 fn delete_vertices_map_returns_correct_mappings() {
8548 let mut g = Graph::with_vertices(5);
8549 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (3, 4)]).unwrap();
8550 let (map, invmap) = g.delete_vertices_map(&[1, 3]).unwrap();
8551 // Removed: 1 and 3. Retained: 0 → 0, 2 → 1, 4 → 2.
8552 assert_eq!(map, vec![Some(0), None, Some(1), None, Some(2)]);
8553 assert_eq!(invmap, vec![0, 2, 4]);
8554 assert_eq!(g.vcount(), 3);
8555 // Only edges between retained vertices survive — none do here.
8556 assert_eq!(g.ecount(), 0);
8557 }
8558
8559 #[test]
8560 fn delete_vertices_directed_preserves_direction() {
8561 let mut g = Graph::new(4, true).unwrap();
8562 g.add_edges(vec![(0, 1), (1, 2), (2, 0), (3, 0)]).unwrap();
8563 g.delete_vertices(&[3]).unwrap();
8564 assert_eq!(g.vcount(), 3);
8565 assert!(g.is_directed());
8566 // Surviving directed edges (3 → 0) gone; (0,1),(1,2),(2,0) keep direction.
8567 assert!(g.get_eid(0, 1).is_ok());
8568 assert!(g.get_eid(1, 0).is_err()); // wrong direction
8569 }
8570
8571 #[test]
8572 fn delete_vertices_self_loop_on_removed_vertex() {
8573 let mut g = Graph::with_vertices(3);
8574 g.add_edges(vec![(0, 0), (0, 1), (1, 2)]).unwrap();
8575 g.delete_vertices(&[0]).unwrap();
8576 // Self-loop and edges to vertex 0 gone; only (1,2) → (0,1) survives.
8577 assert_eq!(g.vcount(), 2);
8578 assert_eq!(g.ecount(), 1);
8579 assert!(g.find_eid(0, 1).unwrap().is_some());
8580 }
8581
8582 #[test]
8583 fn delete_vertices_preserves_parallel_edges() {
8584 let mut g = Graph::with_vertices(3);
8585 g.add_edges(vec![(0, 1), (0, 1), (1, 2)]).unwrap();
8586 g.delete_vertices(&[2]).unwrap();
8587 assert_eq!(g.vcount(), 2);
8588 assert_eq!(g.ecount(), 2); // both parallel (0,1) edges retained
8589 assert_eq!(g.degree(0).unwrap(), 2);
8590 assert_eq!(g.degree(1).unwrap(), 2);
8591 }
8592
8593 #[test]
8594 fn add_edges_after_delete_works() {
8595 let mut g = Graph::with_vertices(4);
8596 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
8597 g.delete_vertices(&[0]).unwrap(); // now n=3, vertices 0,1,2
8598 // Add a new edge and check indexes still work.
8599 g.add_edge(0, 2).unwrap();
8600 assert_eq!(g.ecount(), 3);
8601 assert_eq!(g.degree(0).unwrap(), 2); // (0,1)+(0,2)
8602 assert!(g.find_eid(0, 2).unwrap().is_some());
8603 }
8604
8605 #[test]
8606 fn from_adjacency_matrix_undirected_triangle() {
8607 let adj = vec![
8608 vec![0.0, 1.0, 1.0],
8609 vec![1.0, 0.0, 1.0],
8610 vec![1.0, 1.0, 0.0],
8611 ];
8612 let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
8613 assert_eq!(g.vcount(), 3);
8614 assert_eq!(g.ecount(), 3);
8615 assert!(!g.is_directed());
8616 }
8617
8618 #[test]
8619 fn from_adjacency_matrix_directed() {
8620 let adj = vec![
8621 vec![0.0, 1.0, 0.0],
8622 vec![0.0, 0.0, 1.0],
8623 vec![1.0, 0.0, 0.0],
8624 ];
8625 let g = Graph::from_adjacency_matrix(&adj, true).unwrap();
8626 assert_eq!(g.vcount(), 3);
8627 assert_eq!(g.ecount(), 3);
8628 assert!(g.is_directed());
8629 }
8630
8631 #[test]
8632 fn from_adjacency_matrix_with_self_loop() {
8633 let adj = vec![vec![1.0, 1.0], vec![1.0, 0.0]];
8634 let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
8635 assert_eq!(g.vcount(), 2);
8636 assert_eq!(g.ecount(), 2); // self-loop on 0 + edge 0-1
8637 }
8638
8639 #[test]
8640 fn from_adjacency_matrix_empty() {
8641 let adj: Vec<Vec<f64>> = Vec::new();
8642 let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
8643 assert_eq!(g.vcount(), 0);
8644 assert_eq!(g.ecount(), 0);
8645 }
8646
8647 #[test]
8648 fn from_adjacency_matrix_non_square_error() {
8649 let adj = vec![vec![0.0, 1.0], vec![1.0, 0.0, 1.0]];
8650 assert!(Graph::from_adjacency_matrix(&adj, false).is_err());
8651 }
8652
8653 #[test]
8654 fn from_adjacency_matrix_weighted_basic() {
8655 let adj = vec![
8656 vec![0.0, 2.5, 0.0],
8657 vec![2.5, 0.0, 1.0],
8658 vec![0.0, 1.0, 0.0],
8659 ];
8660 let (g, weights) = Graph::from_adjacency_matrix_weighted(&adj, false).unwrap();
8661 assert_eq!(g.vcount(), 3);
8662 assert_eq!(g.ecount(), 2);
8663 assert_eq!(weights.len(), 2);
8664 assert!((weights[0] - 2.5).abs() < 1e-10);
8665 assert!((weights[1] - 1.0).abs() < 1e-10);
8666 }
8667
8668 #[test]
8669 fn from_adjacency_matrix_weighted_directed() {
8670 let adj = vec![
8671 vec![0.0, 3.0, 0.0],
8672 vec![0.0, 0.0, 2.0],
8673 vec![1.5, 0.0, 0.0],
8674 ];
8675 let (g, weights) = Graph::from_adjacency_matrix_weighted(&adj, true).unwrap();
8676 assert_eq!(g.vcount(), 3);
8677 assert_eq!(g.ecount(), 3);
8678 assert_eq!(weights.len(), 3);
8679 assert!((weights[0] - 3.0).abs() < 1e-10);
8680 assert!((weights[1] - 2.0).abs() < 1e-10);
8681 assert!((weights[2] - 1.5).abs() < 1e-10);
8682 }
8683
8684 #[test]
8685 fn from_adjacency_matrix_multi_edges() {
8686 let adj = vec![vec![0.0, 3.0], vec![3.0, 0.0]];
8687 let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
8688 assert_eq!(g.vcount(), 2);
8689 assert_eq!(g.ecount(), 3); // 3 parallel edges
8690 }
8691
8692 #[test]
8693 fn from_adjacency_list_undirected_triangle() {
8694 let adj = vec![vec![1, 2], vec![0, 2], vec![0, 1]];
8695 let g = Graph::from_adjacency_list(&adj, false).unwrap();
8696 assert_eq!(g.vcount(), 3);
8697 assert_eq!(g.ecount(), 3);
8698 assert!(!g.is_directed());
8699 }
8700
8701 #[test]
8702 fn from_adjacency_list_directed() {
8703 let adj = vec![vec![1, 2], vec![2], vec![]];
8704 let g = Graph::from_adjacency_list(&adj, true).unwrap();
8705 assert_eq!(g.vcount(), 3);
8706 assert_eq!(g.ecount(), 3);
8707 assert!(g.is_directed());
8708 }
8709
8710 #[test]
8711 fn from_adjacency_list_empty() {
8712 let adj: Vec<Vec<u32>> = Vec::new();
8713 let g = Graph::from_adjacency_list(&adj, false).unwrap();
8714 assert_eq!(g.vcount(), 0);
8715 assert_eq!(g.ecount(), 0);
8716 }
8717
8718 #[test]
8719 fn from_adjacency_list_isolated_vertices() {
8720 let adj = vec![vec![], vec![], vec![]];
8721 let g = Graph::from_adjacency_list(&adj, false).unwrap();
8722 assert_eq!(g.vcount(), 3);
8723 assert_eq!(g.ecount(), 0);
8724 }
8725
8726 #[test]
8727 fn from_adjacency_list_self_loop() {
8728 let adj = vec![vec![0, 1], vec![0]];
8729 let g = Graph::from_adjacency_list(&adj, false).unwrap();
8730 assert_eq!(g.vcount(), 2);
8731 assert_eq!(g.ecount(), 2); // self-loop on 0 + edge 0-1
8732 }
8733
8734 #[test]
8735 fn from_adjacency_list_out_of_range_error() {
8736 let adj = vec![vec![5]]; // only 1 vertex but references vertex 5
8737 assert!(Graph::from_adjacency_list(&adj, false).is_err());
8738 }
8739
8740 #[test]
8741 fn neighbors_iter_matches_neighbors_undirected() {
8742 let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 3), (2, 3), (3, 4)], false, None).unwrap();
8743 for v in 0..g.vcount() {
8744 let from_vec = g.neighbors(v).unwrap();
8745 let from_iter: Vec<VertexId> = g.neighbors_iter(v).unwrap().collect();
8746 assert_eq!(from_vec, from_iter, "mismatch at vertex {v}");
8747 }
8748 }
8749
8750 #[test]
8751 fn neighbors_iter_matches_neighbors_directed() {
8752 let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 3), (2, 3), (3, 4)], true, None).unwrap();
8753 for v in 0..g.vcount() {
8754 let from_vec = g.neighbors(v).unwrap();
8755 let from_iter: Vec<VertexId> = g.neighbors_iter(v).unwrap().collect();
8756 assert_eq!(from_vec, from_iter, "mismatch at vertex {v}");
8757 }
8758 }
8759
8760 #[test]
8761 fn neighbors_iter_exact_size() {
8762 let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], false, None).unwrap();
8763 let iter = g.neighbors_iter(0).unwrap();
8764 assert_eq!(iter.len(), 3);
8765 }
8766
8767 #[test]
8768 fn neighbors_iter_invalid_vertex() {
8769 let g = Graph::with_vertices(3);
8770 assert!(g.neighbors_iter(5).is_err());
8771 }
8772}