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 /// Second-order biased random walk (`Node2Vec`) from `start`.
3501 ///
3502 /// `p` controls the likelihood of returning to the previous vertex;
3503 /// `q` controls exploration vs. exploitation (BFS-like vs DFS-like).
3504 /// When `p = q = 1.0` this is equivalent to a standard random walk.
3505 ///
3506 /// ```
3507 /// use rust_igraph::Graph;
3508 ///
3509 /// let g = Graph::from_edges(
3510 /// &[(0,1), (1,2), (2,3), (3,0), (0,2)], false, None
3511 /// ).unwrap();
3512 /// let (vertices, _edges) = g.random_walk_node2vec(0, 10, 2.0, 0.5, 42).unwrap();
3513 /// assert_eq!(vertices[0], 0);
3514 /// ```
3515 #[allow(clippy::too_many_arguments)]
3516 pub fn random_walk_node2vec(
3517 &self,
3518 start: VertexId,
3519 steps: u32,
3520 p: f64,
3521 q: f64,
3522 seed: u64,
3523 ) -> IgraphResult<(Vec<VertexId>, Vec<EdgeId>)> {
3524 use crate::algorithms::paths::dijkstra::DijkstraMode;
3525 let mode = if self.directed {
3526 DijkstraMode::Out
3527 } else {
3528 DijkstraMode::All
3529 };
3530 crate::algorithms::paths::random_walk_node2vec::random_walk_node2vec(
3531 self, None, start, mode, steps, p, q, seed,
3532 )
3533 }
3534
3535 // ── Graph properties ─────────────────────────────────────────────
3536
3537 /// Compute the radius (minimum eccentricity) of the graph.
3538 ///
3539 /// Returns `None` for the empty graph.
3540 ///
3541 /// # Examples
3542 ///
3543 /// ```
3544 /// use rust_igraph::Graph;
3545 ///
3546 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3547 /// assert_eq!(g.radius().unwrap(), Some(2));
3548 /// ```
3549 pub fn radius(&self) -> IgraphResult<Option<u32>> {
3550 crate::algorithms::paths::radii::radius(self)
3551 }
3552
3553 /// Compute the eccentricity of every vertex.
3554 ///
3555 /// `result[v]` is the maximum shortest-path distance from vertex `v`.
3556 ///
3557 /// # Examples
3558 ///
3559 /// ```
3560 /// use rust_igraph::Graph;
3561 ///
3562 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3563 /// assert_eq!(g.eccentricity().unwrap(), vec![2, 1, 2]);
3564 /// ```
3565 pub fn eccentricity(&self) -> IgraphResult<Vec<u32>> {
3566 crate::algorithms::paths::radii::eccentricity(self)
3567 }
3568
3569 /// Compute the girth (length of the shortest cycle) of the graph.
3570 ///
3571 /// Returns `None` if the graph is acyclic.
3572 ///
3573 /// # Examples
3574 ///
3575 /// ```
3576 /// use rust_igraph::Graph;
3577 ///
3578 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3579 /// assert_eq!(g.girth().unwrap(), Some(3));
3580 /// ```
3581 pub fn girth(&self) -> IgraphResult<Option<u32>> {
3582 crate::algorithms::properties::girth::girth(self)
3583 }
3584
3585 /// Check whether the graph is a tree.
3586 ///
3587 /// Returns `Some(root)` where `root` is the first root vertex found,
3588 /// or `None` if the graph is not a tree. The `mode` parameter controls
3589 /// how edges are followed for directed graphs.
3590 ///
3591 /// # Examples
3592 ///
3593 /// ```
3594 /// use rust_igraph::{Graph, DijkstraMode};
3595 ///
3596 /// let g = Graph::from_edges(&[(0,1), (1,2), (1,3)], false, None).unwrap();
3597 /// assert!(g.is_tree(DijkstraMode::All).unwrap().is_some());
3598 /// ```
3599 pub fn is_tree(
3600 &self,
3601 mode: crate::algorithms::paths::dijkstra::DijkstraMode,
3602 ) -> IgraphResult<Option<VertexId>> {
3603 crate::algorithms::properties::is_tree::is_tree(self, mode)
3604 }
3605
3606 /// Check whether the directed graph is a DAG.
3607 ///
3608 /// Returns `false` for undirected graphs.
3609 ///
3610 /// # Examples
3611 ///
3612 /// ```
3613 /// use rust_igraph::Graph;
3614 ///
3615 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
3616 /// assert!(g.is_dag());
3617 /// ```
3618 pub fn is_dag(&self) -> bool {
3619 crate::algorithms::properties::is_dag::is_dag(self)
3620 }
3621
3622 /// Count the total number of triangles in the graph.
3623 ///
3624 /// # Examples
3625 ///
3626 /// ```
3627 /// use rust_igraph::Graph;
3628 ///
3629 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3630 /// assert_eq!(g.count_triangles().unwrap(), 1);
3631 /// ```
3632 pub fn count_triangles(&self) -> IgraphResult<u64> {
3633 crate::algorithms::properties::triangles::count_triangles(self)
3634 }
3635
3636 /// Compute the harmonic centrality of all vertices.
3637 ///
3638 /// Harmonic centrality of `v` is the sum of inverse distances
3639 /// from `v` to all other reachable vertices.
3640 ///
3641 /// # Examples
3642 ///
3643 /// ```
3644 /// use rust_igraph::Graph;
3645 ///
3646 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3647 /// let h = g.harmonic_centrality().unwrap();
3648 /// assert_eq!(h.len(), 3);
3649 /// ```
3650 pub fn harmonic_centrality(&self) -> IgraphResult<Vec<f64>> {
3651 crate::algorithms::properties::harmonic::harmonic_centrality(self)
3652 }
3653
3654 /// Compute the k-hop neighborhood size for every vertex.
3655 ///
3656 /// `result[v]` is the number of vertices within distance `order` from
3657 /// `v` (including `v` itself).
3658 ///
3659 /// # Examples
3660 ///
3661 /// ```
3662 /// use rust_igraph::Graph;
3663 ///
3664 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3665 /// let sizes = g.neighborhood_size(1).unwrap();
3666 /// assert_eq!(sizes, vec![2, 3, 3, 2]);
3667 /// ```
3668 pub fn neighborhood_size(&self, order: i32) -> IgraphResult<Vec<u32>> {
3669 crate::algorithms::properties::neighborhood::neighborhood_size(self, order)
3670 }
3671
3672 // ── Connectivity ─────────────────────────────────────────────────
3673
3674 /// Compute the vertex connectivity (minimum vertex cut) of the graph.
3675 ///
3676 /// # Examples
3677 ///
3678 /// ```
3679 /// use rust_igraph::Graph;
3680 ///
3681 /// let g = Graph::from_edges(
3682 /// &[(0,1), (0,2), (1,3), (2,3)], false, None,
3683 /// ).unwrap();
3684 /// assert_eq!(g.vertex_connectivity().unwrap(), 2);
3685 /// ```
3686 pub fn vertex_connectivity(&self) -> IgraphResult<i64> {
3687 crate::algorithms::flow::vertex_connectivity::vertex_connectivity(self, true)
3688 }
3689
3690 /// Compute the edge connectivity (minimum edge cut) of the graph.
3691 ///
3692 /// # Examples
3693 ///
3694 /// ```
3695 /// use rust_igraph::Graph;
3696 ///
3697 /// let g = Graph::from_edges(
3698 /// &[(0,1), (0,2), (1,3), (2,3)], false, None,
3699 /// ).unwrap();
3700 /// assert_eq!(g.edge_connectivity().unwrap(), 2);
3701 /// ```
3702 pub fn edge_connectivity(&self) -> IgraphResult<i64> {
3703 crate::algorithms::flow::edge_connectivity::edge_connectivity(self, true)
3704 }
3705
3706 /// Find all vertices reachable from `source`.
3707 ///
3708 /// # Examples
3709 ///
3710 /// ```
3711 /// use rust_igraph::{Graph, SubcomponentMode};
3712 ///
3713 /// let g = Graph::from_edges(&[(0,1), (1,2), (3,4)], false, None).unwrap();
3714 /// let comp = g.subcomponent(0, SubcomponentMode::All).unwrap();
3715 /// assert_eq!(comp.len(), 3);
3716 /// ```
3717 pub fn subcomponent(
3718 &self,
3719 source: VertexId,
3720 mode: crate::algorithms::connectivity::subcomponent::SubcomponentMode,
3721 ) -> IgraphResult<Vec<VertexId>> {
3722 crate::algorithms::connectivity::subcomponent::subcomponent(self, source, mode)
3723 }
3724
3725 // ── Cliques ──────────────────────────────────────────────────────
3726
3727 /// Find all cliques in the graph within a size range.
3728 ///
3729 /// # Examples
3730 ///
3731 /// ```
3732 /// use rust_igraph::Graph;
3733 ///
3734 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3735 /// let c = g.cliques(3, 3, None).unwrap();
3736 /// assert_eq!(c.len(), 1);
3737 /// ```
3738 pub fn cliques(
3739 &self,
3740 min_size: u32,
3741 max_size: u32,
3742 max_results: Option<usize>,
3743 ) -> IgraphResult<Vec<Vec<VertexId>>> {
3744 crate::algorithms::cliques::cliques(self, min_size, max_size, max_results)
3745 }
3746
3747 /// Find all maximal cliques in the graph.
3748 ///
3749 /// # Examples
3750 ///
3751 /// ```
3752 /// use rust_igraph::Graph;
3753 ///
3754 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3755 /// let mc = g.maximal_cliques().unwrap();
3756 /// assert_eq!(mc.len(), 1);
3757 /// assert_eq!(mc[0].len(), 3);
3758 /// ```
3759 pub fn maximal_cliques(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
3760 crate::algorithms::cliques::maximal_cliques(self)
3761 }
3762
3763 /// Compute the independence number (max independent set size).
3764 ///
3765 /// # Examples
3766 ///
3767 /// ```
3768 /// use rust_igraph::Graph;
3769 ///
3770 /// // Triangle: independence number is 1 (can pick at most 1 non-adjacent vertex pair... no, 1)
3771 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3772 /// assert_eq!(g.independence_number().unwrap(), 1);
3773 /// ```
3774 pub fn independence_number(&self) -> IgraphResult<u32> {
3775 crate::algorithms::cliques::independence_number(self)
3776 }
3777
3778 // ── Operators ────────────────────────────────────────────────────
3779
3780 /// Permute the vertices of the graph.
3781 ///
3782 /// `permutation[v]` gives the new id for vertex `v`. Returns a new
3783 /// graph with edges reconnected accordingly.
3784 ///
3785 /// # Examples
3786 ///
3787 /// ```
3788 /// use rust_igraph::Graph;
3789 ///
3790 /// // permutation[new] = old: new 0 ← old 2, new 1 ← old 0, new 2 ← old 1
3791 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
3792 /// let p = g.permute_vertices(&[2, 0, 1]).unwrap();
3793 /// assert!(p.has_edge(1, 2));
3794 /// assert!(p.has_edge(2, 0));
3795 /// ```
3796 pub fn permute_vertices(&self, permutation: &[VertexId]) -> IgraphResult<Graph> {
3797 crate::algorithms::operators::permute_vertices::permute_vertices(self, permutation)
3798 }
3799
3800 // ── Layout ───────────────────────────────────────────────────────
3801
3802 /// Fruchterman-Reingold force-directed layout with default parameters.
3803 ///
3804 /// # Examples
3805 ///
3806 /// ```
3807 /// use rust_igraph::Graph;
3808 ///
3809 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3810 /// let coords = g.layout_fruchterman_reingold().unwrap();
3811 /// assert_eq!(coords.len(), 3);
3812 /// ```
3813 pub fn layout_fruchterman_reingold(&self) -> IgraphResult<Vec<(f64, f64)>> {
3814 use crate::algorithms::layout::fruchterman_reingold::FrParams;
3815 crate::algorithms::layout::fruchterman_reingold::layout_fruchterman_reingold(
3816 self,
3817 &FrParams::default(),
3818 )
3819 }
3820
3821 /// Kamada-Kawai spring-embedder layout with default parameters.
3822 ///
3823 /// # Examples
3824 ///
3825 /// ```
3826 /// use rust_igraph::Graph;
3827 ///
3828 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
3829 /// let coords = g.layout_kamada_kawai().unwrap();
3830 /// assert_eq!(coords.len(), 4);
3831 /// ```
3832 pub fn layout_kamada_kawai(&self) -> IgraphResult<Vec<[f64; 2]>> {
3833 use crate::algorithms::layout::kamada_kawai::KkParams;
3834 let params = KkParams::default_for(self.vcount() as usize);
3835 crate::algorithms::layout::kamada_kawai::layout_kamada_kawai(self, None, ¶ms, None)
3836 }
3837
3838 /// `DrL` (Distributed Recursive Layout) with default options.
3839 ///
3840 /// # Examples
3841 ///
3842 /// ```
3843 /// use rust_igraph::Graph;
3844 ///
3845 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3846 /// let coords = g.layout_drl().unwrap();
3847 /// assert_eq!(coords.len(), 3);
3848 /// ```
3849 pub fn layout_drl(&self) -> IgraphResult<Vec<[f64; 2]>> {
3850 use crate::algorithms::layout::drl::DrlOptions;
3851 crate::algorithms::layout::drl::layout_drl(self, None, &DrlOptions::default(), None)
3852 }
3853
3854 /// Circular layout.
3855 ///
3856 /// # Examples
3857 ///
3858 /// ```
3859 /// use rust_igraph::Graph;
3860 ///
3861 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
3862 /// let coords = g.layout_circle();
3863 /// assert_eq!(coords.len(), 3);
3864 /// ```
3865 pub fn layout_circle(&self) -> Vec<(f64, f64)> {
3866 crate::algorithms::layout::simple::layout_circle(self, None)
3867 }
3868
3869 /// Random layout with the given RNG seed.
3870 ///
3871 /// # Examples
3872 ///
3873 /// ```
3874 /// use rust_igraph::Graph;
3875 ///
3876 /// let g = Graph::with_vertices(5);
3877 /// let coords = g.layout_random(42);
3878 /// assert_eq!(coords.len(), 5);
3879 /// ```
3880 pub fn layout_random(&self, seed: u64) -> Vec<(f64, f64)> {
3881 crate::algorithms::layout::simple::layout_random(self, seed)
3882 }
3883
3884 /// Grid layout.
3885 ///
3886 /// `width` specifies the number of columns. Pass 0 to auto-compute
3887 /// (ceil of square root of vertex count).
3888 ///
3889 /// # Examples
3890 ///
3891 /// ```
3892 /// use rust_igraph::Graph;
3893 ///
3894 /// let g = Graph::with_vertices(9);
3895 /// let coords = g.layout_grid(3);
3896 /// assert_eq!(coords.len(), 9);
3897 /// ```
3898 pub fn layout_grid(&self, width: i32) -> Vec<(f64, f64)> {
3899 crate::algorithms::layout::simple::layout_grid(self, width)
3900 }
3901
3902 // ── Triangle / local clustering ──────────────────────────────────
3903
3904 /// Per-vertex triangle count.
3905 ///
3906 /// `result[v]` is the number of triangles incident to vertex `v`.
3907 ///
3908 /// # Examples
3909 ///
3910 /// ```
3911 /// use rust_igraph::Graph;
3912 ///
3913 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0),(2,3)], false, None).unwrap();
3914 /// let t = g.count_adjacent_triangles().unwrap();
3915 /// assert_eq!(t[0], 1);
3916 /// assert_eq!(t[3], 0);
3917 /// ```
3918 pub fn count_adjacent_triangles(&self) -> IgraphResult<Vec<u64>> {
3919 crate::algorithms::properties::triangles::count_adjacent_triangles(self)
3920 }
3921
3922 /// Per-vertex local clustering coefficient (local transitivity).
3923 ///
3924 /// `result[v]` is `None` for vertices with degree < 2.
3925 ///
3926 /// # Examples
3927 ///
3928 /// ```
3929 /// use rust_igraph::Graph;
3930 ///
3931 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0),(2,3)], false, None).unwrap();
3932 /// let lcc = g.transitivity_local_undirected().unwrap();
3933 /// assert!(lcc[0].unwrap() > 0.9); // vertex 0 in triangle
3934 /// assert!(lcc[3].is_none()); // degree 1
3935 /// ```
3936 pub fn transitivity_local_undirected(&self) -> IgraphResult<Vec<Option<f64>>> {
3937 crate::algorithms::properties::triangles::transitivity_local_undirected(self)
3938 }
3939
3940 // ── Network metrics ──────────────────────────────────────────────
3941
3942 /// Reciprocity of a directed graph.
3943 ///
3944 /// Returns the fraction of edges that are reciprocated, or `None` for
3945 /// empty graphs.
3946 ///
3947 /// # Examples
3948 ///
3949 /// ```
3950 /// use rust_igraph::Graph;
3951 ///
3952 /// let g = Graph::from_edges(&[(0,1),(1,0),(1,2)], true, None).unwrap();
3953 /// let r = g.reciprocity().unwrap().unwrap();
3954 /// assert!((r - 2.0/3.0).abs() < 1e-12);
3955 /// ```
3956 pub fn reciprocity(&self) -> IgraphResult<Option<f64>> {
3957 crate::algorithms::properties::reciprocity::reciprocity(self)
3958 }
3959
3960 /// Burt's constraint for each vertex.
3961 ///
3962 /// # Examples
3963 ///
3964 /// ```
3965 /// use rust_igraph::Graph;
3966 ///
3967 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,2)], false, None).unwrap();
3968 /// let c = g.constraint(None).unwrap();
3969 /// assert_eq!(c.len(), 3);
3970 /// ```
3971 pub fn constraint(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
3972 crate::algorithms::properties::constraint::constraint(self, weights)
3973 }
3974
3975 /// Whether the graph has multi-edges.
3976 ///
3977 /// # Examples
3978 ///
3979 /// ```
3980 /// use rust_igraph::Graph;
3981 ///
3982 /// let g = Graph::from_edges(&[(0,1),(0,1)], false, None).unwrap();
3983 /// assert!(g.has_multiple().unwrap());
3984 /// ```
3985 pub fn has_multiple(&self) -> IgraphResult<bool> {
3986 crate::algorithms::properties::multiplicity::has_multiple(self)
3987 }
3988
3989 /// Per-edge multiplicity count.
3990 ///
3991 /// # Examples
3992 ///
3993 /// ```
3994 /// use rust_igraph::Graph;
3995 ///
3996 /// let g = Graph::from_edges(&[(0,1),(0,1),(1,2)], false, None).unwrap();
3997 /// let mc = g.count_multiple().unwrap();
3998 /// assert_eq!(mc[0], 2);
3999 /// assert_eq!(mc[2], 1);
4000 /// ```
4001 pub fn count_multiple(&self) -> IgraphResult<Vec<usize>> {
4002 crate::algorithms::properties::multiplicity::count_multiple(self)
4003 }
4004
4005 /// Test whether two vertices are adjacent.
4006 ///
4007 /// # Examples
4008 ///
4009 /// ```
4010 /// use rust_igraph::Graph;
4011 ///
4012 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
4013 /// assert!(g.are_adjacent(0, 1).unwrap());
4014 /// assert!(!g.are_adjacent(0, 2).unwrap());
4015 /// ```
4016 pub fn are_adjacent(&self, v1: VertexId, v2: VertexId) -> IgraphResult<bool> {
4017 crate::algorithms::properties::are_adjacent::are_adjacent(self, v1, v2)
4018 }
4019
4020 // ── Motifs ───────────────────────────────────────────────────────
4021
4022 /// Triad census of a directed graph.
4023 ///
4024 /// # Examples
4025 ///
4026 /// ```
4027 /// use rust_igraph::{Graph, TriadType};
4028 ///
4029 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], true, None).unwrap();
4030 /// let tc = g.triad_census().unwrap();
4031 /// assert!(tc.get(TriadType::T030C) > 0.0);
4032 /// ```
4033 pub fn triad_census(
4034 &self,
4035 ) -> IgraphResult<crate::algorithms::motifs::triad_census::TriadCensus> {
4036 crate::algorithms::motifs::triad_census::triad_census(self)
4037 }
4038
4039 /// Dyad census of a directed graph.
4040 ///
4041 /// # Examples
4042 ///
4043 /// ```
4044 /// use rust_igraph::Graph;
4045 ///
4046 /// let g = Graph::from_edges(&[(0,1),(1,0),(1,2)], true, None).unwrap();
4047 /// let dc = g.dyad_census().unwrap();
4048 /// assert!((dc.mutual - 1.0).abs() < 1e-12);
4049 /// ```
4050 pub fn dyad_census(&self) -> IgraphResult<crate::algorithms::motifs::DyadCensus> {
4051 crate::algorithms::motifs::dyad_census(self)
4052 }
4053
4054 // ── Similarity ───────────────────────────────────────────────────
4055
4056 /// Jaccard similarity between all pairs of vertices.
4057 ///
4058 /// Returns a flattened `n × n` matrix (row-major).
4059 ///
4060 /// # Examples
4061 ///
4062 /// ```
4063 /// use rust_igraph::Graph;
4064 ///
4065 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,2)], false, None).unwrap();
4066 /// let sim = g.similarity_jaccard().unwrap();
4067 /// assert_eq!(sim.len(), 9); // 3×3 matrix
4068 /// ```
4069 pub fn similarity_jaccard(&self) -> IgraphResult<Vec<f64>> {
4070 crate::algorithms::properties::similarity::similarity_jaccard(self)
4071 }
4072
4073 /// Co-citation scores for all vertex pairs.
4074 ///
4075 /// # Examples
4076 ///
4077 /// ```
4078 /// use rust_igraph::Graph;
4079 ///
4080 /// let g = Graph::from_edges(&[(0,2),(1,2)], true, None).unwrap();
4081 /// let cc = g.cocitation().unwrap();
4082 /// assert!(!cc.is_empty());
4083 /// ```
4084 pub fn cocitation(&self) -> IgraphResult<Vec<u32>> {
4085 crate::algorithms::properties::similarity::cocitation(self)
4086 }
4087
4088 // ── Graph structure recognizers ─────────────────────────────────
4089
4090 /// Check whether the graph is a cograph.
4091 ///
4092 /// # Examples
4093 ///
4094 /// ```
4095 /// use rust_igraph::Graph;
4096 ///
4097 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
4098 /// assert!(g.is_cograph().unwrap());
4099 /// ```
4100 pub fn is_cograph(&self) -> IgraphResult<bool> {
4101 crate::algorithms::properties::is_cograph::is_cograph(self)
4102 }
4103
4104 /// Check whether the graph is series-parallel.
4105 ///
4106 /// # Examples
4107 ///
4108 /// ```
4109 /// use rust_igraph::Graph;
4110 ///
4111 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4112 /// assert!(g.is_series_parallel().unwrap());
4113 /// ```
4114 pub fn is_series_parallel(&self) -> IgraphResult<bool> {
4115 crate::algorithms::properties::is_series_parallel::is_series_parallel(self)
4116 }
4117
4118 /// Check whether the graph is outerplanar.
4119 ///
4120 /// # Examples
4121 ///
4122 /// ```
4123 /// use rust_igraph::Graph;
4124 ///
4125 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4126 /// assert!(g.is_outerplanar().unwrap());
4127 /// ```
4128 pub fn is_outerplanar(&self) -> IgraphResult<bool> {
4129 crate::algorithms::properties::is_outerplanar::is_outerplanar(self)
4130 }
4131
4132 /// Check whether the graph is chordal.
4133 ///
4134 /// # Examples
4135 ///
4136 /// ```
4137 /// use rust_igraph::Graph;
4138 ///
4139 /// let g = Graph::from_edges(
4140 /// &[(0,1), (1,2), (2,0), (0,3), (1,3), (2,3)], false, None
4141 /// ).unwrap();
4142 /// assert!(g.is_chordal().unwrap());
4143 /// ```
4144 pub fn is_chordal(&self) -> IgraphResult<bool> {
4145 let result = crate::algorithms::chordality::is_chordal(self, None)?;
4146 Ok(result.chordal)
4147 }
4148
4149 /// Check whether the graph is a forest (acyclic).
4150 ///
4151 /// # Examples
4152 ///
4153 /// ```
4154 /// use rust_igraph::Graph;
4155 ///
4156 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4157 /// assert!(g.is_forest().unwrap());
4158 /// ```
4159 pub fn is_forest(&self) -> IgraphResult<bool> {
4160 Ok(crate::algorithms::properties::is_forest::is_forest(
4161 self,
4162 crate::algorithms::paths::dijkstra::DijkstraMode::Out,
4163 )?
4164 .is_some())
4165 }
4166
4167 // ── Cycles and motifs ───────────────────────────────────────────
4168
4169 /// Compute a fundamental cycle basis of the graph.
4170 ///
4171 /// # Examples
4172 ///
4173 /// ```
4174 /// use rust_igraph::Graph;
4175 ///
4176 /// let g = Graph::from_edges(
4177 /// &[(0,1), (1,2), (2,0)], false, None
4178 /// ).unwrap();
4179 /// let cycles = g.fundamental_cycles().unwrap();
4180 /// assert_eq!(cycles.len(), 1);
4181 /// ```
4182 pub fn fundamental_cycles(&self) -> IgraphResult<Vec<Vec<u32>>> {
4183 crate::algorithms::fundamental_cycles::fundamental_cycles(self, None, None)
4184 }
4185
4186 /// Compute a minimum weight cycle basis.
4187 ///
4188 /// # Examples
4189 ///
4190 /// ```
4191 /// use rust_igraph::Graph;
4192 ///
4193 /// let g = Graph::from_edges(
4194 /// &[(0,1), (1,2), (2,0), (1,3), (3,0)], false, None
4195 /// ).unwrap();
4196 /// let basis = g.minimum_cycle_basis().unwrap();
4197 /// assert_eq!(basis.len(), 2);
4198 /// ```
4199 pub fn minimum_cycle_basis(&self) -> IgraphResult<Vec<Vec<u32>>> {
4200 crate::algorithms::minimum_cycle_basis::minimum_cycle_basis(self, None, false)
4201 }
4202
4203 // ── Cuts, covers, and sets ──────────────────────────────────────
4204
4205 /// Find a minimum feedback arc set.
4206 ///
4207 /// Returns edges whose removal makes the graph acyclic.
4208 ///
4209 /// # Examples
4210 ///
4211 /// ```
4212 /// use rust_igraph::Graph;
4213 ///
4214 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], true, None).unwrap();
4215 /// let fas = g.feedback_arc_set().unwrap();
4216 /// assert!(!fas.is_empty());
4217 /// ```
4218 pub fn feedback_arc_set(&self) -> IgraphResult<Vec<u32>> {
4219 crate::algorithms::feedback_arc_set::feedback_arc_set(
4220 self,
4221 None,
4222 crate::algorithms::feedback_arc_set::FasAlgorithm::EadesLinSmyth,
4223 )
4224 }
4225
4226 /// Find the maximum cut of the graph.
4227 ///
4228 /// # Examples
4229 ///
4230 /// ```
4231 /// use rust_igraph::Graph;
4232 ///
4233 /// let g = Graph::from_edges(
4234 /// &[(0,1), (1,2), (2,3)], false, None
4235 /// ).unwrap();
4236 /// let result = g.maximum_cut().unwrap();
4237 /// assert!(result.cut_value > 0);
4238 /// ```
4239 pub fn maximum_cut(&self) -> IgraphResult<crate::algorithms::max_cut::MaxCutResult> {
4240 crate::algorithms::max_cut::maximum_cut(self)
4241 }
4242
4243 /// Find a minimum vertex cover.
4244 ///
4245 /// # Examples
4246 ///
4247 /// ```
4248 /// use rust_igraph::Graph;
4249 ///
4250 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4251 /// let cover = g.minimum_vertex_cover().unwrap();
4252 /// assert!(cover.contains(&1));
4253 /// ```
4254 pub fn minimum_vertex_cover(&self) -> IgraphResult<Vec<u32>> {
4255 crate::algorithms::vertex_cover::minimum_vertex_cover(self)
4256 }
4257
4258 /// Find a minimum edge cover.
4259 ///
4260 /// # Examples
4261 ///
4262 /// ```
4263 /// use rust_igraph::Graph;
4264 ///
4265 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4266 /// let cover = g.minimum_edge_cover().unwrap();
4267 /// assert!(!cover.is_empty());
4268 /// ```
4269 pub fn minimum_edge_cover(&self) -> IgraphResult<Vec<u32>> {
4270 crate::algorithms::edge_cover::minimum_edge_cover(self)
4271 }
4272
4273 /// Find a maximum independent set.
4274 ///
4275 /// # Examples
4276 ///
4277 /// ```
4278 /// use rust_igraph::Graph;
4279 ///
4280 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4281 /// let mis = g.maximum_independent_set().unwrap();
4282 /// assert_eq!(mis.len(), 2);
4283 /// ```
4284 pub fn maximum_independent_set(&self) -> IgraphResult<Vec<u32>> {
4285 crate::algorithms::independent_set::maximum_independent_set(self)
4286 }
4287
4288 // ── Coloring ────────────────────────────────────────────────────
4289
4290 /// Greedy vertex coloring.
4291 ///
4292 /// # Examples
4293 ///
4294 /// ```
4295 /// use rust_igraph::Graph;
4296 ///
4297 /// let g = Graph::from_edges(
4298 /// &[(0,1), (1,2), (2,0)], false, None
4299 /// ).unwrap();
4300 /// let colors = g.vertex_coloring().unwrap();
4301 /// assert_eq!(colors.len(), 3);
4302 /// // Adjacent vertices must have different colors
4303 /// assert_ne!(colors[0], colors[1]);
4304 /// ```
4305 pub fn vertex_coloring(&self) -> IgraphResult<Vec<u32>> {
4306 crate::algorithms::coloring::vertex_coloring_greedy(
4307 self,
4308 crate::algorithms::coloring::GreedyColoringHeuristic::ColoredNeighbors,
4309 )
4310 }
4311
4312 // ── Spanning trees ──────────────────────────────────────────────
4313
4314 /// Sample a random spanning tree.
4315 ///
4316 /// # Examples
4317 ///
4318 /// ```
4319 /// use rust_igraph::Graph;
4320 ///
4321 /// let g = Graph::from_edges(
4322 /// &[(0,1), (1,2), (2,0), (1,3)], false, None
4323 /// ).unwrap();
4324 /// let edges = g.random_spanning_tree(42).unwrap();
4325 /// assert_eq!(edges.len(), 3);
4326 /// ```
4327 pub fn random_spanning_tree(&self, seed: u64) -> IgraphResult<Vec<u32>> {
4328 crate::algorithms::spanning::random_spanning_tree::random_spanning_tree(self, None, seed)
4329 }
4330
4331 // ── Community detection (extended) ──────────────────────────────
4332
4333 /// Edge betweenness community detection.
4334 ///
4335 /// # Examples
4336 ///
4337 /// ```
4338 /// use rust_igraph::Graph;
4339 ///
4340 /// let g = Graph::from_edges(
4341 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (2,3)],
4342 /// false, None
4343 /// ).unwrap();
4344 /// let result = g.edge_betweenness_community().unwrap();
4345 /// assert!(!result.membership.is_empty());
4346 /// ```
4347 pub fn edge_betweenness_community(
4348 &self,
4349 ) -> IgraphResult<crate::algorithms::community::edge_betweenness_community::EdgeBetweennessResult>
4350 {
4351 crate::algorithms::community::edge_betweenness_community::edge_betweenness_community(self)
4352 }
4353
4354 // ── Isomorphism (canonical / BLISS) ─────────────────────────────
4355
4356 /// Compute a canonical vertex permutation.
4357 ///
4358 /// # Examples
4359 ///
4360 /// ```
4361 /// use rust_igraph::Graph;
4362 ///
4363 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4364 /// let perm = g.canonical_permutation().unwrap();
4365 /// assert_eq!(perm.len(), 3);
4366 /// ```
4367 pub fn canonical_permutation(&self) -> IgraphResult<Vec<u32>> {
4368 crate::algorithms::isomorphism::canonical::canonical_permutation::canonical_permutation(
4369 self, None,
4370 )
4371 }
4372
4373 /// Count automorphisms of the graph.
4374 ///
4375 /// # Examples
4376 ///
4377 /// ```
4378 /// use rust_igraph::Graph;
4379 ///
4380 /// // K3 has 3! = 6 automorphisms
4381 /// let g = Graph::from_edges(
4382 /// &[(0,1), (1,2), (2,0)], false, None
4383 /// ).unwrap();
4384 /// let count = g.count_automorphisms().unwrap();
4385 /// assert!((count - 6.0).abs() < 1e-10);
4386 /// ```
4387 pub fn count_automorphisms(&self) -> IgraphResult<f64> {
4388 crate::algorithms::isomorphism::canonical::count_automorphisms::count_automorphisms(
4389 self, None,
4390 )
4391 }
4392
4393 /// Compute a generating set for the automorphism group.
4394 ///
4395 /// # Examples
4396 ///
4397 /// ```
4398 /// use rust_igraph::Graph;
4399 ///
4400 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4401 /// let gens = g.automorphism_group().unwrap();
4402 /// assert!(!gens.is_empty());
4403 /// ```
4404 pub fn automorphism_group(&self) -> IgraphResult<Vec<Vec<u32>>> {
4405 crate::algorithms::isomorphism::canonical::automorphism_group::automorphism_group(
4406 self, None,
4407 )
4408 }
4409
4410 // ── Epidemics ───────────────────────────────────────────────────
4411
4412 /// Run a single SIR (Susceptible-Infected-Recovered) simulation.
4413 ///
4414 /// `beta` is the infection rate, `gamma` is the recovery rate.
4415 ///
4416 /// # Examples
4417 ///
4418 /// ```
4419 /// use rust_igraph::Graph;
4420 ///
4421 /// let g = Graph::from_edges(
4422 /// &[(0,1), (1,2), (2,3), (3,4)], false, None
4423 /// ).unwrap();
4424 /// let result = g.sir(0.5, 0.1, 1, 42).unwrap();
4425 /// assert!(!result.is_empty());
4426 /// ```
4427 pub fn sir(
4428 &self,
4429 beta: f64,
4430 gamma: f64,
4431 no_sim: usize,
4432 seed: u64,
4433 ) -> IgraphResult<Vec<crate::algorithms::epidemics::Sir>> {
4434 crate::algorithms::epidemics::sir(self, beta, gamma, no_sim, seed)
4435 }
4436
4437 // ── Spanner ─────────────────────────────────────────────────────
4438
4439 /// Compute a graph spanner with the given stretch factor.
4440 ///
4441 /// Returns edge indices forming the spanner subgraph.
4442 ///
4443 /// # Examples
4444 ///
4445 /// ```
4446 /// use rust_igraph::Graph;
4447 ///
4448 /// let g = Graph::from_edges(
4449 /// &[(0,1), (1,2), (2,0), (1,3)], false, None
4450 /// ).unwrap();
4451 /// let edges = g.spanner(3.0).unwrap();
4452 /// assert!(!edges.is_empty());
4453 /// ```
4454 pub fn spanner(&self, stretch: f64) -> IgraphResult<Vec<u32>> {
4455 crate::algorithms::paths::spanner::spanner(self, stretch, None)
4456 }
4457
4458 // ── Graph recognizers ─────────────────────────────────────────
4459
4460 /// Check whether this graph is acyclic (a DAG for directed, forest for undirected).
4461 ///
4462 /// # Examples
4463 ///
4464 /// ```
4465 /// use rust_igraph::Graph;
4466 ///
4467 /// let tree = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4468 /// assert!(tree.is_acyclic());
4469 /// ```
4470 pub fn is_acyclic(&self) -> bool {
4471 crate::algorithms::properties::is_acyclic::is_acyclic(self)
4472 }
4473
4474 /// Check whether this graph is an apex forest.
4475 ///
4476 /// # Examples
4477 ///
4478 /// ```
4479 /// use rust_igraph::Graph;
4480 ///
4481 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4482 /// assert!(g.is_apex_forest().unwrap());
4483 /// ```
4484 pub fn is_apex_forest(&self) -> IgraphResult<bool> {
4485 crate::algorithms::properties::is_apex_forest::is_apex_forest(self)
4486 }
4487
4488 /// Check whether this graph is an apex tree.
4489 ///
4490 /// # Examples
4491 ///
4492 /// ```
4493 /// use rust_igraph::Graph;
4494 ///
4495 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4496 /// assert!(g.is_apex_tree().unwrap());
4497 /// ```
4498 pub fn is_apex_tree(&self) -> IgraphResult<bool> {
4499 crate::algorithms::properties::is_apex_tree::is_apex_tree(self)
4500 }
4501
4502 /// Check whether this graph is banner-free.
4503 ///
4504 /// # Examples
4505 ///
4506 /// ```
4507 /// use rust_igraph::Graph;
4508 ///
4509 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4510 /// assert!(g.is_banner_free().unwrap());
4511 /// ```
4512 pub fn is_banner_free(&self) -> IgraphResult<bool> {
4513 crate::algorithms::properties::is_banner_free::is_banner_free(self)
4514 }
4515
4516 /// Check whether this graph is a biclique.
4517 ///
4518 /// # Examples
4519 ///
4520 /// ```
4521 /// use rust_igraph::Graph;
4522 ///
4523 /// let g = Graph::from_edges(&[(0,2), (0,3), (1,2), (1,3)], false, None).unwrap();
4524 /// assert!(g.is_biclique().unwrap());
4525 /// ```
4526 pub fn is_biclique(&self) -> IgraphResult<bool> {
4527 crate::algorithms::properties::is_biclique::is_biclique(self)
4528 }
4529
4530 /// Check whether this graph is biregular.
4531 ///
4532 /// # Examples
4533 ///
4534 /// ```
4535 /// use rust_igraph::Graph;
4536 ///
4537 /// let g = Graph::from_edges(&[(0,2), (0,3), (1,2), (1,3)], false, None).unwrap();
4538 /// assert!(g.is_biregular().unwrap());
4539 /// ```
4540 pub fn is_biregular(&self) -> IgraphResult<bool> {
4541 crate::algorithms::properties::is_biregular::is_biregular(self)
4542 }
4543
4544 /// Check whether this graph is a block graph.
4545 ///
4546 /// # Examples
4547 ///
4548 /// ```
4549 /// use rust_igraph::Graph;
4550 ///
4551 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4552 /// assert!(g.is_block_graph().unwrap());
4553 /// ```
4554 pub fn is_block_graph(&self) -> IgraphResult<bool> {
4555 crate::algorithms::properties::is_block::is_block_graph(self)
4556 }
4557
4558 /// Check whether this graph is bowtie-free.
4559 ///
4560 /// # Examples
4561 ///
4562 /// ```
4563 /// use rust_igraph::Graph;
4564 ///
4565 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4566 /// assert!(g.is_bowtie_free().unwrap());
4567 /// ```
4568 pub fn is_bowtie_free(&self) -> IgraphResult<bool> {
4569 crate::algorithms::properties::is_bowtie_free::is_bowtie_free(self)
4570 }
4571
4572 /// Check whether this graph is bull-free.
4573 ///
4574 /// # Examples
4575 ///
4576 /// ```
4577 /// use rust_igraph::Graph;
4578 ///
4579 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4580 /// assert!(g.is_bull_free().unwrap());
4581 /// ```
4582 pub fn is_bull_free(&self) -> IgraphResult<bool> {
4583 crate::algorithms::properties::is_bull_free::is_bull_free(self)
4584 }
4585
4586 /// Check whether this graph is C4-free (contains no 4-cycle).
4587 ///
4588 /// # Examples
4589 ///
4590 /// ```
4591 /// use rust_igraph::Graph;
4592 ///
4593 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4594 /// assert!(g.is_c4_free().unwrap());
4595 /// ```
4596 pub fn is_c4_free(&self) -> IgraphResult<bool> {
4597 crate::algorithms::properties::is_c4_free::is_c4_free(self)
4598 }
4599
4600 /// Check whether this graph is C5-free.
4601 ///
4602 /// # Examples
4603 ///
4604 /// ```
4605 /// use rust_igraph::Graph;
4606 ///
4607 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4608 /// assert!(g.is_c5_free().unwrap());
4609 /// ```
4610 pub fn is_c5_free(&self) -> IgraphResult<bool> {
4611 crate::algorithms::properties::is_c5_free::is_c5_free(self)
4612 }
4613
4614 /// Check whether this graph is a cactus graph.
4615 ///
4616 /// # Examples
4617 ///
4618 /// ```
4619 /// use rust_igraph::Graph;
4620 ///
4621 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4622 /// assert!(g.is_cactus_graph().unwrap());
4623 /// ```
4624 pub fn is_cactus_graph(&self) -> IgraphResult<bool> {
4625 crate::algorithms::properties::is_cactus::is_cactus_graph(self)
4626 }
4627
4628 /// Check whether this graph is a caterpillar.
4629 ///
4630 /// # Examples
4631 ///
4632 /// ```
4633 /// use rust_igraph::Graph;
4634 ///
4635 /// let g = Graph::from_edges(&[(0,1), (1,2), (1,3)], false, None).unwrap();
4636 /// assert!(g.is_caterpillar().unwrap());
4637 /// ```
4638 pub fn is_caterpillar(&self) -> IgraphResult<bool> {
4639 crate::algorithms::properties::is_caterpillar::is_caterpillar(self)
4640 }
4641
4642 /// Check whether this graph is a chain graph.
4643 ///
4644 /// # Examples
4645 ///
4646 /// ```
4647 /// use rust_igraph::Graph;
4648 ///
4649 /// let g = Graph::from_edges(&[(0,2), (0,3), (1,3)], false, None).unwrap();
4650 /// assert!(g.is_chain_graph().unwrap());
4651 /// ```
4652 pub fn is_chain_graph(&self) -> IgraphResult<bool> {
4653 crate::algorithms::properties::is_chain_graph::is_chain_graph(self)
4654 }
4655
4656 /// Check whether this graph is chordal bipartite.
4657 ///
4658 /// # Examples
4659 ///
4660 /// ```
4661 /// use rust_igraph::Graph;
4662 ///
4663 /// let g = Graph::from_edges(&[(0,2), (1,2)], false, None).unwrap();
4664 /// assert!(g.is_chordal_bipartite().unwrap());
4665 /// ```
4666 pub fn is_chordal_bipartite(&self) -> IgraphResult<bool> {
4667 crate::algorithms::properties::is_chordal_bipartite::is_chordal_bipartite(self)
4668 }
4669
4670 /// Check whether this graph is claw-free.
4671 ///
4672 /// # Examples
4673 ///
4674 /// ```
4675 /// use rust_igraph::Graph;
4676 ///
4677 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4678 /// assert!(g.is_claw_free().unwrap());
4679 /// ```
4680 pub fn is_claw_free(&self) -> IgraphResult<bool> {
4681 crate::algorithms::properties::is_claw_free::is_claw_free(self)
4682 }
4683
4684 /// Check whether this graph is a cluster graph (disjoint union of cliques).
4685 ///
4686 /// # Examples
4687 ///
4688 /// ```
4689 /// use rust_igraph::Graph;
4690 ///
4691 /// let g = Graph::from_edges(&[(0,1)], false, None).unwrap();
4692 /// assert!(g.is_cluster_graph().unwrap());
4693 /// ```
4694 pub fn is_cluster_graph(&self) -> IgraphResult<bool> {
4695 crate::algorithms::properties::is_cluster::is_cluster_graph(self)
4696 }
4697
4698 /// Check whether this graph is co-bipartite.
4699 ///
4700 /// # Examples
4701 ///
4702 /// ```
4703 /// use rust_igraph::Graph;
4704 ///
4705 /// let g = Graph::from_edges(&[(0,1)], false, None).unwrap();
4706 /// assert!(g.is_co_bipartite().unwrap());
4707 /// ```
4708 pub fn is_co_bipartite(&self) -> IgraphResult<bool> {
4709 crate::algorithms::properties::is_co_bipartite::is_co_bipartite(self)
4710 }
4711
4712 /// Check whether this graph is co-chordal.
4713 ///
4714 /// # Examples
4715 ///
4716 /// ```
4717 /// use rust_igraph::Graph;
4718 ///
4719 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4720 /// assert!(g.is_co_chordal().unwrap());
4721 /// ```
4722 pub fn is_co_chordal(&self) -> IgraphResult<bool> {
4723 crate::algorithms::properties::is_co_chordal::is_co_chordal(self)
4724 }
4725
4726 /// Check whether this graph is a complete graph.
4727 ///
4728 /// # Examples
4729 ///
4730 /// ```
4731 /// use rust_igraph::Graph;
4732 ///
4733 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4734 /// assert!(g.is_complete().unwrap());
4735 /// ```
4736 pub fn is_complete(&self) -> IgraphResult<bool> {
4737 crate::algorithms::properties::is_complete::is_complete(self)
4738 }
4739
4740 /// Check whether this graph is a complete bipartite graph.
4741 ///
4742 /// # Examples
4743 ///
4744 /// ```
4745 /// use rust_igraph::Graph;
4746 ///
4747 /// let g = Graph::from_edges(&[(0,2), (0,3), (1,2), (1,3)], false, None).unwrap();
4748 /// assert!(g.is_complete_bipartite().unwrap());
4749 /// ```
4750 pub fn is_complete_bipartite(&self) -> IgraphResult<bool> {
4751 crate::algorithms::properties::is_complete_bipartite::is_complete_bipartite(self)
4752 }
4753
4754 /// Check whether this graph is cricket-free.
4755 ///
4756 /// # Examples
4757 ///
4758 /// ```
4759 /// use rust_igraph::Graph;
4760 ///
4761 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4762 /// assert!(g.is_cricket_free().unwrap());
4763 /// ```
4764 pub fn is_cricket_free(&self) -> IgraphResult<bool> {
4765 crate::algorithms::properties::is_cricket_free::is_cricket_free(self)
4766 }
4767
4768 /// Check whether this graph is cubic (3-regular).
4769 ///
4770 /// # Examples
4771 ///
4772 /// ```
4773 /// use rust_igraph::{Graph, full_graph};
4774 ///
4775 /// let g = full_graph(4, false, false).unwrap();
4776 /// assert!(g.is_cubic().unwrap());
4777 /// ```
4778 pub fn is_cubic(&self) -> IgraphResult<bool> {
4779 crate::algorithms::properties::is_cubic::is_cubic(self)
4780 }
4781
4782 /// Check whether this graph is a cycle.
4783 ///
4784 /// # Examples
4785 ///
4786 /// ```
4787 /// use rust_igraph::{Graph, cycle_graph};
4788 ///
4789 /// let g = cycle_graph(5, false, false).unwrap();
4790 /// assert!(g.is_cycle().unwrap());
4791 /// ```
4792 pub fn is_cycle(&self) -> IgraphResult<bool> {
4793 crate::algorithms::properties::is_cycle::is_cycle(self)
4794 }
4795
4796 /// Check whether this graph is dart-free.
4797 ///
4798 /// # Examples
4799 ///
4800 /// ```
4801 /// use rust_igraph::Graph;
4802 ///
4803 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4804 /// assert!(g.is_dart_free().unwrap());
4805 /// ```
4806 pub fn is_dart_free(&self) -> IgraphResult<bool> {
4807 crate::algorithms::properties::is_dart_free::is_dart_free(self)
4808 }
4809
4810 /// Check whether this graph is diamond-free.
4811 ///
4812 /// # Examples
4813 ///
4814 /// ```
4815 /// use rust_igraph::Graph;
4816 ///
4817 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4818 /// assert!(g.is_diamond_free().unwrap());
4819 /// ```
4820 pub fn is_diamond_free(&self) -> IgraphResult<bool> {
4821 crate::algorithms::properties::is_diamond_free::is_diamond_free(self)
4822 }
4823
4824 /// Check whether this graph is distance-hereditary.
4825 ///
4826 /// # Examples
4827 ///
4828 /// ```
4829 /// use rust_igraph::Graph;
4830 ///
4831 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4832 /// assert!(g.is_distance_hereditary().unwrap());
4833 /// ```
4834 pub fn is_distance_hereditary(&self) -> IgraphResult<bool> {
4835 crate::algorithms::properties::is_distance_hereditary::is_distance_hereditary(self)
4836 }
4837
4838 /// Check whether this graph is Eulerian.
4839 ///
4840 /// # Examples
4841 ///
4842 /// ```
4843 /// use rust_igraph::Graph;
4844 ///
4845 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4846 /// let class = g.is_eulerian().unwrap();
4847 /// assert!(class.has_cycle);
4848 /// ```
4849 pub fn is_eulerian(
4850 &self,
4851 ) -> IgraphResult<crate::algorithms::paths::eulerian::EulerianClassification> {
4852 crate::algorithms::paths::eulerian::is_eulerian(self)
4853 }
4854
4855 /// Check whether this graph is fork-free.
4856 ///
4857 /// # Examples
4858 ///
4859 /// ```
4860 /// use rust_igraph::Graph;
4861 ///
4862 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4863 /// assert!(g.is_fork_free().unwrap());
4864 /// ```
4865 pub fn is_fork_free(&self) -> IgraphResult<bool> {
4866 crate::algorithms::properties::is_fork_free::is_fork_free(self)
4867 }
4868
4869 /// Check whether this graph is gem-free.
4870 ///
4871 /// # Examples
4872 ///
4873 /// ```
4874 /// use rust_igraph::Graph;
4875 ///
4876 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4877 /// assert!(g.is_gem_free().unwrap());
4878 /// ```
4879 pub fn is_gem_free(&self) -> IgraphResult<bool> {
4880 crate::algorithms::properties::is_gem_free::is_gem_free(self)
4881 }
4882
4883 /// Check whether this graph is geodetic.
4884 ///
4885 /// # Examples
4886 ///
4887 /// ```
4888 /// use rust_igraph::Graph;
4889 ///
4890 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4891 /// assert!(g.is_geodetic().unwrap());
4892 /// ```
4893 pub fn is_geodetic(&self) -> IgraphResult<bool> {
4894 crate::algorithms::properties::is_geodetic::is_geodetic(self)
4895 }
4896
4897 /// Check whether this graph is house-free.
4898 ///
4899 /// # Examples
4900 ///
4901 /// ```
4902 /// use rust_igraph::Graph;
4903 ///
4904 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4905 /// assert!(g.is_house_free().unwrap());
4906 /// ```
4907 pub fn is_house_free(&self) -> IgraphResult<bool> {
4908 crate::algorithms::properties::is_house_free::is_house_free(self)
4909 }
4910
4911 /// Check whether this graph is k-degenerate.
4912 ///
4913 /// # Examples
4914 ///
4915 /// ```
4916 /// use rust_igraph::Graph;
4917 ///
4918 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
4919 /// assert!(g.is_k_degenerate(1).unwrap());
4920 /// ```
4921 pub fn is_k_degenerate(&self, k: u32) -> IgraphResult<bool> {
4922 crate::algorithms::properties::is_k_degenerate::is_k_degenerate(self, k)
4923 }
4924
4925 /// Check whether this graph is a lobster.
4926 ///
4927 /// # Examples
4928 ///
4929 /// ```
4930 /// use rust_igraph::Graph;
4931 ///
4932 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4933 /// assert!(g.is_lobster().unwrap());
4934 /// ```
4935 pub fn is_lobster(&self) -> IgraphResult<bool> {
4936 crate::algorithms::properties::is_lobster::is_lobster(self)
4937 }
4938
4939 /// Check whether this graph is net-free.
4940 ///
4941 /// # Examples
4942 ///
4943 /// ```
4944 /// use rust_igraph::Graph;
4945 ///
4946 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4947 /// assert!(g.is_net_free().unwrap());
4948 /// ```
4949 pub fn is_net_free(&self) -> IgraphResult<bool> {
4950 crate::algorithms::properties::is_net_free::is_net_free(self)
4951 }
4952
4953 /// Check whether this graph is P5-free.
4954 ///
4955 /// # Examples
4956 ///
4957 /// ```
4958 /// use rust_igraph::Graph;
4959 ///
4960 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4961 /// assert!(g.is_p5_free().unwrap());
4962 /// ```
4963 pub fn is_p5_free(&self) -> IgraphResult<bool> {
4964 crate::algorithms::properties::is_p5_free::is_p5_free(self)
4965 }
4966
4967 /// Check whether this graph is a path.
4968 ///
4969 /// # Examples
4970 ///
4971 /// ```
4972 /// use rust_igraph::Graph;
4973 ///
4974 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
4975 /// assert!(g.is_path().unwrap());
4976 /// ```
4977 pub fn is_path(&self) -> IgraphResult<bool> {
4978 crate::algorithms::properties::is_path::is_path(self)
4979 }
4980
4981 /// Check whether this graph is paw-free.
4982 ///
4983 /// # Examples
4984 ///
4985 /// ```
4986 /// use rust_igraph::Graph;
4987 ///
4988 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
4989 /// assert!(g.is_paw_free().unwrap());
4990 /// ```
4991 pub fn is_paw_free(&self) -> IgraphResult<bool> {
4992 crate::algorithms::properties::is_paw_free::is_paw_free(self)
4993 }
4994
4995 /// Check whether this graph is a proper interval graph.
4996 ///
4997 /// # Examples
4998 ///
4999 /// ```
5000 /// use rust_igraph::Graph;
5001 ///
5002 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5003 /// assert!(g.is_proper_interval().unwrap());
5004 /// ```
5005 pub fn is_proper_interval(&self) -> IgraphResult<bool> {
5006 crate::algorithms::properties::is_proper_interval::is_proper_interval(self)
5007 }
5008
5009 /// Check whether this graph is a pseudo-forest.
5010 ///
5011 /// # Examples
5012 ///
5013 /// ```
5014 /// use rust_igraph::Graph;
5015 ///
5016 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5017 /// assert!(g.is_pseudo_forest().unwrap());
5018 /// ```
5019 pub fn is_pseudo_forest(&self) -> IgraphResult<bool> {
5020 crate::algorithms::properties::is_pseudo_forest::is_pseudo_forest(self)
5021 }
5022
5023 /// Check whether this graph is Ptolemaic.
5024 ///
5025 /// # Examples
5026 ///
5027 /// ```
5028 /// use rust_igraph::Graph;
5029 ///
5030 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5031 /// assert!(g.is_ptolemaic().unwrap());
5032 /// ```
5033 pub fn is_ptolemaic(&self) -> IgraphResult<bool> {
5034 crate::algorithms::properties::is_ptolemaic::is_ptolemaic(self)
5035 }
5036
5037 /// Check whether this graph is self-complementary.
5038 ///
5039 /// # Examples
5040 ///
5041 /// ```
5042 /// use rust_igraph::Graph;
5043 ///
5044 /// // P_4 (path on 4 vertices) is self-complementary
5045 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5046 /// assert!(g.is_self_complementary().unwrap());
5047 /// ```
5048 pub fn is_self_complementary(&self) -> IgraphResult<bool> {
5049 crate::algorithms::properties::is_self_complementary::is_self_complementary(self)
5050 }
5051
5052 /// Check whether this graph is semicomplete.
5053 ///
5054 /// # Examples
5055 ///
5056 /// ```
5057 /// use rust_igraph::Graph;
5058 ///
5059 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], true, None).unwrap();
5060 /// assert!(g.is_semicomplete().unwrap());
5061 /// ```
5062 pub fn is_semicomplete(&self) -> IgraphResult<bool> {
5063 crate::algorithms::properties::is_semicomplete::is_semicomplete(self)
5064 }
5065
5066 /// Check whether this graph is a spider.
5067 ///
5068 /// # Examples
5069 ///
5070 /// ```
5071 /// use rust_igraph::Graph;
5072 ///
5073 /// let g = Graph::from_edges(&[(0,1), (0,2), (0,3)], false, None).unwrap();
5074 /// assert!(g.is_spider().unwrap());
5075 /// ```
5076 pub fn is_spider(&self) -> IgraphResult<bool> {
5077 crate::algorithms::properties::is_spider::is_spider(self)
5078 }
5079
5080 /// Check whether this graph is a split graph.
5081 ///
5082 /// # Examples
5083 ///
5084 /// ```
5085 /// use rust_igraph::Graph;
5086 ///
5087 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2), (2,3)], false, None).unwrap();
5088 /// assert!(g.is_split_graph().unwrap());
5089 /// ```
5090 pub fn is_split_graph(&self) -> IgraphResult<bool> {
5091 crate::algorithms::properties::is_split::is_split_graph(self)
5092 }
5093
5094 /// Check whether this graph is a star.
5095 ///
5096 /// # Examples
5097 ///
5098 /// ```
5099 /// use rust_igraph::Graph;
5100 ///
5101 /// let g = Graph::from_edges(&[(0,1), (0,2), (0,3)], false, None).unwrap();
5102 /// assert!(g.is_star().unwrap());
5103 /// ```
5104 pub fn is_star(&self) -> IgraphResult<bool> {
5105 crate::algorithms::properties::is_star::is_star(self)
5106 }
5107
5108 /// Check whether this graph is strongly chordal.
5109 ///
5110 /// # Examples
5111 ///
5112 /// ```
5113 /// use rust_igraph::Graph;
5114 ///
5115 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5116 /// assert!(g.is_strongly_chordal().unwrap());
5117 /// ```
5118 pub fn is_strongly_chordal(&self) -> IgraphResult<bool> {
5119 crate::algorithms::properties::is_strongly_chordal::is_strongly_chordal(self)
5120 }
5121
5122 /// Check whether this graph is a threshold graph.
5123 ///
5124 /// # Examples
5125 ///
5126 /// ```
5127 /// use rust_igraph::Graph;
5128 ///
5129 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
5130 /// assert!(g.is_threshold_graph().unwrap());
5131 /// ```
5132 pub fn is_threshold_graph(&self) -> IgraphResult<bool> {
5133 crate::algorithms::properties::is_threshold::is_threshold_graph(self)
5134 }
5135
5136 /// Check whether this graph is a tournament.
5137 ///
5138 /// # Examples
5139 ///
5140 /// ```
5141 /// use rust_igraph::Graph;
5142 ///
5143 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], true, None).unwrap();
5144 /// assert!(g.is_tournament().unwrap());
5145 /// ```
5146 pub fn is_tournament(&self) -> IgraphResult<bool> {
5147 crate::algorithms::properties::is_tournament::is_tournament(self)
5148 }
5149
5150 /// Check whether this graph is triangle-free.
5151 ///
5152 /// # Examples
5153 ///
5154 /// ```
5155 /// use rust_igraph::Graph;
5156 ///
5157 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5158 /// assert!(g.is_triangle_free().unwrap());
5159 /// ```
5160 pub fn is_triangle_free(&self) -> IgraphResult<bool> {
5161 crate::algorithms::properties::is_triangle_free::is_triangle_free(self)
5162 }
5163
5164 /// Check whether this graph is trivially perfect.
5165 ///
5166 /// # Examples
5167 ///
5168 /// ```
5169 /// use rust_igraph::Graph;
5170 ///
5171 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
5172 /// assert!(g.is_trivially_perfect().unwrap());
5173 /// ```
5174 pub fn is_trivially_perfect(&self) -> IgraphResult<bool> {
5175 crate::algorithms::properties::is_trivially_perfect::is_trivially_perfect(self)
5176 }
5177
5178 /// Check whether this graph is unicyclic.
5179 ///
5180 /// # Examples
5181 ///
5182 /// ```
5183 /// use rust_igraph::Graph;
5184 ///
5185 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5186 /// assert!(g.is_unicyclic().unwrap());
5187 /// ```
5188 pub fn is_unicyclic(&self) -> IgraphResult<bool> {
5189 crate::algorithms::properties::is_unicyclic::is_unicyclic(self)
5190 }
5191
5192 /// Check whether this graph is a wheel.
5193 ///
5194 /// # Examples
5195 ///
5196 /// ```
5197 /// use rust_igraph::Graph;
5198 ///
5199 /// // W_4: center 0 connected to rim {1,2,3}, rim forms a cycle
5200 /// let g = Graph::from_edges(
5201 /// &[(0,1), (0,2), (0,3), (1,2), (2,3), (3,1)],
5202 /// false, None
5203 /// ).unwrap();
5204 /// assert!(g.is_wheel().unwrap());
5205 /// ```
5206 pub fn is_wheel(&self) -> IgraphResult<bool> {
5207 crate::algorithms::properties::is_wheel::is_wheel(self)
5208 }
5209
5210 /// Check whether the graph is regular (all vertices have the same degree).
5211 ///
5212 /// # Examples
5213 ///
5214 /// ```
5215 /// use rust_igraph::{Graph, full_graph};
5216 ///
5217 /// let g = full_graph(4, false, false).unwrap();
5218 /// assert!(g.is_regular().unwrap());
5219 /// ```
5220 pub fn is_regular(&self) -> IgraphResult<bool> {
5221 crate::algorithms::properties::is_regular::is_regular(self)
5222 }
5223
5224 /// Check whether the graph is strongly regular, returning parameters if so.
5225 ///
5226 /// # Examples
5227 ///
5228 /// ```
5229 /// use rust_igraph::{Graph, cycle_graph};
5230 ///
5231 /// let g = cycle_graph(5, false, false).unwrap();
5232 /// let result = g.is_strongly_regular().unwrap();
5233 /// assert!(result.is_some());
5234 /// ```
5235 pub fn is_strongly_regular(
5236 &self,
5237 ) -> IgraphResult<
5238 Option<crate::algorithms::properties::is_strongly_regular::StronglyRegularParams>,
5239 > {
5240 crate::algorithms::properties::is_strongly_regular::is_strongly_regular(self)
5241 }
5242
5243 /// Check whether the graph is weakly chordal.
5244 ///
5245 /// # Examples
5246 ///
5247 /// ```
5248 /// use rust_igraph::Graph;
5249 ///
5250 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
5251 /// assert!(g.is_weakly_chordal().unwrap());
5252 /// ```
5253 pub fn is_weakly_chordal(&self) -> IgraphResult<bool> {
5254 crate::algorithms::properties::is_weakly_chordal::is_weakly_chordal(self)
5255 }
5256
5257 /// Check whether the graph is well-covered.
5258 ///
5259 /// # Examples
5260 ///
5261 /// ```
5262 /// use rust_igraph::{Graph, full_graph};
5263 ///
5264 /// let g = full_graph(4, false, false).unwrap();
5265 /// assert!(g.is_well_covered().unwrap());
5266 /// ```
5267 pub fn is_well_covered(&self) -> IgraphResult<bool> {
5268 crate::algorithms::properties::is_well_covered::is_well_covered(self)
5269 }
5270
5271 /// Check whether the graph is a windmill graph, returning (k, n) if so.
5272 ///
5273 /// # Examples
5274 ///
5275 /// ```
5276 /// use rust_igraph::{Graph, full_graph};
5277 ///
5278 /// let g = full_graph(3, false, false).unwrap();
5279 /// let result = g.is_windmill().unwrap();
5280 /// assert!(result.is_some());
5281 /// ```
5282 pub fn is_windmill(&self) -> IgraphResult<Option<(u32, u32)>> {
5283 crate::algorithms::properties::is_windmill::is_windmill(self)
5284 }
5285
5286 /// Check whether the graph is complete multipartite, returning
5287 /// partition sizes if so.
5288 ///
5289 /// # Examples
5290 ///
5291 /// ```
5292 /// use rust_igraph::{Graph, full_graph};
5293 ///
5294 /// let g = full_graph(3, false, false).unwrap();
5295 /// let result = g.is_complete_multipartite().unwrap();
5296 /// assert!(result.is_some());
5297 /// ```
5298 pub fn is_complete_multipartite(&self) -> IgraphResult<Option<Vec<u32>>> {
5299 crate::algorithms::properties::is_complete_multipartite::is_complete_multipartite(self)
5300 }
5301
5302 /// Check whether this graph satisfies Dirac's condition for Hamiltonicity.
5303 ///
5304 /// # Examples
5305 ///
5306 /// ```
5307 /// use rust_igraph::{Graph, full_graph};
5308 ///
5309 /// let g = full_graph(5, false, false).unwrap();
5310 /// assert!(g.satisfies_dirac().unwrap());
5311 /// ```
5312 pub fn satisfies_dirac(&self) -> IgraphResult<bool> {
5313 crate::algorithms::properties::satisfies_dirac::satisfies_dirac(self)
5314 }
5315
5316 /// Check whether this graph satisfies Ore's condition for Hamiltonicity.
5317 ///
5318 /// # Examples
5319 ///
5320 /// ```
5321 /// use rust_igraph::{Graph, full_graph};
5322 ///
5323 /// let g = full_graph(5, false, false).unwrap();
5324 /// assert!(g.satisfies_ore().unwrap());
5325 /// ```
5326 pub fn satisfies_ore(&self) -> IgraphResult<bool> {
5327 crate::algorithms::properties::satisfies_ore::satisfies_ore(self)
5328 }
5329
5330 // ── Centrality variants ───────────────────────────────────────
5331
5332 /// Compute edge betweenness centrality.
5333 ///
5334 /// # Examples
5335 ///
5336 /// ```
5337 /// use rust_igraph::Graph;
5338 ///
5339 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5340 /// let eb = g.edge_betweenness().unwrap();
5341 /// assert_eq!(eb.len(), 3);
5342 /// ```
5343 pub fn edge_betweenness(&self) -> IgraphResult<Vec<f64>> {
5344 crate::algorithms::properties::edge_betweenness::edge_betweenness(self)
5345 }
5346
5347 /// Compute betweenness centrality with a distance cutoff.
5348 ///
5349 /// # Examples
5350 ///
5351 /// ```
5352 /// use rust_igraph::Graph;
5353 ///
5354 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5355 /// let bc = g.betweenness_cutoff(2).unwrap();
5356 /// assert_eq!(bc.len(), 4);
5357 /// ```
5358 pub fn betweenness_cutoff(&self, cutoff: u32) -> IgraphResult<Vec<f64>> {
5359 crate::algorithms::properties::betweenness_cutoff::betweenness_cutoff(self, cutoff)
5360 }
5361
5362 /// Compute weighted betweenness centrality.
5363 ///
5364 /// # Examples
5365 ///
5366 /// ```
5367 /// use rust_igraph::Graph;
5368 ///
5369 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5370 /// let bc = g.betweenness_weighted(&[1.0, 2.0]).unwrap();
5371 /// assert_eq!(bc.len(), 3);
5372 /// ```
5373 pub fn betweenness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
5374 crate::algorithms::properties::betweenness_weighted::betweenness_weighted(self, weights)
5375 }
5376
5377 /// Compute weighted closeness centrality.
5378 ///
5379 /// # Examples
5380 ///
5381 /// ```
5382 /// use rust_igraph::Graph;
5383 ///
5384 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5385 /// let cc = g.closeness_weighted(&[1.0, 2.0]).unwrap();
5386 /// assert_eq!(cc.len(), 3);
5387 /// ```
5388 pub fn closeness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
5389 crate::algorithms::properties::closeness_weighted::closeness_weighted(self, weights)
5390 }
5391
5392 /// Compute edge betweenness centrality with a distance cutoff.
5393 ///
5394 /// # Examples
5395 ///
5396 /// ```
5397 /// use rust_igraph::Graph;
5398 ///
5399 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5400 /// let eb = g.edge_betweenness_cutoff(2).unwrap();
5401 /// assert_eq!(eb.len(), 3);
5402 /// ```
5403 pub fn edge_betweenness_cutoff(&self, cutoff: u32) -> IgraphResult<Vec<f64>> {
5404 crate::algorithms::properties::edge_betweenness_cutoff::edge_betweenness_cutoff(
5405 self, cutoff,
5406 )
5407 }
5408
5409 /// Compute weighted edge betweenness centrality.
5410 ///
5411 /// # Examples
5412 ///
5413 /// ```
5414 /// use rust_igraph::Graph;
5415 ///
5416 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5417 /// let eb = g.edge_betweenness_weighted(&[1.0, 2.0]).unwrap();
5418 /// assert_eq!(eb.len(), 2);
5419 /// ```
5420 pub fn edge_betweenness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
5421 crate::algorithms::properties::edge_betweenness_weighted::edge_betweenness_weighted(
5422 self, weights,
5423 )
5424 }
5425
5426 /// Compute weighted harmonic centrality.
5427 ///
5428 /// # Examples
5429 ///
5430 /// ```
5431 /// use rust_igraph::Graph;
5432 ///
5433 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5434 /// let hc = g.harmonic_centrality_weighted(&[1.0, 2.0]).unwrap();
5435 /// assert_eq!(hc.len(), 3);
5436 /// ```
5437 pub fn harmonic_centrality_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
5438 crate::algorithms::properties::harmonic_weighted::harmonic_centrality_weighted(
5439 self, weights,
5440 )
5441 }
5442
5443 /// Compute weighted `PageRank`.
5444 ///
5445 /// # Examples
5446 ///
5447 /// ```
5448 /// use rust_igraph::Graph;
5449 ///
5450 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5451 /// let pr = g.pagerank_weighted(&[1.0, 2.0]).unwrap();
5452 /// assert_eq!(pr.len(), 3);
5453 /// ```
5454 pub fn pagerank_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
5455 crate::algorithms::properties::pagerank_weighted::pagerank_weighted(self, weights)
5456 }
5457
5458 // ── Connectivity & structural ─────────────────────────────────
5459
5460 /// Compute the cohesive block structure.
5461 ///
5462 /// # Examples
5463 ///
5464 /// ```
5465 /// use rust_igraph::Graph;
5466 ///
5467 /// let g = Graph::from_edges(
5468 /// &[(0,1), (1,2), (2,0), (2,3), (3,4), (4,5), (5,3)],
5469 /// false, None
5470 /// ).unwrap();
5471 /// let blocks = g.cohesive_blocks().unwrap();
5472 /// assert!(!blocks.blocks.is_empty());
5473 /// ```
5474 pub fn cohesive_blocks(
5475 &self,
5476 ) -> IgraphResult<crate::algorithms::connectivity::cohesive_blocks::CohesiveBlocks> {
5477 crate::algorithms::connectivity::cohesive_blocks::cohesive_blocks(self)
5478 }
5479
5480 /// Count reachable vertices from each vertex.
5481 ///
5482 /// # Examples
5483 ///
5484 /// ```
5485 /// use rust_igraph::Graph;
5486 ///
5487 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5488 /// let counts = g.count_reachable().unwrap();
5489 /// assert_eq!(counts, vec![3, 3, 3]);
5490 /// ```
5491 pub fn count_reachable(&self) -> IgraphResult<Vec<u32>> {
5492 crate::algorithms::connectivity::reachability::count_reachable(self)
5493 }
5494
5495 /// Compute the reachability matrix.
5496 ///
5497 /// # Examples
5498 ///
5499 /// ```
5500 /// use rust_igraph::Graph;
5501 ///
5502 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5503 /// let mat = g.reachability_matrix().unwrap();
5504 /// assert!(mat[0][2]);
5505 /// assert!(!mat[2][0]);
5506 /// ```
5507 pub fn reachability_matrix(&self) -> IgraphResult<Vec<Vec<bool>>> {
5508 crate::algorithms::connectivity::reachability_matrix::reachability_matrix(self)
5509 }
5510
5511 /// Compute the transitive closure.
5512 ///
5513 /// # Examples
5514 ///
5515 /// ```
5516 /// use rust_igraph::Graph;
5517 ///
5518 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5519 /// let tc = g.transitive_closure().unwrap();
5520 /// assert!(tc.has_edge(0, 2));
5521 /// ```
5522 pub fn transitive_closure(&self) -> IgraphResult<Graph> {
5523 crate::algorithms::connectivity::transitive_closure::transitive_closure(self)
5524 }
5525
5526 // ── Flow & cuts ───────────────────────────────────────────────
5527
5528 /// Compute the global minimum cut.
5529 ///
5530 /// # Examples
5531 ///
5532 /// ```
5533 /// use rust_igraph::Graph;
5534 ///
5535 /// let g = Graph::from_edges(
5536 /// &[(0,1), (0,2), (1,3), (2,3)], false, None
5537 /// ).unwrap();
5538 /// let mc = g.mincut(None).unwrap();
5539 /// assert!(mc.value >= 2.0 - 1e-10);
5540 /// ```
5541 pub fn mincut(
5542 &self,
5543 capacity: Option<&[f64]>,
5544 ) -> IgraphResult<crate::algorithms::flow::mincut::Mincut> {
5545 crate::algorithms::flow::mincut::mincut(self, capacity)
5546 }
5547
5548 /// Compute the global minimum cut value.
5549 ///
5550 /// # Examples
5551 ///
5552 /// ```
5553 /// use rust_igraph::Graph;
5554 ///
5555 /// let g = Graph::from_edges(
5556 /// &[(0,1), (0,2), (1,3), (2,3)], false, None
5557 /// ).unwrap();
5558 /// let val = g.mincut_value(None).unwrap();
5559 /// assert!(val >= 2.0 - 1e-10);
5560 /// ```
5561 pub fn mincut_value(&self, capacity: Option<&[f64]>) -> IgraphResult<f64> {
5562 crate::algorithms::flow::mincut_value::mincut_value(self, capacity)
5563 }
5564
5565 /// Compute the Gomory-Hu tree.
5566 ///
5567 /// # Examples
5568 ///
5569 /// ```
5570 /// use rust_igraph::Graph;
5571 ///
5572 /// let g = Graph::from_edges(
5573 /// &[(0,1), (0,2), (1,2), (1,3)], false, None
5574 /// ).unwrap();
5575 /// let tree = g.gomory_hu_tree(None).unwrap();
5576 /// assert_eq!(tree.tree.vcount(), 4);
5577 /// ```
5578 pub fn gomory_hu_tree(
5579 &self,
5580 capacity: Option<&[f64]>,
5581 ) -> IgraphResult<crate::algorithms::flow::gomory_hu_tree::GomoryHuTree> {
5582 crate::algorithms::flow::gomory_hu_tree::gomory_hu_tree(self, capacity)
5583 }
5584
5585 /// Enumerate all minimum s-t cuts.
5586 ///
5587 /// # Examples
5588 ///
5589 /// ```
5590 /// use rust_igraph::Graph;
5591 ///
5592 /// let g = Graph::from_edges(
5593 /// &[(0,1), (0,2), (1,3), (2,3)], true, None
5594 /// ).unwrap();
5595 /// let cuts = g.all_st_cuts(0, 3).unwrap();
5596 /// assert!(!cuts.cuts.is_empty());
5597 /// ```
5598 pub fn all_st_cuts(
5599 &self,
5600 source: VertexId,
5601 target: VertexId,
5602 ) -> IgraphResult<crate::algorithms::flow::all_st_cuts::StCuts> {
5603 crate::algorithms::flow::all_st_cuts::all_st_cuts(self, source, target)
5604 }
5605
5606 /// Count edge-disjoint paths between two vertices.
5607 ///
5608 /// # Examples
5609 ///
5610 /// ```
5611 /// use rust_igraph::Graph;
5612 ///
5613 /// let g = Graph::from_edges(
5614 /// &[(0,1), (0,2), (1,3), (2,3)], false, None
5615 /// ).unwrap();
5616 /// let count = g.edge_disjoint_paths(0, 3).unwrap();
5617 /// assert_eq!(count, 2);
5618 /// ```
5619 pub fn edge_disjoint_paths(&self, source: VertexId, target: VertexId) -> IgraphResult<i64> {
5620 crate::algorithms::flow::edge_disjoint_paths::edge_disjoint_paths(self, source, target)
5621 }
5622
5623 // ── Paths & distances ─────────────────────────────────────────
5624
5625 /// Compute BFS distances from a source vertex.
5626 ///
5627 /// # Examples
5628 ///
5629 /// ```
5630 /// use rust_igraph::Graph;
5631 ///
5632 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5633 /// let dist = g.distances(0).unwrap();
5634 /// assert_eq!(dist[3], Some(3));
5635 /// ```
5636 pub fn distances(&self, source: VertexId) -> IgraphResult<Vec<Option<u32>>> {
5637 crate::algorithms::paths::distances::distances(self, source)
5638 }
5639
5640 /// Compute an Eulerian path if one exists.
5641 ///
5642 /// # Examples
5643 ///
5644 /// ```
5645 /// use rust_igraph::Graph;
5646 ///
5647 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5648 /// let path = g.eulerian_path().unwrap();
5649 /// assert!(path.is_some());
5650 /// ```
5651 pub fn eulerian_path(&self) -> IgraphResult<Option<Vec<u32>>> {
5652 crate::algorithms::paths::eulerian_construct::eulerian_path(self)
5653 }
5654
5655 /// Compute mean geodesic distance.
5656 ///
5657 /// # Examples
5658 ///
5659 /// ```
5660 /// use rust_igraph::Graph;
5661 ///
5662 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5663 /// let d = g.mean_distance().unwrap();
5664 /// assert!(d.is_some());
5665 /// ```
5666 pub fn mean_distance(&self) -> IgraphResult<Option<f64>> {
5667 crate::algorithms::properties::basic::mean_distance(self)
5668 }
5669
5670 /// Topological sort of a directed graph.
5671 ///
5672 /// # Examples
5673 ///
5674 /// ```
5675 /// use rust_igraph::Graph;
5676 ///
5677 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5678 /// let order = g.topological_sorting().unwrap();
5679 /// assert_eq!(order, vec![0, 1, 2]);
5680 /// ```
5681 pub fn topological_sorting(&self) -> IgraphResult<Vec<VertexId>> {
5682 crate::algorithms::properties::topological_sorting::topological_sorting(
5683 self,
5684 crate::algorithms::paths::dijkstra::DijkstraMode::Out,
5685 )
5686 }
5687
5688 /// List all triangles in the graph.
5689 ///
5690 /// # Examples
5691 ///
5692 /// ```
5693 /// use rust_igraph::Graph;
5694 ///
5695 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5696 /// let tris = g.list_triangles().unwrap();
5697 /// assert_eq!(tris.len(), 1);
5698 /// ```
5699 pub fn list_triangles(&self) -> IgraphResult<Vec<(u32, u32, u32)>> {
5700 crate::algorithms::properties::list_triangles::list_triangles(self)
5701 }
5702
5703 /// Compute the degree distribution.
5704 ///
5705 /// # Examples
5706 ///
5707 /// ```
5708 /// use rust_igraph::Graph;
5709 ///
5710 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5711 /// let dist = g.degree_distribution().unwrap();
5712 /// assert!(!dist.is_empty());
5713 /// ```
5714 pub fn degree_distribution(&self) -> IgraphResult<Vec<u32>> {
5715 crate::algorithms::properties::degree_distribution::degree_distribution(
5716 self,
5717 crate::algorithms::properties::degree::DegreeMode::All,
5718 )
5719 }
5720
5721 /// Get the edge list as (source, target) pairs.
5722 ///
5723 /// # Examples
5724 ///
5725 /// ```
5726 /// use rust_igraph::Graph;
5727 ///
5728 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5729 /// let edges = g.get_edgelist().unwrap();
5730 /// assert_eq!(edges.len(), 2);
5731 /// ```
5732 pub fn get_edgelist(&self) -> IgraphResult<Vec<(VertexId, VertexId)>> {
5733 crate::algorithms::properties::edgelist::get_edgelist(self)
5734 }
5735
5736 /// Compute the graph's regularity (degree if regular, None otherwise).
5737 ///
5738 /// # Examples
5739 ///
5740 /// ```
5741 /// use rust_igraph::Graph;
5742 ///
5743 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5744 /// assert_eq!(g.regularity().unwrap(), Some(2));
5745 /// ```
5746 pub fn regularity(&self) -> IgraphResult<Option<u32>> {
5747 crate::algorithms::properties::is_regular::regularity(self)
5748 }
5749
5750 /// Find a cycle in the graph.
5751 ///
5752 /// # Examples
5753 ///
5754 /// ```
5755 /// use rust_igraph::Graph;
5756 ///
5757 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
5758 /// let result = g.find_cycle().unwrap();
5759 /// assert!(!result.vertices.is_empty());
5760 /// ```
5761 pub fn find_cycle(&self) -> IgraphResult<crate::algorithms::cycles::CycleResult> {
5762 crate::algorithms::cycles::find_cycle(self, crate::algorithms::cycles::CycleMode::All)
5763 }
5764
5765 /// Compute the joint degree matrix.
5766 ///
5767 /// # Examples
5768 ///
5769 /// ```
5770 /// use rust_igraph::Graph;
5771 ///
5772 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5773 /// let jdm = g.joint_degree_matrix(None).unwrap();
5774 /// assert!(!jdm.is_empty());
5775 /// ```
5776 pub fn joint_degree_matrix(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<Vec<f64>>> {
5777 crate::algorithms::properties::joint_degree_matrix::joint_degree_matrix(self, weights)
5778 }
5779
5780 /// Compute the minimum dominating set.
5781 ///
5782 /// # Examples
5783 ///
5784 /// ```
5785 /// use rust_igraph::Graph;
5786 ///
5787 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5788 /// let dom = g.minimum_dominating_set().unwrap();
5789 /// assert!(!dom.is_empty());
5790 /// ```
5791 pub fn minimum_dominating_set(&self) -> IgraphResult<Vec<VertexId>> {
5792 crate::algorithms::dominating_set::minimum_dominating_set(self)
5793 }
5794
5795 /// Compute the k-th power of this graph.
5796 ///
5797 /// # Examples
5798 ///
5799 /// ```
5800 /// use rust_igraph::Graph;
5801 ///
5802 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5803 /// let g2 = g.graph_power(2).unwrap();
5804 /// assert!(g2.has_edge(0, 2));
5805 /// ```
5806 pub fn graph_power(&self, order: u32) -> IgraphResult<Graph> {
5807 crate::algorithms::operators::connect_neighborhood::graph_power(self, order)
5808 }
5809
5810 /// Compute the trussness of each edge.
5811 ///
5812 /// # Examples
5813 ///
5814 /// ```
5815 /// use rust_igraph::Graph;
5816 ///
5817 /// let g = Graph::from_edges(
5818 /// &[(0,1), (1,2), (2,0), (2,3)], false, None
5819 /// ).unwrap();
5820 /// let tr = g.trussness().unwrap();
5821 /// assert_eq!(tr.len(), g.ecount());
5822 /// ```
5823 pub fn trussness(&self) -> IgraphResult<Vec<u32>> {
5824 crate::algorithms::properties::trussness::trussness(self)
5825 }
5826
5827 // ── Graph operators ──────────────────────────────────────────────
5828
5829 /// Union of two graphs on the same vertex set.
5830 ///
5831 /// ```
5832 /// use rust_igraph::Graph;
5833 ///
5834 /// let a = Graph::from_edges(&[(0,1)], false, None).unwrap();
5835 /// let b = Graph::from_edges(&[(1,2)], false, None).unwrap();
5836 /// let u = a.union(&b).unwrap();
5837 /// assert_eq!(u.ecount(), 2);
5838 /// ```
5839 pub fn union(&self, other: &Graph) -> IgraphResult<Graph> {
5840 crate::algorithms::operators::union::union(self, other)
5841 }
5842
5843 /// Intersection of two graphs on the same vertex set.
5844 ///
5845 /// ```
5846 /// use rust_igraph::Graph;
5847 ///
5848 /// let a = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5849 /// let b = Graph::from_edges(&[(1,2), (2,3)], false, None).unwrap();
5850 /// let i = a.intersection(&b).unwrap();
5851 /// assert_eq!(i.ecount(), 1);
5852 /// ```
5853 pub fn intersection(&self, other: &Graph) -> IgraphResult<Graph> {
5854 crate::algorithms::operators::intersection::intersection(self, other)
5855 }
5856
5857 /// Edge difference: edges in `self` but not in `other`.
5858 ///
5859 /// ```
5860 /// use rust_igraph::Graph;
5861 ///
5862 /// let a = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
5863 /// let b = Graph::from_edges(&[(1,2)], false, None).unwrap();
5864 /// let d = a.difference(&b).unwrap();
5865 /// assert_eq!(d.ecount(), 1);
5866 /// ```
5867 pub fn difference(&self, other: &Graph) -> IgraphResult<Graph> {
5868 crate::algorithms::operators::difference::difference(self, other)
5869 }
5870
5871 /// Disjoint union: concatenate vertex sets, then concatenate edge sets.
5872 ///
5873 /// ```
5874 /// use rust_igraph::Graph;
5875 ///
5876 /// let a = Graph::from_edges(&[(0,1)], false, None).unwrap();
5877 /// let b = Graph::from_edges(&[(0,1)], false, None).unwrap();
5878 /// let d = a.disjoint_union(&b).unwrap();
5879 /// assert_eq!(d.vcount(), 4);
5880 /// assert_eq!(d.ecount(), 2);
5881 /// ```
5882 pub fn disjoint_union(&self, other: &Graph) -> IgraphResult<Graph> {
5883 crate::algorithms::operators::disjoint_union::disjoint_union(self, other)
5884 }
5885
5886 /// Join: disjoint union plus all edges between the two vertex sets.
5887 ///
5888 /// ```
5889 /// use rust_igraph::Graph;
5890 ///
5891 /// let a = Graph::with_vertices(2);
5892 /// let b = Graph::with_vertices(2);
5893 /// let j = a.join(&b).unwrap();
5894 /// assert_eq!(j.vcount(), 4);
5895 /// assert_eq!(j.ecount(), 4);
5896 /// ```
5897 pub fn join(&self, other: &Graph) -> IgraphResult<Graph> {
5898 crate::algorithms::operators::join::join(self, other)
5899 }
5900
5901 /// Compose two graphs (relational composition).
5902 ///
5903 /// ```
5904 /// use rust_igraph::Graph;
5905 ///
5906 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
5907 /// let c = g.compose(&g).unwrap();
5908 /// assert!(c.ecount() > 0);
5909 /// ```
5910 pub fn compose(&self, other: &Graph) -> IgraphResult<Graph> {
5911 crate::algorithms::operators::compose::compose(self, other)
5912 }
5913
5914 /// Cartesian product of two graphs.
5915 ///
5916 /// ```
5917 /// use rust_igraph::Graph;
5918 ///
5919 /// let p2 = Graph::from_edges(&[(0,1)], false, None).unwrap();
5920 /// let grid = p2.cartesian_product(&p2).unwrap();
5921 /// assert_eq!(grid.vcount(), 4);
5922 /// ```
5923 pub fn cartesian_product(&self, other: &Graph) -> IgraphResult<Graph> {
5924 crate::algorithms::operators::products::cartesian_product(self, other)
5925 }
5926
5927 /// Tensor (categorical/direct) product of two graphs.
5928 ///
5929 /// ```
5930 /// use rust_igraph::Graph;
5931 ///
5932 /// let p2 = Graph::from_edges(&[(0,1)], false, None).unwrap();
5933 /// let t = p2.tensor_product(&p2).unwrap();
5934 /// assert_eq!(t.vcount(), 4);
5935 /// ```
5936 pub fn tensor_product(&self, other: &Graph) -> IgraphResult<Graph> {
5937 crate::algorithms::operators::products::tensor_product(self, other)
5938 }
5939
5940 /// Strong product of two graphs.
5941 ///
5942 /// ```
5943 /// use rust_igraph::Graph;
5944 ///
5945 /// let p2 = Graph::from_edges(&[(0,1)], false, None).unwrap();
5946 /// let s = p2.strong_product(&p2).unwrap();
5947 /// assert_eq!(s.vcount(), 4);
5948 /// ```
5949 pub fn strong_product(&self, other: &Graph) -> IgraphResult<Graph> {
5950 crate::algorithms::operators::products::strong_product(self, other)
5951 }
5952
5953 /// Lexicographic product of two graphs.
5954 ///
5955 /// ```
5956 /// use rust_igraph::Graph;
5957 ///
5958 /// let a = Graph::from_edges(&[(0,1)], false, None).unwrap();
5959 /// let b = Graph::with_vertices(2);
5960 /// let l = a.lexicographic_product(&b).unwrap();
5961 /// assert_eq!(l.vcount(), 4);
5962 /// ```
5963 pub fn lexicographic_product(&self, other: &Graph) -> IgraphResult<Graph> {
5964 crate::algorithms::operators::products::lexicographic_product(self, other)
5965 }
5966
5967 /// Connect each vertex to all vertices within distance `order`.
5968 ///
5969 /// ```
5970 /// use rust_igraph::Graph;
5971 ///
5972 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5973 /// let c = g.connect_neighborhood(2).unwrap();
5974 /// assert!(c.ecount() > g.ecount());
5975 /// ```
5976 pub fn connect_neighborhood(&self, order: u32) -> IgraphResult<Graph> {
5977 crate::algorithms::operators::connect_neighborhood::connect_neighborhood(self, order)
5978 }
5979
5980 /// Rewire edges while preserving the degree sequence.
5981 ///
5982 /// ```
5983 /// use rust_igraph::Graph;
5984 ///
5985 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3), (3,0)], false, None).unwrap();
5986 /// let r = g.rewire(100, false, 42).unwrap();
5987 /// assert_eq!(r.ecount(), g.ecount());
5988 /// ```
5989 pub fn rewire(&self, num_trials: usize, loops: bool, seed: u64) -> IgraphResult<Graph> {
5990 crate::algorithms::operators::rewire::rewire(self, num_trials, loops, seed)
5991 }
5992
5993 /// Randomly rewire each edge with probability `prob`.
5994 ///
5995 /// ```
5996 /// use rust_igraph::Graph;
5997 ///
5998 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
5999 /// let r = g.rewire_edges(0.5, false, 42).unwrap();
6000 /// assert_eq!(r.vcount(), g.vcount());
6001 /// ```
6002 pub fn rewire_edges(&self, prob: f64, loops: bool, seed: u64) -> IgraphResult<Graph> {
6003 crate::algorithms::operators::rewire_edges::rewire_edges(self, prob, loops, seed)
6004 }
6005
6006 /// Extract a subgraph induced by the given edge ids.
6007 ///
6008 /// ```
6009 /// use rust_igraph::Graph;
6010 ///
6011 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
6012 /// let s = g.subgraph_from_edges(&[0, 1]).unwrap();
6013 /// assert_eq!(s.graph.ecount(), 2);
6014 /// ```
6015 pub fn subgraph_from_edges(
6016 &self,
6017 eids: &[u32],
6018 ) -> IgraphResult<crate::algorithms::operators::subgraph_from_edges::SubgraphFromEdgesResult>
6019 {
6020 crate::algorithms::operators::subgraph_from_edges::subgraph_from_edges(self, eids, true)
6021 }
6022
6023 /// The Even-Tarjan reduction of a directed graph.
6024 ///
6025 /// ```
6026 /// use rust_igraph::Graph;
6027 ///
6028 /// let g = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
6029 /// let et = g.even_tarjan_reduction().unwrap();
6030 /// assert!(et.graph.vcount() > g.vcount());
6031 /// ```
6032 pub fn even_tarjan_reduction(
6033 &self,
6034 ) -> IgraphResult<crate::algorithms::operators::even_tarjan::EvenTarjanResult> {
6035 crate::algorithms::operators::even_tarjan::even_tarjan_reduction(self)
6036 }
6037
6038 /// Bipartite projection onto one vertex type.
6039 ///
6040 /// `project_type` selects which side: `false` projects the `false`-typed
6041 /// vertices, `true` projects the `true`-typed vertices.
6042 ///
6043 /// ```
6044 /// use rust_igraph::Graph;
6045 ///
6046 /// let mut g = Graph::new(4, false).unwrap();
6047 /// g.add_edge(0, 2).unwrap();
6048 /// g.add_edge(0, 3).unwrap();
6049 /// g.add_edge(1, 2).unwrap();
6050 /// g.add_edge(1, 3).unwrap();
6051 /// let types = vec![false, false, true, true];
6052 /// let p = g.bipartite_projection(&types, false).unwrap();
6053 /// assert_eq!(p.graph.vcount(), 2);
6054 /// ```
6055 pub fn bipartite_projection(
6056 &self,
6057 types: &[bool],
6058 project_type: bool,
6059 ) -> IgraphResult<crate::algorithms::operators::bipartite_projection::BipartiteProjection> {
6060 crate::algorithms::operators::bipartite_projection::bipartite_projection(
6061 self,
6062 types,
6063 project_type,
6064 )
6065 }
6066
6067 // ── Paths (advanced) ─────────────────────────────────────────────
6068
6069 /// Bellman-Ford shortest-path distances from a single source.
6070 ///
6071 /// ```
6072 /// use rust_igraph::Graph;
6073 ///
6074 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,3)], false, None).unwrap();
6075 /// let w = vec![1.0; g.ecount()];
6076 /// let d = g.bellman_ford_distances(0, &w).unwrap();
6077 /// assert!((d[3].unwrap() - 3.0).abs() < 1e-9);
6078 /// ```
6079 pub fn bellman_ford_distances(
6080 &self,
6081 source: VertexId,
6082 weights: &[f64],
6083 ) -> IgraphResult<Vec<Option<f64>>> {
6084 crate::algorithms::paths::bellman_ford::bellman_ford_distances(self, source, weights)
6085 }
6086
6087 /// Floyd-Warshall all-pairs shortest-path distances.
6088 ///
6089 /// ```
6090 /// use rust_igraph::Graph;
6091 ///
6092 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
6093 /// let d = g.floyd_warshall_distances(None).unwrap();
6094 /// assert!((d[0][2].unwrap() - 2.0).abs() < 1e-9);
6095 /// ```
6096 pub fn floyd_warshall_distances(
6097 &self,
6098 weights: Option<&[f64]>,
6099 ) -> IgraphResult<Vec<Vec<Option<f64>>>> {
6100 crate::algorithms::paths::floyd_warshall::floyd_warshall_distances(self, weights)
6101 }
6102
6103 /// Find the k shortest paths between two vertices.
6104 ///
6105 /// ```
6106 /// use rust_igraph::Graph;
6107 ///
6108 /// let g = Graph::from_edges(
6109 /// &[(0,1), (1,3), (0,2), (2,3)], false, None,
6110 /// ).unwrap();
6111 /// let w = vec![1.0; g.ecount()];
6112 /// let paths = g.k_shortest_paths(0, 3, &w, 2).unwrap();
6113 /// assert_eq!(paths.len(), 2);
6114 /// ```
6115 pub fn k_shortest_paths(
6116 &self,
6117 source: VertexId,
6118 target: VertexId,
6119 weights: &[f64],
6120 k: usize,
6121 ) -> IgraphResult<Vec<crate::algorithms::paths::k_shortest_paths::KShortestPath>> {
6122 use crate::algorithms::paths::dijkstra::DijkstraMode;
6123 crate::algorithms::paths::k_shortest_paths::k_shortest_paths(
6124 self,
6125 source,
6126 target,
6127 weights,
6128 k,
6129 DijkstraMode::Out,
6130 )
6131 }
6132
6133 /// Enumerate all simple paths from a source vertex.
6134 ///
6135 /// ```
6136 /// use rust_igraph::Graph;
6137 ///
6138 /// let g = Graph::from_edges(&[(0,1), (1,2), (0,2)], false, None).unwrap();
6139 /// let paths = g.all_simple_paths(0, Some(&[2]), 1, 10).unwrap();
6140 /// assert!(paths.len() >= 2);
6141 /// ```
6142 pub fn all_simple_paths(
6143 &self,
6144 from: u32,
6145 to: Option<&[u32]>,
6146 min_length: i32,
6147 max_length: i32,
6148 ) -> IgraphResult<Vec<Vec<u32>>> {
6149 crate::algorithms::paths::simple_paths::all_simple_paths(
6150 self,
6151 from,
6152 to,
6153 crate::algorithms::paths::simple_paths::SimplePathMode::Out,
6154 min_length,
6155 max_length,
6156 -1,
6157 )
6158 }
6159
6160 // ── Matching ──────────────────────────────────────────────────────
6161
6162 /// Maximum bipartite matching.
6163 ///
6164 /// ```
6165 /// use rust_igraph::Graph;
6166 ///
6167 /// let mut g = Graph::new(4, false).unwrap();
6168 /// g.add_edge(0, 2).unwrap();
6169 /// g.add_edge(0, 3).unwrap();
6170 /// g.add_edge(1, 2).unwrap();
6171 /// let types = vec![false, false, true, true];
6172 /// let m = g.maximum_bipartite_matching(&types).unwrap();
6173 /// assert_eq!(m.matching_size, 2);
6174 /// ```
6175 pub fn maximum_bipartite_matching(
6176 &self,
6177 types: &[bool],
6178 ) -> IgraphResult<crate::algorithms::matching::MatchingResult> {
6179 crate::algorithms::matching::maximum_bipartite_matching(self, types)
6180 }
6181
6182 // ── Coloring ─────────────────────────────────────────────────────
6183
6184 /// Greedy vertex coloring.
6185 ///
6186 /// ```
6187 /// use rust_igraph::{Graph, GreedyColoringHeuristic};
6188 ///
6189 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6190 /// let colors = g.vertex_coloring_greedy(GreedyColoringHeuristic::ColoredNeighbors).unwrap();
6191 /// assert_eq!(colors.len(), 3);
6192 /// ```
6193 pub fn vertex_coloring_greedy(
6194 &self,
6195 heuristic: crate::algorithms::coloring::GreedyColoringHeuristic,
6196 ) -> IgraphResult<Vec<u32>> {
6197 crate::algorithms::coloring::vertex_coloring_greedy(self, heuristic)
6198 }
6199
6200 // ── Cycles ───────────────────────────────────────────────────────
6201
6202 /// Enumerate all simple cycles.
6203 ///
6204 /// ```
6205 /// use rust_igraph::{Graph, SimpleCycleMode};
6206 ///
6207 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6208 /// let cycles = g.simple_cycles(SimpleCycleMode::All, 3, None).unwrap();
6209 /// assert!(!cycles.is_empty());
6210 /// ```
6211 pub fn simple_cycles(
6212 &self,
6213 mode: crate::algorithms::simple_cycles::SimpleCycleMode,
6214 min_length: u32,
6215 max_length: Option<u32>,
6216 ) -> IgraphResult<Vec<crate::algorithms::simple_cycles::SimpleCycle>> {
6217 crate::algorithms::simple_cycles::simple_cycles(self, mode, min_length, max_length, None)
6218 }
6219
6220 // ── Community (additional) ───────────────────────────────────────
6221
6222 /// Leading eigenvector community detection.
6223 ///
6224 /// ```
6225 /// use rust_igraph::Graph;
6226 ///
6227 /// let g = Graph::from_edges(
6228 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (2,3)],
6229 /// false, None,
6230 /// ).unwrap();
6231 /// let result = g.leading_eigenvector(None, None).unwrap();
6232 /// assert!(result.membership.len() == g.vcount() as usize);
6233 /// ```
6234 pub fn leading_eigenvector(
6235 &self,
6236 weights: Option<&[f64]>,
6237 steps: Option<u32>,
6238 ) -> IgraphResult<crate::algorithms::community::leading_eigenvector::LeadingEigenvectorResult>
6239 {
6240 crate::algorithms::community::leading_eigenvector::leading_eigenvector(self, weights, steps)
6241 }
6242
6243 /// Fluid community detection.
6244 ///
6245 /// ```
6246 /// use rust_igraph::Graph;
6247 ///
6248 /// let g = Graph::from_edges(
6249 /// &[(0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (2,3)],
6250 /// false, None,
6251 /// ).unwrap();
6252 /// let r = g.fluid_communities(2).unwrap();
6253 /// assert_eq!(r.membership.len(), g.vcount() as usize);
6254 /// ```
6255 pub fn fluid_communities(
6256 &self,
6257 k: u32,
6258 ) -> IgraphResult<crate::algorithms::community::fluid_communities::FluidResult> {
6259 crate::algorithms::community::fluid_communities::fluid_communities(self, k)
6260 }
6261
6262 /// Motif census (subgraph isomorphism classes of a given size).
6263 ///
6264 /// ```
6265 /// use rust_igraph::Graph;
6266 ///
6267 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], true, None).unwrap();
6268 /// let hist = g.motifs_randesu(3).unwrap();
6269 /// assert!(!hist.is_empty());
6270 /// ```
6271 pub fn motifs_randesu(&self, size: u32) -> IgraphResult<Vec<f64>> {
6272 crate::algorithms::motifs::motifs_randesu::motifs_randesu(self, size)
6273 }
6274
6275 /// Personalized `PageRank` with a custom reset distribution.
6276 ///
6277 /// ```
6278 /// use rust_igraph::Graph;
6279 ///
6280 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6281 /// let reset = vec![1.0, 0.0, 0.0];
6282 /// let pr = g.personalized_pagerank(&reset).unwrap();
6283 /// assert_eq!(pr.len(), 3);
6284 /// ```
6285 pub fn personalized_pagerank(&self, reset: &[f64]) -> IgraphResult<Vec<f64>> {
6286 crate::algorithms::properties::personalized_pagerank::personalized_pagerank_default(
6287 self, reset,
6288 )
6289 }
6290
6291 // ── Isomorphism ─────────────────────────────────────────────────
6292
6293 /// Test whether two graphs are isomorphic (VF2).
6294 ///
6295 /// ```
6296 /// use rust_igraph::Graph;
6297 ///
6298 /// let a = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6299 /// let b = Graph::from_edges(&[(0,2), (2,1), (1,0)], false, None).unwrap();
6300 /// let result = a.isomorphic_vf2(&b).unwrap();
6301 /// assert!(result.iso);
6302 /// ```
6303 pub fn isomorphic_vf2(
6304 &self,
6305 other: &Graph,
6306 ) -> IgraphResult<crate::algorithms::isomorphism::vf2::Vf2Isomorphism> {
6307 crate::algorithms::isomorphism::vf2::isomorphic_vf2(self, other, None, None, None, None)
6308 }
6309
6310 /// Quick isomorphism test (delegates to the best available method).
6311 ///
6312 /// ```
6313 /// use rust_igraph::Graph;
6314 ///
6315 /// let a = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6316 /// let b = Graph::from_edges(&[(0,2), (2,1), (1,0)], false, None).unwrap();
6317 /// assert!(a.isomorphic(&b).unwrap());
6318 /// ```
6319 pub fn isomorphic(&self, other: &Graph) -> IgraphResult<bool> {
6320 crate::algorithms::isomorphism::queries::isomorphic(self, other)
6321 }
6322
6323 /// Count the number of isomorphisms between two graphs (VF2).
6324 ///
6325 /// ```
6326 /// use rust_igraph::Graph;
6327 ///
6328 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6329 /// let count = g.count_isomorphisms_vf2(&g).unwrap();
6330 /// assert_eq!(count, 6); // C3 has 6 automorphisms
6331 /// ```
6332 pub fn count_isomorphisms_vf2(&self, other: &Graph) -> IgraphResult<u64> {
6333 crate::algorithms::isomorphism::vf2::count_isomorphisms_vf2(
6334 self, other, None, None, None, None,
6335 )
6336 }
6337
6338 // ── Cliques ─────────────────────────────────────────────────────
6339
6340 /// Find all maximal independent vertex sets.
6341 ///
6342 /// ```
6343 /// use rust_igraph::Graph;
6344 ///
6345 /// let g = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
6346 /// let sets = g.independent_vertex_sets(1, 3).unwrap();
6347 /// assert!(!sets.is_empty());
6348 /// ```
6349 pub fn independent_vertex_sets(
6350 &self,
6351 min_size: u32,
6352 max_size: u32,
6353 ) -> IgraphResult<Vec<Vec<u32>>> {
6354 crate::algorithms::cliques::independent_vertex_sets(self, min_size, max_size, None)
6355 }
6356
6357 // ── Network properties ──────────────────────────────────────────
6358
6359 /// Global efficiency of the graph.
6360 ///
6361 /// ```
6362 /// use rust_igraph::Graph;
6363 ///
6364 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6365 /// let e = g.global_efficiency().unwrap();
6366 /// assert!(e.unwrap_or(0.0) > 0.0);
6367 /// ```
6368 pub fn global_efficiency(&self) -> IgraphResult<Option<f64>> {
6369 crate::algorithms::properties::efficiency::global_efficiency(self)
6370 }
6371
6372 /// Local efficiency for each vertex.
6373 ///
6374 /// ```
6375 /// use rust_igraph::Graph;
6376 ///
6377 /// let g = Graph::from_edges(&[(0,1), (1,2), (2,0)], false, None).unwrap();
6378 /// let e = g.local_efficiency().unwrap();
6379 /// assert_eq!(e.len(), 3);
6380 /// ```
6381 pub fn local_efficiency(&self) -> IgraphResult<Vec<f64>> {
6382 crate::algorithms::properties::efficiency::local_efficiency(self)
6383 }
6384
6385 /// Degree assortativity coefficient.
6386 ///
6387 /// ```
6388 /// use rust_igraph::Graph;
6389 ///
6390 /// let g = Graph::from_edges(
6391 /// &[(0,1), (1,2), (2,3), (3,4)], false, None,
6392 /// ).unwrap();
6393 /// let r = g.assortativity_degree().unwrap();
6394 /// assert!(r.is_some());
6395 /// ```
6396 pub fn assortativity_degree(&self) -> IgraphResult<Option<f64>> {
6397 crate::algorithms::properties::assortativity::assortativity_degree(self)
6398 }
6399
6400 /// Diversity (entropy) of vertex edge weights.
6401 ///
6402 /// ```
6403 /// use rust_igraph::Graph;
6404 ///
6405 /// let g = Graph::from_edges(&[(0,1), (0,2), (1,2)], false, None).unwrap();
6406 /// let w = vec![1.0, 2.0, 3.0];
6407 /// let d = g.diversity(&w).unwrap();
6408 /// assert_eq!(d.len(), 3);
6409 /// ```
6410 pub fn diversity(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
6411 crate::algorithms::properties::strength::diversity(self, weights)
6412 }
6413
6414 // ---- Paths (batch 5) ----
6415
6416 /// All-pairs shortest-path distances (unweighted BFS).
6417 ///
6418 /// Returns a flat `n*n` vector in row-major order where
6419 /// `result[i*n + j]` is the distance from vertex `i` to `j`,
6420 /// or `None` if unreachable.
6421 ///
6422 /// ```
6423 /// use rust_igraph::Graph;
6424 ///
6425 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6426 /// let d = g.distances_all().unwrap();
6427 /// assert_eq!(d[0 * 3 + 2], Some(2)); // path 0→1→2
6428 /// ```
6429 pub fn distances_all(&self) -> IgraphResult<Vec<Option<u32>>> {
6430 crate::algorithms::paths::distances_all::distances_all(self)
6431 }
6432
6433 /// Shortest-path distances from a set of source vertices.
6434 ///
6435 /// Returns a flat `sources.len() * n` vector in row-major order.
6436 ///
6437 /// ```
6438 /// use rust_igraph::Graph;
6439 ///
6440 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6441 /// let d = g.distances_from(&[0]).unwrap();
6442 /// assert_eq!(d[3], Some(3)); // vertex 3 is 3 hops from vertex 0
6443 /// ```
6444 pub fn distances_from(&self, sources: &[VertexId]) -> IgraphResult<Vec<Option<u32>>> {
6445 crate::algorithms::paths::distances_from::distances_from(self, sources)
6446 }
6447
6448 /// Shortest-path trees from a source vertex (one path per target).
6449 ///
6450 /// Returns a vector of vertex sequences, one per target vertex.
6451 ///
6452 /// ```
6453 /// use rust_igraph::Graph;
6454 ///
6455 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6456 /// let paths = g.get_shortest_paths(0).unwrap();
6457 /// assert_eq!(paths[2], vec![0, 1, 2]);
6458 /// ```
6459 pub fn get_shortest_paths(&self, source: VertexId) -> IgraphResult<Vec<Vec<VertexId>>> {
6460 crate::algorithms::paths::shortest_paths::get_shortest_paths(self, source)
6461 }
6462
6463 /// All shortest paths from a source vertex.
6464 ///
6465 /// ```
6466 /// use rust_igraph::Graph;
6467 ///
6468 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,3),(2,3)], false, None).unwrap();
6469 /// let asp = g.get_all_shortest_paths(0).unwrap();
6470 /// // Two shortest paths from 0 to 3: 0-1-3 and 0-2-3
6471 /// assert_eq!(asp.paths[3].len(), 2);
6472 /// ```
6473 pub fn get_all_shortest_paths(
6474 &self,
6475 source: VertexId,
6476 ) -> IgraphResult<crate::AllShortestPaths> {
6477 crate::algorithms::paths::all_shortest_paths::get_all_shortest_paths(self, source)
6478 }
6479
6480 /// Johnson's algorithm for all-pairs shortest paths with edge weights.
6481 ///
6482 /// Handles negative weights (but not negative cycles).
6483 ///
6484 /// ```
6485 /// use rust_igraph::Graph;
6486 ///
6487 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6488 /// let d = g.johnson_distances(&[1.0, 2.0]).unwrap();
6489 /// assert!((d[0][2].unwrap() - 3.0).abs() < 1e-10);
6490 /// ```
6491 pub fn johnson_distances(&self, weights: &[f64]) -> IgraphResult<Vec<Vec<Option<f64>>>> {
6492 crate::algorithms::paths::johnson::johnson_distances(self, weights)
6493 }
6494
6495 /// Widest (bottleneck) paths from a source vertex.
6496 ///
6497 /// ```
6498 /// use rust_igraph::Graph;
6499 ///
6500 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6501 /// let wp = g.widest_paths(0, &[10.0, 5.0]).unwrap();
6502 /// assert!((wp.widths[2].unwrap() - 5.0).abs() < 1e-10);
6503 /// ```
6504 pub fn widest_paths(
6505 &self,
6506 from: VertexId,
6507 weights: &[f64],
6508 ) -> IgraphResult<crate::WidestPaths> {
6509 crate::algorithms::paths::widest_path::widest_paths(self, from, weights)
6510 }
6511
6512 /// Graph center — vertices with minimum eccentricity.
6513 ///
6514 /// ```
6515 /// use rust_igraph::{Graph, cycle_graph};
6516 ///
6517 /// let g = cycle_graph(5, false, false).unwrap();
6518 /// let center = g.graph_center().unwrap();
6519 /// assert_eq!(center.len(), 5); // all vertices equidistant in a cycle
6520 /// ```
6521 pub fn graph_center(&self) -> IgraphResult<Vec<VertexId>> {
6522 crate::algorithms::paths::graph_center::graph_center(
6523 self,
6524 crate::algorithms::paths::radii::EccMode::Out,
6525 )
6526 }
6527
6528 /// Path-length histogram of the graph.
6529 ///
6530 /// ```
6531 /// use rust_igraph::Graph;
6532 ///
6533 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6534 /// let h = g.path_length_hist(false).unwrap();
6535 /// assert!(!h.hist.is_empty());
6536 /// ```
6537 pub fn path_length_hist(&self, directed: bool) -> IgraphResult<crate::PathLengthHistResult> {
6538 crate::algorithms::paths::histogram::path_length_hist(self, directed)
6539 }
6540
6541 /// Find an Eulerian cycle (every edge visited exactly once, returning
6542 /// to start).
6543 ///
6544 /// ```
6545 /// use rust_igraph::cycle_graph;
6546 ///
6547 /// let g = cycle_graph(5, false, false).unwrap();
6548 /// let cycle = g.eulerian_cycle().unwrap();
6549 /// assert_eq!(cycle.len(), 5); // 5 edges in C5
6550 /// ```
6551 pub fn eulerian_cycle(&self) -> IgraphResult<Vec<EdgeId>> {
6552 crate::algorithms::paths::eulerian_construct::eulerian_cycle(self)
6553 }
6554
6555 // ---- Centrality / properties (batch 5) ----
6556
6557 /// HITS hub and authority scores.
6558 ///
6559 /// ```
6560 /// use rust_igraph::Graph;
6561 ///
6562 /// let g = Graph::from_edges(&[(0,1),(1,2)], true, None).unwrap();
6563 /// let hits = g.hub_and_authority_scores().unwrap();
6564 /// assert_eq!(hits.hub.len(), 3);
6565 /// ```
6566 pub fn hub_and_authority_scores(&self) -> IgraphResult<crate::HitsScores> {
6567 crate::algorithms::properties::hits::hub_and_authority_scores(self)
6568 }
6569
6570 /// Weighted vertex degree (strength).
6571 ///
6572 /// ```
6573 /// use rust_igraph::Graph;
6574 ///
6575 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,2)], false, None).unwrap();
6576 /// let s = g.strength(&[1.0, 2.0, 3.0]).unwrap();
6577 /// assert!((s[0] - 3.0).abs() < 1e-10); // edges 0-1 (w=1) + 0-2 (w=2)
6578 /// ```
6579 pub fn strength(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
6580 crate::algorithms::properties::strength::strength(self, weights)
6581 }
6582
6583 /// Average nearest-neighbor degree by degree class (knn(k)).
6584 ///
6585 /// ```
6586 /// use rust_igraph::Graph;
6587 ///
6588 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6589 /// let k = g.knnk().unwrap();
6590 /// assert!(k.len() > 0);
6591 /// ```
6592 pub fn knnk(&self) -> IgraphResult<Vec<Option<f64>>> {
6593 crate::algorithms::properties::knn::knnk(self)
6594 }
6595
6596 /// Barrat's weighted clustering coefficient per vertex.
6597 ///
6598 /// ```
6599 /// use rust_igraph::Graph;
6600 ///
6601 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6602 /// let t = g.transitivity_barrat(&[1.0, 1.0, 1.0]).unwrap();
6603 /// assert!(t[0].unwrap() > 0.0);
6604 /// ```
6605 pub fn transitivity_barrat(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
6606 crate::algorithms::properties::triangles::transitivity_barrat(self, weights)
6607 }
6608
6609 /// Local scan statistic (order 1) — triangle counts per vertex.
6610 ///
6611 /// ```
6612 /// use rust_igraph::Graph;
6613 ///
6614 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6615 /// let s = g.local_scan_1(None).unwrap();
6616 /// assert_eq!(s.len(), 3);
6617 /// ```
6618 pub fn local_scan_1(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
6619 crate::algorithms::properties::local_scan::local_scan_1(self, weights)
6620 }
6621
6622 /// Maximum cardinality search ordering.
6623 ///
6624 /// ```
6625 /// use rust_igraph::Graph;
6626 ///
6627 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6628 /// let mcs = g.maximum_cardinality_search().unwrap();
6629 /// assert_eq!(mcs.alpha.len(), 3);
6630 /// ```
6631 pub fn maximum_cardinality_search(&self) -> IgraphResult<crate::McsResult> {
6632 crate::algorithms::chordality::maximum_cardinality_search(self)
6633 }
6634
6635 /// Vertex with the highest degree.
6636 ///
6637 /// ```
6638 /// use rust_igraph::Graph;
6639 ///
6640 /// let g = Graph::from_edges(&[(0,1),(0,2),(0,3),(1,2)], false, None).unwrap();
6641 /// let v = g.max_degree_vertex().unwrap();
6642 /// assert_eq!(v, Some(0)); // vertex 0 has degree 3
6643 /// ```
6644 pub fn max_degree_vertex(&self) -> IgraphResult<Option<VertexId>> {
6645 crate::algorithms::properties::degree::max_degree_vertex(
6646 self,
6647 crate::algorithms::properties::degree::DegreeMode::All,
6648 )
6649 }
6650
6651 // ---- Graph predicates / queries (batch 5) ----
6652
6653 /// Whether the graph has at least one self-loop.
6654 ///
6655 /// ```
6656 /// use rust_igraph::Graph;
6657 ///
6658 /// let g = Graph::from_edges(&[(0,1),(1,1)], false, None).unwrap();
6659 /// assert!(g.has_loop().unwrap());
6660 /// ```
6661 pub fn has_loop(&self) -> IgraphResult<bool> {
6662 crate::algorithms::properties::multiplicity::has_loop(self)
6663 }
6664
6665 /// Whether the graph has at least one pair of mutual (reciprocal) edges.
6666 ///
6667 /// ```
6668 /// use rust_igraph::Graph;
6669 ///
6670 /// let g = Graph::from_edges(&[(0,1),(1,0)], true, None).unwrap();
6671 /// assert!(g.has_mutual(true).unwrap());
6672 /// ```
6673 pub fn has_mutual(&self, loops: bool) -> IgraphResult<bool> {
6674 crate::algorithms::properties::mutual::has_mutual(self, loops)
6675 }
6676
6677 /// Per-edge test: is each edge a multi-edge?
6678 ///
6679 /// ```
6680 /// use rust_igraph::Graph;
6681 ///
6682 /// let g = Graph::from_edges(&[(0,1),(0,1),(1,2)], false, None).unwrap();
6683 /// let m = g.is_multiple().unwrap();
6684 /// assert!(m.iter().any(|&x| x)); // at least one multi-edge
6685 /// ```
6686 pub fn is_multiple(&self) -> IgraphResult<Vec<bool>> {
6687 crate::algorithms::properties::multiplicity::is_multiple(self)
6688 }
6689
6690 /// Per-edge test: is each edge mutual (has a reciprocal)?
6691 ///
6692 /// ```
6693 /// use rust_igraph::Graph;
6694 ///
6695 /// let g = Graph::from_edges(&[(0,1),(1,0),(0,2)], true, None).unwrap();
6696 /// let m = g.is_mutual(true).unwrap();
6697 /// assert!(m[0]); // edge 0→1 has reciprocal 1→0
6698 /// ```
6699 pub fn is_mutual(&self, loops: bool) -> IgraphResult<Vec<bool>> {
6700 crate::algorithms::properties::mutual::is_mutual(self, loops)
6701 }
6702
6703 // ---- Community detection (batch 5) ----
6704
6705 /// Modularity of a given community assignment.
6706 ///
6707 /// ```
6708 /// use rust_igraph::Graph;
6709 ///
6710 /// let g = Graph::from_edges(
6711 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6712 /// false, None,
6713 /// ).unwrap();
6714 /// let q = g.modularity(&[0,0,0,1,1,1], 1.0).unwrap();
6715 /// assert!(q.unwrap() > 0.0);
6716 /// ```
6717 pub fn modularity(&self, membership: &[u32], resolution: f64) -> IgraphResult<Option<f64>> {
6718 crate::algorithms::community::modularity::modularity(self, membership, resolution)
6719 }
6720
6721 // ---- Constructors / operators (batch 5) ----
6722
6723 /// Mycielskian — triangle-free graph with increasing chromatic number.
6724 ///
6725 /// ```
6726 /// use rust_igraph::Graph;
6727 ///
6728 /// let g = Graph::from_edges(&[(0,1)], false, None).unwrap();
6729 /// let m = g.mycielskian(1).unwrap();
6730 /// assert!(m.vcount() > g.vcount());
6731 /// ```
6732 pub fn mycielskian(&self, k: u32) -> IgraphResult<Graph> {
6733 crate::algorithms::constructors::mycielskian::mycielskian(self, k)
6734 }
6735
6736 /// Prüfer sequence of a labeled tree.
6737 ///
6738 /// ```
6739 /// use rust_igraph::Graph;
6740 ///
6741 /// // Path graph 0-1-2-3 is a tree
6742 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6743 /// let seq = g.to_prufer().unwrap();
6744 /// assert_eq!(seq.len(), 2); // n-2 elements for n=4
6745 /// ```
6746 pub fn to_prufer(&self) -> IgraphResult<Vec<u32>> {
6747 crate::algorithms::constructors::prufer::to_prufer(self)
6748 }
6749
6750 // ---- Connectivity / percolation (batch 5) ----
6751
6752 /// Bond (edge) percolation — add edges one by one and track components.
6753 ///
6754 /// ```
6755 /// use rust_igraph::Graph;
6756 ///
6757 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6758 /// let p = g.bond_percolation(&[0, 1, 2]).unwrap();
6759 /// assert_eq!(p.giant_size.len(), 3); // one snapshot per step
6760 /// ```
6761 pub fn bond_percolation(
6762 &self,
6763 edge_order: &[EdgeId],
6764 ) -> IgraphResult<crate::EdgelistPercolation> {
6765 crate::algorithms::connectivity::percolation::bond_percolation(self, edge_order)
6766 }
6767
6768 /// Site (vertex) percolation — add vertices one by one and track components.
6769 ///
6770 /// ```
6771 /// use rust_igraph::Graph;
6772 ///
6773 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6774 /// let p = g.site_percolation(&[0, 1, 2, 3]).unwrap();
6775 /// assert_eq!(p.giant_size.len(), 4);
6776 /// ```
6777 pub fn site_percolation(
6778 &self,
6779 vertex_order: &[VertexId],
6780 ) -> IgraphResult<crate::SitePercolation> {
6781 crate::algorithms::connectivity::percolation::site_percolation(self, vertex_order)
6782 }
6783
6784 /// Reachability matrix (transitive closure as a boolean matrix).
6785 ///
6786 /// ```
6787 /// use rust_igraph::{Graph, ReachabilityMode};
6788 ///
6789 /// let g = Graph::from_edges(&[(0,1),(1,2)], true, None).unwrap();
6790 /// let r = g.reachability(ReachabilityMode::Out).unwrap();
6791 /// assert!(r.is_reachable(0, 2)); // 0 can reach 2 transitively
6792 /// ```
6793 pub fn reachability(
6794 &self,
6795 mode: crate::ReachabilityMode,
6796 ) -> IgraphResult<crate::ReachabilityResult> {
6797 crate::algorithms::connectivity::reachability_scc::reachability(self, mode)
6798 }
6799
6800 // ---- Neighborhoods (batch 5) ----
6801
6802 /// Induced subgraphs of k-hop neighborhoods around each vertex.
6803 ///
6804 /// ```
6805 /// use rust_igraph::Graph;
6806 ///
6807 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6808 /// let nbrs = g.neighborhood_graphs(1).unwrap();
6809 /// assert_eq!(nbrs.len(), 4); // one subgraph per vertex
6810 /// ```
6811 pub fn neighborhood_graphs(&self, order: i32) -> IgraphResult<Vec<Graph>> {
6812 crate::algorithms::properties::neighborhood::neighborhood_graphs(self, order)
6813 }
6814
6815 // ---- Cliques (batch 5) ----
6816
6817 /// Weighted clique number — maximum total weight of any clique.
6818 ///
6819 /// ```
6820 /// use rust_igraph::Graph;
6821 ///
6822 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
6823 /// let wc = g.weighted_clique_number(&[1.0, 2.0, 3.0]).unwrap();
6824 /// assert!((wc - 6.0).abs() < 1e-10); // triangle, all 3 vertices
6825 /// ```
6826 pub fn weighted_clique_number(&self, vertex_weights: &[f64]) -> IgraphResult<f64> {
6827 crate::algorithms::cliques::weighted_clique_number(self, vertex_weights)
6828 }
6829
6830 // ---- Isomorphism (batch 5) ----
6831
6832 /// Isomorphism class of a small graph (up to 6 vertices).
6833 ///
6834 /// ```
6835 /// use rust_igraph::Graph;
6836 ///
6837 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6838 /// let cls = g.isoclass().unwrap();
6839 /// assert!(cls > 0);
6840 /// ```
6841 pub fn isoclass(&self) -> IgraphResult<u32> {
6842 crate::algorithms::motifs::isoclass::isoclass(self)
6843 }
6844
6845 // ---- Layout (batch 5) ----
6846
6847 /// Multidimensional scaling layout.
6848 ///
6849 /// ```
6850 /// use rust_igraph::Graph;
6851 ///
6852 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6853 /// let pos = g.layout_mds(None).unwrap();
6854 /// assert_eq!(pos.len(), 4);
6855 /// ```
6856 pub fn layout_mds(&self, dist: Option<&[Vec<f64>]>) -> IgraphResult<Vec<[f64; 2]>> {
6857 crate::algorithms::layout::mds::layout_mds(self, dist)
6858 }
6859
6860 /// Spherical layout (3D positions on a unit sphere).
6861 ///
6862 /// ```
6863 /// use rust_igraph::Graph;
6864 ///
6865 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6866 /// let pos = g.layout_sphere();
6867 /// assert_eq!(pos.len(), 3);
6868 /// ```
6869 pub fn layout_sphere(&self) -> Vec<(f64, f64, f64)> {
6870 crate::algorithms::layout::simple::layout_sphere(self)
6871 }
6872
6873 // ---- Weighted community detection (batch 6) ----
6874
6875 /// Louvain community detection with edge weights.
6876 ///
6877 /// ```
6878 /// use rust_igraph::Graph;
6879 ///
6880 /// let g = Graph::from_edges(
6881 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6882 /// false, None,
6883 /// ).unwrap();
6884 /// let r = g.louvain_weighted(&[1.0; 7]).unwrap();
6885 /// assert!(r.modularity > 0.0);
6886 /// ```
6887 pub fn louvain_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LouvainResult> {
6888 crate::algorithms::community::louvain::louvain_weighted(self, weights)
6889 }
6890
6891 /// Leiden community detection with edge weights.
6892 ///
6893 /// ```
6894 /// use rust_igraph::Graph;
6895 ///
6896 /// let g = Graph::from_edges(
6897 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6898 /// false, None,
6899 /// ).unwrap();
6900 /// let r = g.leiden_weighted(&[1.0; 7]).unwrap();
6901 /// assert!(r.quality > 0.0);
6902 /// ```
6903 pub fn leiden_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LeidenResult> {
6904 crate::algorithms::community::leiden::leiden_weighted(self, weights)
6905 }
6906
6907 /// Label propagation with edge weights.
6908 ///
6909 /// ```
6910 /// use rust_igraph::Graph;
6911 ///
6912 /// let g = Graph::from_edges(
6913 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6914 /// false, None,
6915 /// ).unwrap();
6916 /// let r = g.label_propagation_weighted(&[1.0; 7]).unwrap();
6917 /// assert_eq!(r.membership.len(), 6);
6918 /// ```
6919 pub fn label_propagation_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LpaResult> {
6920 crate::algorithms::community::label_propagation::label_propagation_weighted(self, weights)
6921 }
6922
6923 /// Walktrap community detection with edge weights.
6924 ///
6925 /// ```
6926 /// use rust_igraph::Graph;
6927 ///
6928 /// let g = Graph::from_edges(
6929 /// &[(0,1),(1,2),(2,0),(3,4),(4,5),(5,3),(0,3)],
6930 /// false, None,
6931 /// ).unwrap();
6932 /// let r = g.walktrap_weighted(&[1.0; 7]).unwrap();
6933 /// assert!(!r.modularity.is_empty());
6934 /// ```
6935 pub fn walktrap_weighted(&self, weights: &[f64]) -> IgraphResult<crate::WalktrapResult> {
6936 crate::algorithms::community::walktrap::walktrap_weighted(self, weights)
6937 }
6938
6939 // ---- Weighted distance/centrality (batch 6) ----
6940
6941 /// Weighted diameter (longest shortest-path distance).
6942 ///
6943 /// ```
6944 /// use rust_igraph::Graph;
6945 ///
6946 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6947 /// let d = g.diameter_weighted(&[1.0, 2.0]).unwrap();
6948 /// assert!((d.unwrap() - 3.0).abs() < 1e-10);
6949 /// ```
6950 pub fn diameter_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
6951 crate::algorithms::paths::radii::diameter_weighted(self, weights)
6952 }
6953
6954 /// Weighted eccentricity per vertex.
6955 ///
6956 /// ```
6957 /// use rust_igraph::Graph;
6958 ///
6959 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6960 /// let e = g.eccentricity_weighted(&[1.0, 2.0]).unwrap();
6961 /// assert_eq!(e.len(), 3);
6962 /// ```
6963 pub fn eccentricity_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
6964 crate::algorithms::paths::radii::eccentricity_weighted(self, weights)
6965 }
6966
6967 /// Weighted graph radius.
6968 ///
6969 /// ```
6970 /// use rust_igraph::Graph;
6971 ///
6972 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
6973 /// let r = g.radius_weighted(&[1.0, 2.0]).unwrap();
6974 /// assert!(r.is_some());
6975 /// ```
6976 pub fn radius_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
6977 crate::algorithms::paths::radii::radius_weighted(self, weights)
6978 }
6979
6980 /// Weighted knn(k) — average nearest-neighbor degree by degree class.
6981 ///
6982 /// ```
6983 /// use rust_igraph::Graph;
6984 ///
6985 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
6986 /// let k = g.knnk_weighted(&[1.0, 1.0, 1.0]).unwrap();
6987 /// assert!(!k.is_empty());
6988 /// ```
6989 pub fn knnk_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
6990 crate::algorithms::properties::knn::knnk_weighted(self, weights)
6991 }
6992
6993 /// `PageRank` via linear-system solver (alternative to power iteration).
6994 ///
6995 /// ```
6996 /// use rust_igraph::Graph;
6997 ///
6998 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], true, None).unwrap();
6999 /// let pr = g.pagerank_linsys().unwrap();
7000 /// assert_eq!(pr.len(), 3);
7001 /// ```
7002 pub fn pagerank_linsys(&self) -> IgraphResult<Vec<f64>> {
7003 crate::algorithms::properties::pagerank_linsys::pagerank_linsys(self)
7004 }
7005
7006 /// Local scan statistic of order k.
7007 ///
7008 /// ```
7009 /// use rust_igraph::Graph;
7010 ///
7011 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7012 /// let s = g.local_scan_k(1, None).unwrap();
7013 /// assert_eq!(s.len(), 3);
7014 /// ```
7015 pub fn local_scan_k(&self, k: u32, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
7016 crate::algorithms::properties::local_scan_k::local_scan_k(self, k, weights)
7017 }
7018
7019 // ---- Validators / predicates (batch 6) ----
7020
7021 /// Per-edge test: is each edge a self-loop?
7022 ///
7023 /// ```
7024 /// use rust_igraph::Graph;
7025 ///
7026 /// let g = Graph::from_edges(&[(0,1),(1,1),(2,3)], false, None).unwrap();
7027 /// let loops = g.is_loop().unwrap();
7028 /// assert!(!loops[0]); // 0-1 is not a loop
7029 /// assert!(loops[1]); // 1-1 is a loop
7030 /// ```
7031 pub fn is_loop(&self) -> IgraphResult<Vec<bool>> {
7032 crate::algorithms::properties::multiplicity::is_loop(self)
7033 }
7034
7035 /// Whether a set of vertices forms a clique.
7036 ///
7037 /// ```
7038 /// use rust_igraph::Graph;
7039 ///
7040 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7041 /// assert!(g.is_clique(&[0, 1, 2], false).unwrap());
7042 /// ```
7043 pub fn is_clique(&self, vertices: &[VertexId], directed: bool) -> IgraphResult<bool> {
7044 crate::algorithms::properties::is_clique::is_clique(self, vertices, directed)
7045 }
7046
7047 /// Whether a set of vertices forms an independent set.
7048 ///
7049 /// ```
7050 /// use rust_igraph::Graph;
7051 ///
7052 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7053 /// assert!(g.is_independent_vertex_set(&[0, 2]).unwrap());
7054 /// ```
7055 pub fn is_independent_vertex_set(&self, vertices: &[VertexId]) -> IgraphResult<bool> {
7056 crate::algorithms::properties::is_clique::is_independent_vertex_set(self, vertices)
7057 }
7058
7059 /// Whether a set of vertices is a separator (its removal disconnects the graph).
7060 ///
7061 /// ```
7062 /// use rust_igraph::Graph;
7063 ///
7064 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7065 /// assert!(g.is_separator(&[1]).unwrap());
7066 /// ```
7067 pub fn is_separator(&self, candidates: &[VertexId]) -> IgraphResult<bool> {
7068 crate::algorithms::connectivity::separators::is_separator(self, candidates)
7069 }
7070
7071 /// Whether a separator is minimal.
7072 ///
7073 /// ```
7074 /// use rust_igraph::Graph;
7075 ///
7076 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7077 /// assert!(g.is_minimal_separator(&[1]).unwrap());
7078 /// ```
7079 pub fn is_minimal_separator(&self, candidates: &[VertexId]) -> IgraphResult<bool> {
7080 crate::algorithms::connectivity::separators::is_minimal_separator(self, candidates)
7081 }
7082
7083 /// Whether a coloring is a valid vertex coloring.
7084 ///
7085 /// ```
7086 /// use rust_igraph::Graph;
7087 ///
7088 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7089 /// assert!(g.is_vertex_coloring(&[0, 1, 0]).unwrap());
7090 /// ```
7091 pub fn is_vertex_coloring(&self, colors: &[u32]) -> IgraphResult<bool> {
7092 crate::algorithms::coloring::is_vertex_coloring(self, colors)
7093 }
7094
7095 /// Whether a coloring is a valid edge coloring.
7096 ///
7097 /// ```
7098 /// use rust_igraph::Graph;
7099 ///
7100 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7101 /// assert!(g.is_edge_coloring(&[0, 1]).unwrap());
7102 /// ```
7103 pub fn is_edge_coloring(&self, colors: &[u32]) -> IgraphResult<bool> {
7104 crate::algorithms::coloring::is_edge_coloring(self, colors)
7105 }
7106
7107 /// Whether a set of vertices is a vertex cover.
7108 ///
7109 /// ```
7110 /// use rust_igraph::Graph;
7111 ///
7112 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7113 /// assert!(g.is_vertex_cover(&[1]));
7114 /// ```
7115 pub fn is_vertex_cover(&self, cover: &[VertexId]) -> bool {
7116 crate::algorithms::vertex_cover::is_vertex_cover(self, cover)
7117 }
7118
7119 /// Whether a set of edges is an edge cover.
7120 ///
7121 /// ```
7122 /// use rust_igraph::Graph;
7123 ///
7124 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7125 /// assert!(g.is_edge_cover(&[0, 1]));
7126 /// ```
7127 pub fn is_edge_cover(&self, cover: &[EdgeId]) -> bool {
7128 crate::algorithms::edge_cover::is_edge_cover(self, cover)
7129 }
7130
7131 /// Whether a set of vertices is a dominating set.
7132 ///
7133 /// ```
7134 /// use rust_igraph::Graph;
7135 ///
7136 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7137 /// assert!(g.is_dominating_set(&[1]));
7138 /// ```
7139 pub fn is_dominating_set(&self, dom_set: &[VertexId]) -> bool {
7140 crate::algorithms::dominating_set::is_dominating_set(self, dom_set)
7141 }
7142
7143 // ---- Operators (batch 6) ----
7144
7145 /// Reverse specific edges in a directed graph.
7146 ///
7147 /// ```
7148 /// use rust_igraph::Graph;
7149 ///
7150 /// let g = Graph::from_edges(&[(0,1),(1,2)], true, None).unwrap();
7151 /// let r = g.reverse_edges(&[0]).unwrap();
7152 /// assert_eq!(r.edge(0).unwrap(), (1, 0));
7153 /// ```
7154 pub fn reverse_edges(&self, eids: &[u32]) -> IgraphResult<Graph> {
7155 crate::algorithms::operators::reverse::reverse_edges(self, eids)
7156 }
7157
7158 /// Edges induced by a subset of vertices.
7159 ///
7160 /// ```
7161 /// use rust_igraph::Graph;
7162 ///
7163 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7164 /// let eids = g.induced_subgraph_edges(&[0, 1]).unwrap();
7165 /// assert_eq!(eids.len(), 1); // only edge 0-1
7166 /// ```
7167 pub fn induced_subgraph_edges(&self, vids: &[u32]) -> IgraphResult<Vec<u32>> {
7168 crate::algorithms::operators::induced_subgraph_edges::induced_subgraph_edges(self, vids)
7169 }
7170
7171 // ---- Similarity (batch 6) ----
7172
7173 /// Dice similarity between all vertex pairs (n*n flat matrix).
7174 ///
7175 /// ```
7176 /// use rust_igraph::Graph;
7177 ///
7178 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7179 /// let s = g.similarity_dice().unwrap();
7180 /// let n = g.vcount() as usize;
7181 /// assert_eq!(s.len(), n * n);
7182 /// ```
7183 pub fn similarity_dice(&self) -> IgraphResult<Vec<f64>> {
7184 crate::algorithms::properties::similarity::similarity_dice(self)
7185 }
7186
7187 /// Inverse-log-weighted similarity between all vertex pairs.
7188 ///
7189 /// ```
7190 /// use rust_igraph::Graph;
7191 ///
7192 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7193 /// let s = g.similarity_inverse_log_weighted().unwrap();
7194 /// let n = g.vcount() as usize;
7195 /// assert_eq!(s.len(), n * n);
7196 /// ```
7197 pub fn similarity_inverse_log_weighted(&self) -> IgraphResult<Vec<f64>> {
7198 crate::algorithms::properties::similarity::similarity_inverse_log_weighted(self)
7199 }
7200
7201 /// Jaccard similarity for given edges.
7202 ///
7203 /// ```
7204 /// use rust_igraph::Graph;
7205 ///
7206 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7207 /// let s = g.similarity_jaccard_es(&[0, 1]).unwrap();
7208 /// assert_eq!(s.len(), 2);
7209 /// ```
7210 pub fn similarity_jaccard_es(&self, eids: &[u32]) -> IgraphResult<Vec<f64>> {
7211 crate::algorithms::properties::similarity::similarity_jaccard_es(self, eids)
7212 }
7213
7214 // ---- Layout (batch 6) ----
7215
7216 /// Large Graph Layout (LGL).
7217 ///
7218 /// ```
7219 /// use rust_igraph::{Graph, LglParams};
7220 ///
7221 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7222 /// let pos = g.layout_lgl(&LglParams::default()).unwrap();
7223 /// assert_eq!(pos.len(), 4);
7224 /// ```
7225 pub fn layout_lgl(&self, params: &crate::LglParams) -> IgraphResult<Vec<[f64; 2]>> {
7226 crate::algorithms::layout::lgl::layout_lgl(self, params)
7227 }
7228
7229 /// Random 3D layout.
7230 ///
7231 /// ```
7232 /// use rust_igraph::Graph;
7233 ///
7234 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7235 /// let pos = g.layout_random_3d(42);
7236 /// assert_eq!(pos.len(), 3);
7237 /// ```
7238 pub fn layout_random_3d(&self, seed: u64) -> Vec<(f64, f64, f64)> {
7239 crate::algorithms::layout::simple::layout_random_3d(self, seed)
7240 }
7241
7242 /// Grid 3D layout.
7243 ///
7244 /// ```
7245 /// use rust_igraph::Graph;
7246 ///
7247 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7248 /// let pos = g.layout_grid_3d(2, 2);
7249 /// assert_eq!(pos.len(), 4);
7250 /// ```
7251 pub fn layout_grid_3d(&self, width: i32, height: i32) -> Vec<(f64, f64, f64)> {
7252 crate::algorithms::layout::simple::layout_grid_3d(self, width, height)
7253 }
7254
7255 // ---- Motifs (batch 6) ----
7256
7257 /// Count the total number of motifs of a given size.
7258 ///
7259 /// ```
7260 /// use rust_igraph::Graph;
7261 ///
7262 /// let g = Graph::from_edges(&[(0,1),(1,2),(0,2)], false, None).unwrap();
7263 /// let n = g.motifs_randesu_no(3).unwrap();
7264 /// assert!((n - 1.0).abs() < 1e-10); // one triangle
7265 /// ```
7266 pub fn motifs_randesu_no(&self, size: u32) -> IgraphResult<f64> {
7267 crate::algorithms::motifs::motifs_randesu::motifs_randesu_no(self, size)
7268 }
7269
7270 // ---- Graph inspection (batch 6) ----
7271
7272 /// Structural summary of the graph.
7273 ///
7274 /// ```
7275 /// use rust_igraph::Graph;
7276 ///
7277 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7278 /// let s = g.graph_summary().unwrap();
7279 /// assert_eq!(s.vcount, 3);
7280 /// assert_eq!(s.ecount, 2);
7281 /// ```
7282 pub fn graph_summary(&self) -> IgraphResult<crate::GraphSummary> {
7283 crate::algorithms::properties::summary::graph_summary(self)
7284 }
7285
7286 /// Human-readable structural summary string.
7287 ///
7288 /// ```
7289 /// use rust_igraph::Graph;
7290 ///
7291 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7292 /// let s = g.graph_summary_string().unwrap();
7293 /// assert!(s.contains("Vertices: 3"));
7294 /// assert!(s.contains("Edges: 2"));
7295 /// ```
7296 pub fn graph_summary_string(&self) -> IgraphResult<String> {
7297 crate::graph_summary_string(self)
7298 }
7299
7300 // ---- Matrix representations (batch 7) ----
7301
7302 /// Adjacency matrix of the graph.
7303 ///
7304 /// Returns a dense `V×V` matrix. For undirected graphs with
7305 /// [`AdjacencyType::Both`](crate::AdjacencyType::Both), the result is symmetric.
7306 ///
7307 /// ```
7308 /// use rust_igraph::{Graph, AdjacencyType, LoopHandling};
7309 ///
7310 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7311 /// let m = g.get_adjacency(AdjacencyType::Both, LoopHandling::Once).unwrap();
7312 /// assert_eq!(m.len(), 3);
7313 /// assert!((m[0][1] - 1.0).abs() < 1e-10);
7314 /// assert!((m[0][2]).abs() < 1e-10);
7315 /// ```
7316 pub fn get_adjacency(
7317 &self,
7318 adj_type: crate::AdjacencyType,
7319 loops: crate::LoopHandling,
7320 ) -> IgraphResult<Vec<Vec<f64>>> {
7321 crate::algorithms::properties::adjacency::get_adjacency(self, adj_type, None, loops)
7322 }
7323
7324 /// Weighted adjacency matrix of the graph.
7325 ///
7326 /// ```
7327 /// use rust_igraph::{Graph, AdjacencyType, LoopHandling};
7328 ///
7329 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7330 /// let w = vec![2.0, 3.0];
7331 /// let m = g.get_adjacency_weighted(AdjacencyType::Both, &w, LoopHandling::Once).unwrap();
7332 /// assert!((m[0][1] - 2.0).abs() < 1e-10);
7333 /// ```
7334 pub fn get_adjacency_weighted(
7335 &self,
7336 adj_type: crate::AdjacencyType,
7337 weights: &[f64],
7338 loops: crate::LoopHandling,
7339 ) -> IgraphResult<Vec<Vec<f64>>> {
7340 crate::algorithms::properties::adjacency::get_adjacency(
7341 self,
7342 adj_type,
7343 Some(weights),
7344 loops,
7345 )
7346 }
7347
7348 /// Laplacian matrix L = D - A (unnormalized, unweighted).
7349 ///
7350 /// ```
7351 /// use rust_igraph::Graph;
7352 ///
7353 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7354 /// let lap = g.get_laplacian().unwrap();
7355 /// assert!((lap[0][0] - 1.0).abs() < 1e-10); // degree of vertex 0
7356 /// assert!((lap[1][1] - 2.0).abs() < 1e-10); // degree of vertex 1
7357 /// ```
7358 pub fn get_laplacian(&self) -> IgraphResult<Vec<Vec<f64>>> {
7359 crate::algorithms::properties::laplacian::get_laplacian(
7360 self,
7361 crate::DegreeMode::All,
7362 crate::LaplacianNormalization::Unnormalized,
7363 None,
7364 )
7365 }
7366
7367 /// Laplacian matrix with normalization and optional weights.
7368 ///
7369 /// ```
7370 /// use rust_igraph::{Graph, DegreeMode, LaplacianNormalization};
7371 ///
7372 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7373 /// let lap = g.get_laplacian_full(
7374 /// DegreeMode::All,
7375 /// LaplacianNormalization::Symmetric,
7376 /// None,
7377 /// ).unwrap();
7378 /// assert!(lap[0][0] > 0.0);
7379 /// ```
7380 pub fn get_laplacian_full(
7381 &self,
7382 mode: crate::DegreeMode,
7383 normalization: crate::LaplacianNormalization,
7384 weights: Option<&[f64]>,
7385 ) -> IgraphResult<Vec<Vec<f64>>> {
7386 crate::algorithms::properties::laplacian::get_laplacian(self, mode, normalization, weights)
7387 }
7388
7389 /// Stochastic (transition) matrix of the graph.
7390 ///
7391 /// Each row (or column, if `column_wise` is true) sums to 1.
7392 ///
7393 /// ```
7394 /// use rust_igraph::Graph;
7395 ///
7396 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,2)], false, None).unwrap();
7397 /// let s = g.get_stochastic(false).unwrap();
7398 /// let row_sum: f64 = s[0].iter().sum();
7399 /// assert!((row_sum - 1.0).abs() < 1e-10);
7400 /// ```
7401 pub fn get_stochastic(&self, column_wise: bool) -> IgraphResult<Vec<Vec<f64>>> {
7402 crate::algorithms::properties::stochastic::get_stochastic(self, column_wise, None)
7403 }
7404
7405 // ---- Spectral embedding (batch 7) ----
7406
7407 /// Adjacency spectral embedding into `no` dimensions.
7408 ///
7409 /// Embeds the graph via the leading eigenvalues/eigenvectors of the
7410 /// adjacency matrix.
7411 ///
7412 /// ```
7413 /// use rust_igraph::{Graph, SpectralWhich};
7414 ///
7415 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3),(3,0)], false, None).unwrap();
7416 /// let emb = g.adjacency_spectral_embedding(2, SpectralWhich::LargestMagnitude).unwrap();
7417 /// assert_eq!(emb.embedding.len(), 4); // one row per vertex
7418 /// ```
7419 pub fn adjacency_spectral_embedding(
7420 &self,
7421 no: usize,
7422 which: crate::SpectralWhich,
7423 ) -> IgraphResult<crate::AdjacencySpectralEmbeddingResult> {
7424 crate::algorithms::embedding::adjacency_spectral_embedding::adjacency_spectral_embedding(
7425 self, no, None, which, true, None,
7426 )
7427 }
7428
7429 /// Laplacian spectral embedding into `no` dimensions.
7430 ///
7431 /// ```
7432 /// use rust_igraph::{Graph, SpectralWhich, LaplacianType};
7433 ///
7434 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3),(3,0)], false, None).unwrap();
7435 /// let emb = g.laplacian_spectral_embedding(
7436 /// 2, SpectralWhich::SmallestAlgebraic, LaplacianType::DA,
7437 /// ).unwrap();
7438 /// assert_eq!(emb.embedding.len(), 4);
7439 /// ```
7440 pub fn laplacian_spectral_embedding(
7441 &self,
7442 no: usize,
7443 which: crate::SpectralWhich,
7444 lap_type: crate::LaplacianType,
7445 ) -> IgraphResult<crate::LaplacianSpectralEmbeddingResult> {
7446 crate::algorithms::embedding::laplacian_spectral_embedding::laplacian_spectral_embedding(
7447 self, no, None, which, lap_type, true,
7448 )
7449 }
7450
7451 /// Eigenvalues and eigenvectors of the adjacency matrix.
7452 ///
7453 /// ```
7454 /// use rust_igraph::{Graph, EigenWhich};
7455 ///
7456 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
7457 /// let eig = g.eigen_adjacency(2, EigenWhich::LargestAlgebraic).unwrap();
7458 /// assert_eq!(eig.eigenvalues.len(), 2);
7459 /// ```
7460 pub fn eigen_adjacency(
7461 &self,
7462 nev: usize,
7463 which: crate::EigenWhich,
7464 ) -> IgraphResult<crate::EigenDecomposition> {
7465 crate::algorithms::eigen::adjacency::eigen_adjacency(self, nev, which)
7466 }
7467
7468 // ---- Additional algorithms (batch 7) ----
7469
7470 /// Feedback vertex set — a minimal set of vertices whose removal
7471 /// makes the graph acyclic.
7472 ///
7473 /// ```
7474 /// use rust_igraph::{Graph, FvsAlgorithm};
7475 ///
7476 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], true, None).unwrap();
7477 /// let fvs = g.feedback_vertex_set(FvsAlgorithm::Greedy).unwrap();
7478 /// assert!(!fvs.is_empty()); // need to break the cycle
7479 /// ```
7480 pub fn feedback_vertex_set(&self, algo: crate::FvsAlgorithm) -> IgraphResult<Vec<VertexId>> {
7481 crate::algorithms::feedback_vertex_set::feedback_vertex_set(self, None, algo)
7482 }
7483
7484 /// Complement graph (all edges that are *not* in the original).
7485 ///
7486 /// Self-loops are excluded.
7487 ///
7488 /// ```
7489 /// use rust_igraph::Graph;
7490 ///
7491 /// let g = Graph::from_edges(&[(0,1)], false, Some(3)).unwrap();
7492 /// let c = g.complementer().unwrap();
7493 /// assert_eq!(c.ecount(), 2); // edges 0-2 and 1-2
7494 /// ```
7495 pub fn complementer(&self) -> IgraphResult<Graph> {
7496 crate::algorithms::operators::complementer::complementer(self, false)
7497 }
7498
7499 /// Bipartite projection sizes without building the projected graphs.
7500 ///
7501 /// `types` assigns each vertex to one of two partitions (`false`/`true`).
7502 ///
7503 /// ```
7504 /// use rust_igraph::Graph;
7505 ///
7506 /// // K_{2,3} bipartite graph
7507 /// let g = Graph::from_edges(
7508 /// &[(0,2),(0,3),(0,4),(1,2),(1,3),(1,4)], false, None
7509 /// ).unwrap();
7510 /// let types = vec![false, false, true, true, true];
7511 /// let sz = g.bipartite_projection_size(&types).unwrap();
7512 /// assert_eq!(sz.vcount1, 2);
7513 /// assert_eq!(sz.vcount2, 3);
7514 /// ```
7515 pub fn bipartite_projection_size(
7516 &self,
7517 types: &[bool],
7518 ) -> IgraphResult<crate::BipartiteProjectionSize> {
7519 crate::algorithms::operators::bipartite_projection_size::bipartite_projection_size(
7520 self, types,
7521 )
7522 }
7523
7524 /// Unfold the graph into a tree by BFS from root vertices.
7525 ///
7526 /// Returns the unfolded tree and a mapping from new to old vertex ids.
7527 ///
7528 /// ```
7529 /// use rust_igraph::{Graph, DegreeMode, DijkstraMode, UnfoldTreeResult};
7530 ///
7531 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
7532 /// let r = g.unfold_tree(&[0], DegreeMode::All).unwrap();
7533 /// assert!(r.tree.is_tree(DijkstraMode::All).unwrap().is_some());
7534 /// assert!(!r.vertex_index.is_empty());
7535 /// ```
7536 pub fn unfold_tree(
7537 &self,
7538 roots: &[VertexId],
7539 mode: crate::DegreeMode,
7540 ) -> IgraphResult<crate::UnfoldTreeResult> {
7541 crate::algorithms::properties::unfold_tree::unfold_tree(self, roots, mode)
7542 }
7543
7544 // ---- Analysis / flow / isomorphism (batch 8) ----
7545
7546 /// S-t edge connectivity between two vertices.
7547 ///
7548 /// ```
7549 /// use rust_igraph::Graph;
7550 ///
7551 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,3),(2,3)], false, None).unwrap();
7552 /// let k = g.st_edge_connectivity(0, 3).unwrap();
7553 /// assert_eq!(k, 2);
7554 /// ```
7555 pub fn st_edge_connectivity(&self, source: u32, target: u32) -> IgraphResult<i64> {
7556 crate::st_edge_connectivity(self, source, target)
7557 }
7558
7559 /// Vertex-disjoint paths between two vertices.
7560 ///
7561 /// ```
7562 /// use rust_igraph::Graph;
7563 ///
7564 /// let g = Graph::from_edges(&[(0,1),(0,2),(1,3),(2,3)], false, None).unwrap();
7565 /// let n = g.vertex_disjoint_paths(0, 3).unwrap();
7566 /// assert_eq!(n, 2);
7567 /// ```
7568 pub fn vertex_disjoint_paths(&self, source: u32, target: u32) -> IgraphResult<i64> {
7569 crate::vertex_disjoint_paths(self, source, target)
7570 }
7571
7572 /// Test isomorphism using BLISS canonical labeling.
7573 ///
7574 /// ```
7575 /// use rust_igraph::{Graph, full_graph};
7576 ///
7577 /// let g1 = full_graph(4, false, false).unwrap();
7578 /// let g2 = full_graph(4, false, false).unwrap();
7579 /// let result = g1.isomorphic_bliss(&g2, None, None).unwrap();
7580 /// assert!(result.iso);
7581 /// ```
7582 pub fn isomorphic_bliss(
7583 &self,
7584 other: &Graph,
7585 colors1: Option<&[u32]>,
7586 colors2: Option<&[u32]>,
7587 ) -> IgraphResult<crate::Vf2Isomorphism> {
7588 crate::isomorphic_bliss(self, other, colors1, colors2)
7589 }
7590
7591 /// LAD subgraph isomorphism test.
7592 ///
7593 /// ```
7594 /// use rust_igraph::Graph;
7595 ///
7596 /// let target = Graph::from_edges(&[(0,1),(1,2),(2,0),(2,3)], false, None).unwrap();
7597 /// let pattern = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7598 /// let iso = target.subisomorphic_lad(&pattern, false, None).unwrap();
7599 /// assert!(iso.iso);
7600 /// ```
7601 pub fn subisomorphic_lad(
7602 &self,
7603 pattern: &Graph,
7604 induced: bool,
7605 domains: Option<&[Vec<u32>]>,
7606 ) -> IgraphResult<crate::LadSubisomorphism> {
7607 crate::subisomorphic_lad(pattern, self, domains, induced)
7608 }
7609
7610 /// Betweenness centrality for a subset of vertices.
7611 ///
7612 /// ```
7613 /// use rust_igraph::Graph;
7614 ///
7615 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7616 /// let bc = g.betweenness_subset(&[0, 1], &[2, 3]).unwrap();
7617 /// assert_eq!(bc.len(), 4);
7618 /// ```
7619 pub fn betweenness_subset(&self, sources: &[u32], targets: &[u32]) -> IgraphResult<Vec<f64>> {
7620 crate::betweenness_subset(self, sources, targets, self.is_directed())
7621 }
7622
7623 /// Edge betweenness centrality for a subset of vertices.
7624 ///
7625 /// ```
7626 /// use rust_igraph::Graph;
7627 ///
7628 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,3)], false, None).unwrap();
7629 /// let ebc = g.edge_betweenness_subset(&[0, 1], &[2, 3]).unwrap();
7630 /// assert_eq!(ebc.len(), 3);
7631 /// ```
7632 pub fn edge_betweenness_subset(
7633 &self,
7634 sources: &[u32],
7635 targets: &[u32],
7636 ) -> IgraphResult<Vec<f64>> {
7637 crate::edge_betweenness_subset(self, sources, targets, self.is_directed())
7638 }
7639
7640 /// Weighted edge betweenness community detection.
7641 ///
7642 /// ```
7643 /// use rust_igraph::Graph;
7644 ///
7645 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0),(2,3),(3,4),(4,5),(5,3)], false, None).unwrap();
7646 /// let w = vec![1.0; g.ecount() as usize];
7647 /// let result = g.edge_betweenness_community_weighted(&w).unwrap();
7648 /// assert!(!result.merges.is_empty());
7649 /// ```
7650 pub fn edge_betweenness_community_weighted(
7651 &self,
7652 weights: &[f64],
7653 ) -> IgraphResult<crate::EdgeBetweennessResult> {
7654 crate::edge_betweenness_community_weighted(self, weights)
7655 }
7656
7657 /// Modularity matrix B = A - k*k'/2m.
7658 ///
7659 /// ```
7660 /// use rust_igraph::Graph;
7661 ///
7662 /// let g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
7663 /// let b = g.modularity_matrix(None).unwrap();
7664 /// assert_eq!(b.len(), 3);
7665 /// ```
7666 pub fn modularity_matrix(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<Vec<f64>>> {
7667 crate::modularity_matrix(self, weights, 1.0, self.is_directed())
7668 }
7669
7670 /// Whether the graph is the same as another (structural equality).
7671 ///
7672 /// ```
7673 /// use rust_igraph::Graph;
7674 ///
7675 /// let g1 = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7676 /// let g2 = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7677 /// assert!(g1.is_same_graph(&g2));
7678 /// ```
7679 pub fn is_same_graph(&self, other: &Graph) -> bool {
7680 crate::is_same_graph(self, other)
7681 }
7682
7683 /// Mean distance (weighted).
7684 ///
7685 /// ```
7686 /// use rust_igraph::Graph;
7687 ///
7688 /// let g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7689 /// let w = vec![1.0, 2.0];
7690 /// let md = g.mean_distance_weighted(&w).unwrap();
7691 /// assert!(md.is_some());
7692 /// ```
7693 pub fn mean_distance_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
7694 crate::mean_distance_weighted(self, weights, self.is_directed(), true)
7695 }
7696
7697 // ---- Attribute system ----
7698
7699 /// Set a graph-level attribute.
7700 ///
7701 /// Overwrites any existing value with the same name.
7702 ///
7703 /// ```
7704 /// use rust_igraph::{Graph, AttributeValue};
7705 ///
7706 /// let mut g = Graph::with_vertices(0);
7707 /// g.set_graph_attribute("name", "test".into());
7708 /// assert_eq!(
7709 /// g.graph_attribute("name").and_then(|v| v.as_str()),
7710 /// Some("test"),
7711 /// );
7712 /// ```
7713 pub fn set_graph_attribute(&mut self, name: impl Into<String>, value: AttributeValue) {
7714 self.gattrs.insert(name.into(), value);
7715 }
7716
7717 /// Get a graph-level attribute by name.
7718 ///
7719 /// ```
7720 /// use rust_igraph::{Graph, AttributeValue};
7721 ///
7722 /// let g = Graph::with_vertices(0);
7723 /// assert!(g.graph_attribute("missing").is_none());
7724 /// ```
7725 #[must_use]
7726 pub fn graph_attribute(&self, name: &str) -> Option<&AttributeValue> {
7727 self.gattrs.get(name)
7728 }
7729
7730 /// Delete a graph-level attribute. Returns `true` if it existed.
7731 pub fn delete_graph_attribute(&mut self, name: &str) -> bool {
7732 self.gattrs.remove(name).is_some()
7733 }
7734
7735 /// Check whether a graph-level attribute exists.
7736 #[must_use]
7737 pub fn has_graph_attribute(&self, name: &str) -> bool {
7738 self.gattrs.contains_key(name)
7739 }
7740
7741 /// List all graph-level attribute names.
7742 ///
7743 /// ```
7744 /// use rust_igraph::{Graph, AttributeValue};
7745 ///
7746 /// let mut g = Graph::with_vertices(0);
7747 /// g.set_graph_attribute("name", "test".into());
7748 /// g.set_graph_attribute("year", 2024.0.into());
7749 /// let names = g.graph_attribute_names();
7750 /// assert!(names.contains(&"name"));
7751 /// assert!(names.contains(&"year"));
7752 /// ```
7753 #[must_use]
7754 pub fn graph_attribute_names(&self) -> Vec<&str> {
7755 self.gattrs.keys().map(String::as_str).collect()
7756 }
7757
7758 /// Set a vertex attribute for a single vertex.
7759 ///
7760 /// Creates the attribute vector if it doesn't exist. New entries
7761 /// for other vertices are filled with a type-appropriate default.
7762 ///
7763 /// ```
7764 /// use rust_igraph::{Graph, AttributeValue};
7765 ///
7766 /// let mut g = Graph::with_vertices(3);
7767 /// g.set_vertex_attribute("label", 0, "Alice".into()).unwrap();
7768 /// g.set_vertex_attribute("label", 1, "Bob".into()).unwrap();
7769 /// assert_eq!(
7770 /// g.vertex_attribute("label", 0).and_then(|v| v.as_str()),
7771 /// Some("Alice"),
7772 /// );
7773 /// ```
7774 pub fn set_vertex_attribute(
7775 &mut self,
7776 name: impl Into<String>,
7777 vertex: VertexId,
7778 value: AttributeValue,
7779 ) -> IgraphResult<()> {
7780 self.check_vertex(vertex)?;
7781 let n = self.n as usize;
7782 let key = name.into();
7783 let vals = self.vertex_attrs.entry(key).or_insert_with(|| {
7784 let default = value.default_for_same_type();
7785 vec![default; n]
7786 });
7787 vals[vertex as usize] = value;
7788 Ok(())
7789 }
7790
7791 /// Set a vertex attribute for all vertices at once.
7792 ///
7793 /// The `values` slice must have length equal to `vcount()`.
7794 ///
7795 /// ```
7796 /// use rust_igraph::{Graph, AttributeValue};
7797 ///
7798 /// let mut g = Graph::with_vertices(3);
7799 /// g.set_vertex_attribute_all(
7800 /// "color",
7801 /// vec![1.0.into(), 2.0.into(), 3.0.into()],
7802 /// ).unwrap();
7803 /// let colors = g.vertex_attributes("color").unwrap();
7804 /// assert_eq!(colors.len(), 3);
7805 /// ```
7806 pub fn set_vertex_attribute_all(
7807 &mut self,
7808 name: impl Into<String>,
7809 values: Vec<AttributeValue>,
7810 ) -> IgraphResult<()> {
7811 if values.len() != self.n as usize {
7812 return Err(IgraphError::InvalidArgument(format!(
7813 "attribute vector length {} does not match vcount {}",
7814 values.len(),
7815 self.n,
7816 )));
7817 }
7818 self.vertex_attrs.insert(name.into(), values);
7819 Ok(())
7820 }
7821
7822 /// Get a vertex attribute for a single vertex.
7823 #[must_use]
7824 pub fn vertex_attribute(&self, name: &str, vertex: VertexId) -> Option<&AttributeValue> {
7825 self.vertex_attrs
7826 .get(name)
7827 .and_then(|vals| vals.get(vertex as usize))
7828 }
7829
7830 /// Get the full vertex attribute vector by name.
7831 #[must_use]
7832 pub fn vertex_attributes(&self, name: &str) -> Option<&[AttributeValue]> {
7833 self.vertex_attrs.get(name).map(Vec::as_slice)
7834 }
7835
7836 /// Delete a vertex attribute. Returns `true` if it existed.
7837 pub fn delete_vertex_attribute(&mut self, name: &str) -> bool {
7838 self.vertex_attrs.remove(name).is_some()
7839 }
7840
7841 /// Check whether a vertex attribute exists.
7842 #[must_use]
7843 pub fn has_vertex_attribute(&self, name: &str) -> bool {
7844 self.vertex_attrs.contains_key(name)
7845 }
7846
7847 /// List all vertex attribute names.
7848 ///
7849 /// ```
7850 /// use rust_igraph::{Graph, AttributeValue};
7851 ///
7852 /// let mut g = Graph::with_vertices(2);
7853 /// g.set_vertex_attribute("name", 0, "A".into()).unwrap();
7854 /// assert!(g.vertex_attribute_names().contains(&"name"));
7855 /// ```
7856 #[must_use]
7857 pub fn vertex_attribute_names(&self) -> Vec<&str> {
7858 self.vertex_attrs.keys().map(String::as_str).collect()
7859 }
7860
7861 /// Set an edge attribute for a single edge.
7862 ///
7863 /// Creates the attribute vector if it doesn't exist. New entries
7864 /// for other edges are filled with a type-appropriate default.
7865 ///
7866 /// ```
7867 /// use rust_igraph::{Graph, AttributeValue};
7868 ///
7869 /// let mut g = Graph::from_edges(&[(0,1),(1,2)], false, None).unwrap();
7870 /// g.set_edge_attribute("weight", 0, 1.5.into()).unwrap();
7871 /// assert_eq!(
7872 /// g.edge_attribute("weight", 0).and_then(|v| v.as_f64()),
7873 /// Some(1.5),
7874 /// );
7875 /// ```
7876 pub fn set_edge_attribute(
7877 &mut self,
7878 name: impl Into<String>,
7879 edge: EdgeId,
7880 value: AttributeValue,
7881 ) -> IgraphResult<()> {
7882 let m = self.ecount();
7883 if (edge as usize) >= m {
7884 return Err(IgraphError::EdgeOutOfRange {
7885 id: edge,
7886 m: u32::try_from(m).unwrap_or(u32::MAX),
7887 });
7888 }
7889 let key = name.into();
7890 let vals = self.edge_attrs.entry(key).or_insert_with(|| {
7891 let default = value.default_for_same_type();
7892 vec![default; m]
7893 });
7894 vals[edge as usize] = value;
7895 Ok(())
7896 }
7897
7898 /// Set an edge attribute for all edges at once.
7899 ///
7900 /// The `values` slice must have length equal to `ecount()`.
7901 ///
7902 /// ```
7903 /// use rust_igraph::{Graph, AttributeValue};
7904 ///
7905 /// let mut g = Graph::from_edges(&[(0,1),(1,2),(2,0)], false, None).unwrap();
7906 /// g.set_edge_attribute_all(
7907 /// "weight",
7908 /// vec![1.0.into(), 2.0.into(), 3.0.into()],
7909 /// ).unwrap();
7910 /// let w = g.edge_attributes("weight").unwrap();
7911 /// assert_eq!(w.len(), 3);
7912 /// ```
7913 pub fn set_edge_attribute_all(
7914 &mut self,
7915 name: impl Into<String>,
7916 values: Vec<AttributeValue>,
7917 ) -> IgraphResult<()> {
7918 let m = self.ecount();
7919 if values.len() != m {
7920 return Err(IgraphError::InvalidArgument(format!(
7921 "attribute vector length {} does not match ecount {}",
7922 values.len(),
7923 m,
7924 )));
7925 }
7926 self.edge_attrs.insert(name.into(), values);
7927 Ok(())
7928 }
7929
7930 /// Get an edge attribute for a single edge.
7931 #[must_use]
7932 pub fn edge_attribute(&self, name: &str, edge: EdgeId) -> Option<&AttributeValue> {
7933 self.edge_attrs
7934 .get(name)
7935 .and_then(|vals| vals.get(edge as usize))
7936 }
7937
7938 /// Get the full edge attribute vector by name.
7939 #[must_use]
7940 pub fn edge_attributes(&self, name: &str) -> Option<&[AttributeValue]> {
7941 self.edge_attrs.get(name).map(Vec::as_slice)
7942 }
7943
7944 /// Delete an edge attribute. Returns `true` if it existed.
7945 pub fn delete_edge_attribute(&mut self, name: &str) -> bool {
7946 self.edge_attrs.remove(name).is_some()
7947 }
7948
7949 /// Check whether an edge attribute exists.
7950 #[must_use]
7951 pub fn has_edge_attribute(&self, name: &str) -> bool {
7952 self.edge_attrs.contains_key(name)
7953 }
7954
7955 /// List all edge attribute names.
7956 ///
7957 /// ```
7958 /// use rust_igraph::{Graph, AttributeValue};
7959 ///
7960 /// let mut g = Graph::from_edges(&[(0,1)], false, None).unwrap();
7961 /// g.set_edge_attribute("weight", 0, 1.0.into()).unwrap();
7962 /// assert!(g.edge_attribute_names().contains(&"weight"));
7963 /// ```
7964 #[must_use]
7965 pub fn edge_attribute_names(&self) -> Vec<&str> {
7966 self.edge_attrs.keys().map(String::as_str).collect()
7967 }
7968}
7969
7970impl std::fmt::Display for Graph {
7971 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7972 let kind = if self.directed {
7973 "Directed"
7974 } else {
7975 "Undirected"
7976 };
7977 write!(
7978 f,
7979 "{kind} graph with {} vertices and {} edges",
7980 self.n,
7981 self.ecount()
7982 )
7983 }
7984}
7985
7986/// Iterate over a graph's edges by reference.
7987///
7988/// Yields `(from, to)` pairs in edge-id order, enabling the idiomatic
7989/// `for (u, v) in &graph { ... }` pattern.
7990///
7991/// # Examples
7992///
7993/// ```
7994/// use rust_igraph::Graph;
7995///
7996/// let mut g = Graph::with_vertices(3);
7997/// g.add_edge(0, 1).unwrap();
7998/// g.add_edge(1, 2).unwrap();
7999///
8000/// let edges: Vec<_> = (&g).into_iter().collect();
8001/// assert_eq!(edges, vec![(0, 1), (1, 2)]);
8002/// ```
8003impl<'a> IntoIterator for &'a Graph {
8004 type Item = (VertexId, VertexId);
8005 type IntoIter = EdgeIter<'a>;
8006
8007 fn into_iter(self) -> Self::IntoIter {
8008 self.iter()
8009 }
8010}
8011
8012/// Construct an undirected graph from a slice of `(from, to)` edge pairs.
8013///
8014/// Vertex count is inferred from the maximum endpoint id (plus one).
8015/// This is a convenience for quick construction; for more control use
8016/// [`Graph::from_edges`] or [`GraphBuilder`](super::builder::GraphBuilder).
8017///
8018/// # Examples
8019///
8020/// ```
8021/// use rust_igraph::Graph;
8022///
8023/// let edges = vec![(0u32, 1), (1, 2), (2, 0)];
8024/// let g = Graph::try_from(edges.as_slice()).unwrap();
8025/// assert_eq!(g.vcount(), 3);
8026/// assert_eq!(g.ecount(), 3);
8027/// assert!(!g.is_directed());
8028/// ```
8029impl TryFrom<&[(VertexId, VertexId)]> for Graph {
8030 type Error = IgraphError;
8031
8032 fn try_from(edges: &[(VertexId, VertexId)]) -> IgraphResult<Self> {
8033 let n = match edges.iter().flat_map(|&(u, v)| [u, v]).max() {
8034 Some(m) => m
8035 .checked_add(1)
8036 .ok_or_else(|| IgraphError::InvalidArgument("vertex id overflow".to_owned()))?,
8037 None => 0,
8038 };
8039 let mut g = Self::new(n, false)?;
8040 g.add_edges(edges.to_vec())?;
8041 Ok(g)
8042 }
8043}
8044
8045/// Construct an undirected graph from a `Vec` of `(from, to)` edge pairs.
8046///
8047/// # Examples
8048///
8049/// ```
8050/// use rust_igraph::Graph;
8051///
8052/// let g = Graph::try_from(vec![(0u32, 1), (1, 2), (2, 3)]).unwrap();
8053/// assert_eq!(g.vcount(), 4);
8054/// assert_eq!(g.ecount(), 3);
8055/// ```
8056impl TryFrom<Vec<(VertexId, VertexId)>> for Graph {
8057 type Error = IgraphError;
8058
8059 fn try_from(edges: Vec<(VertexId, VertexId)>) -> IgraphResult<Self> {
8060 let n = match edges.iter().flat_map(|&(u, v)| [u, v]).max() {
8061 Some(m) => m
8062 .checked_add(1)
8063 .ok_or_else(|| IgraphError::InvalidArgument("vertex id overflow".to_owned()))?,
8064 None => 0,
8065 };
8066 let mut g = Self::new(n, false)?;
8067 g.add_edges(edges)?;
8068 Ok(g)
8069 }
8070}
8071
8072/// Collect edges from an iterator into an undirected graph.
8073///
8074/// Vertex count is inferred from the maximum endpoint id.
8075/// For directed graphs or explicit vertex counts, use [`Graph::from_edges`].
8076///
8077/// # Panics
8078///
8079/// Panics if a vertex id would overflow `u32::MAX`.
8080///
8081/// # Examples
8082///
8083/// ```
8084/// use rust_igraph::Graph;
8085///
8086/// let g: Graph = [(0u32, 1), (1, 2), (2, 0)].into_iter().collect();
8087/// assert_eq!(g.vcount(), 3);
8088/// assert_eq!(g.ecount(), 3);
8089/// ```
8090impl std::iter::FromIterator<(VertexId, VertexId)> for Graph {
8091 fn from_iter<I: IntoIterator<Item = (VertexId, VertexId)>>(iter: I) -> Self {
8092 let edges: Vec<(VertexId, VertexId)> = iter.into_iter().collect();
8093 Self::try_from(edges).expect("FromIterator: vertex id overflow or invalid edge")
8094 }
8095}
8096
8097/// Extend a graph by adding edges from an iterator.
8098///
8099/// New vertices are automatically created as needed. This enables
8100/// patterns like `graph.extend(new_edges)`.
8101///
8102/// # Panics
8103///
8104/// Panics if an edge endpoint exceeds the current vertex count and
8105/// cannot be added.
8106///
8107/// # Examples
8108///
8109/// ```
8110/// use rust_igraph::Graph;
8111///
8112/// let mut g = Graph::with_vertices(3);
8113/// g.extend([(0u32, 1), (1, 2)]);
8114/// assert_eq!(g.ecount(), 2);
8115///
8116/// // Extending with a vertex beyond current count grows the graph
8117/// g.extend([(2u32, 5)]);
8118/// assert_eq!(g.vcount(), 6);
8119/// assert_eq!(g.ecount(), 3);
8120/// ```
8121impl Extend<(VertexId, VertexId)> for Graph {
8122 fn extend<I: IntoIterator<Item = (VertexId, VertexId)>>(&mut self, iter: I) {
8123 let edges: Vec<(VertexId, VertexId)> = iter.into_iter().collect();
8124 if edges.is_empty() {
8125 return;
8126 }
8127 let max_id = edges
8128 .iter()
8129 .flat_map(|&(u, v)| [u, v])
8130 .max()
8131 .expect("non-empty edges");
8132 if max_id >= self.n {
8133 self.add_vertices(max_id - self.n + 1)
8134 .expect("Extend: failed to add vertices");
8135 }
8136 self.add_edges(edges).expect("Extend: failed to add edges");
8137 }
8138}
8139
8140/// Structural equality: two graphs are equal if they have the same
8141/// directedness, same vertex count, and the same sorted edge set.
8142///
8143/// This is *not* isomorphism — vertex ids must match exactly.
8144///
8145/// # Examples
8146///
8147/// ```
8148/// use rust_igraph::Graph;
8149///
8150/// let a = Graph::from_edges(&[(0,1), (1,2)], false, None).unwrap();
8151/// let b = Graph::from_edges(&[(1,2), (0,1)], false, None).unwrap();
8152/// assert_eq!(a, b); // same edges, different insertion order
8153///
8154/// let c = Graph::from_edges(&[(0,1), (1,2)], true, None).unwrap();
8155/// assert_ne!(a, c); // different directedness
8156/// ```
8157impl PartialEq for Graph {
8158 fn eq(&self, other: &Self) -> bool {
8159 if self.directed != other.directed || self.n != other.n || self.ecount() != other.ecount() {
8160 return false;
8161 }
8162 let mut self_edges: Vec<(VertexId, VertexId)> = self.iter().collect();
8163 let mut other_edges: Vec<(VertexId, VertexId)> = other.iter().collect();
8164 self_edges.sort_unstable();
8165 other_edges.sort_unstable();
8166 self_edges == other_edges
8167 }
8168}
8169
8170impl Eq for Graph {}
8171
8172/// Hash a graph by its structural content (directedness + vertex count +
8173/// sorted edge set).
8174///
8175/// This is consistent with the [`PartialEq`] impl: structurally equal
8176/// graphs produce the same hash.
8177impl std::hash::Hash for Graph {
8178 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
8179 self.directed.hash(state);
8180 self.n.hash(state);
8181 let mut edges: Vec<(VertexId, VertexId)> = self.iter().collect();
8182 edges.sort_unstable();
8183 edges.hash(state);
8184 }
8185}
8186
8187#[cfg(test)]
8188mod tests {
8189 use super::*;
8190
8191 #[test]
8192 fn empty_graph_counts() {
8193 let g = Graph::with_vertices(0);
8194 assert_eq!(g.vcount(), 0);
8195 assert_eq!(g.ecount(), 0);
8196 assert!(!g.is_directed());
8197 }
8198
8199 #[test]
8200 fn new_directed_flag() {
8201 let g = Graph::new(3, true).unwrap();
8202 assert!(g.is_directed());
8203 let g = Graph::new(3, false).unwrap();
8204 assert!(!g.is_directed());
8205 }
8206
8207 #[test]
8208 fn add_vertices_then_edges() {
8209 let mut g = Graph::with_vertices(3);
8210 g.add_edge(0, 1).unwrap();
8211 g.add_edge(1, 2).unwrap();
8212 assert_eq!(g.vcount(), 3);
8213 assert_eq!(g.ecount(), 2);
8214 assert_eq!(g.degree(1).unwrap(), 2);
8215 let mut nbrs = g.neighbors(1).unwrap();
8216 nbrs.sort_unstable();
8217 assert_eq!(nbrs, vec![0, 2]);
8218 }
8219
8220 #[test]
8221 fn out_of_range_vertex_errors() {
8222 let mut g = Graph::with_vertices(2);
8223 let err = g.add_edge(0, 5).unwrap_err();
8224 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 2 }));
8225 }
8226
8227 #[test]
8228 fn self_loop_counted_correctly() {
8229 let mut g = Graph::with_vertices(1);
8230 g.add_edge(0, 0).unwrap();
8231 assert_eq!(g.ecount(), 1);
8232 // Undirected self-loop: appears as both out and in, degree == 2.
8233 assert_eq!(g.degree(0).unwrap(), 2);
8234 let mut nbrs = g.neighbors(0).unwrap();
8235 nbrs.sort_unstable();
8236 assert_eq!(nbrs, vec![0, 0]);
8237 }
8238
8239 #[test]
8240 fn parallel_edges() {
8241 let mut g = Graph::with_vertices(2);
8242 g.add_edge(0, 1).unwrap();
8243 g.add_edge(0, 1).unwrap();
8244 assert_eq!(g.ecount(), 2);
8245 assert_eq!(g.degree(0).unwrap(), 2);
8246 assert_eq!(g.degree(1).unwrap(), 2);
8247 }
8248
8249 #[test]
8250 fn undirected_canonicalisation() {
8251 // Adding edges (1,0) and (0,1) — both stored canonically as (0,1).
8252 let mut g = Graph::with_vertices(2);
8253 g.add_edge(1, 0).unwrap();
8254 g.add_edge(0, 1).unwrap();
8255 assert_eq!(g.ecount(), 2);
8256 // Both vertices see each other as a neighbour twice.
8257 let mut n0 = g.neighbors(0).unwrap();
8258 let mut n1 = g.neighbors(1).unwrap();
8259 n0.sort_unstable();
8260 n1.sort_unstable();
8261 assert_eq!(n0, vec![1, 1]);
8262 assert_eq!(n1, vec![0, 0]);
8263 }
8264
8265 #[test]
8266 fn directed_neighbors_are_outgoing_only() {
8267 let mut g = Graph::new(3, true).unwrap();
8268 g.add_edge(0, 1).unwrap();
8269 g.add_edge(2, 0).unwrap();
8270 // Directed: neighbors(0) returns out-neighbours only.
8271 assert_eq!(g.neighbors(0).unwrap(), vec![1]);
8272 // Vertex 2 has out-edge to 0.
8273 assert_eq!(g.neighbors(2).unwrap(), vec![0]);
8274 // Vertex 1 has no out-edges.
8275 assert!(g.neighbors(1).unwrap().is_empty());
8276 // Degree counts both in and out for directed.
8277 assert_eq!(g.degree(0).unwrap(), 2); // out: 0->1, in: 2->0
8278 assert_eq!(g.degree(1).unwrap(), 1); // in: 0->1
8279 assert_eq!(g.degree(2).unwrap(), 1); // out: 2->0
8280 }
8281
8282 #[test]
8283 fn add_edges_batch_then_rebuild() {
8284 let mut g = Graph::with_vertices(4);
8285 g.add_edges(vec![(0, 1), (0, 2), (1, 2), (2, 3)]).unwrap();
8286 assert_eq!(g.ecount(), 4);
8287 // Degrees: 0->{1,2} d=2; 1->{0,2} d=2; 2->{0,1,3} d=3; 3->{2} d=1.
8288 assert_eq!(g.degree(0).unwrap(), 2);
8289 assert_eq!(g.degree(1).unwrap(), 2);
8290 assert_eq!(g.degree(2).unwrap(), 3);
8291 assert_eq!(g.degree(3).unwrap(), 1);
8292 }
8293
8294 #[test]
8295 fn clone_is_deep() {
8296 let mut g = Graph::with_vertices(3);
8297 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8298 let g2 = g.clone();
8299 // Mutate g; g2 must be unaffected.
8300 g.add_edge(0, 2).unwrap();
8301 assert_eq!(g.ecount(), 3);
8302 assert_eq!(g2.ecount(), 2);
8303 }
8304
8305 #[test]
8306 fn os_invariant_is_monotone() {
8307 let mut g = Graph::with_vertices(5);
8308 g.add_edges(vec![(0, 1), (0, 2), (3, 4), (1, 2)]).unwrap();
8309 // os should be non-decreasing and end at ecount.
8310 for w in g.os.windows(2) {
8311 assert!(w[0] <= w[1]);
8312 }
8313 assert_eq!(g.os[0], 0);
8314 assert_eq!(*g.os.last().unwrap() as usize, g.ecount());
8315 }
8316
8317 #[test]
8318 fn vertex_out_of_range_when_adding_edge() {
8319 let mut g = Graph::with_vertices(2);
8320 let e = g.add_edge(2, 0).unwrap_err();
8321 assert!(matches!(e, IgraphError::VertexOutOfRange { id: 2, n: 2 }));
8322 // Graph state must be unchanged after the failed add.
8323 assert_eq!(g.ecount(), 0);
8324 }
8325
8326 // -------- ALGO-CORE-001b: edge-id helpers + incident --------
8327
8328 #[test]
8329 fn edge_endpoints_round_trip() {
8330 let mut g = Graph::new(3, true).unwrap();
8331 g.add_edges(vec![(0, 1), (2, 0), (1, 2)]).unwrap();
8332 // Directed: order preserved. edge_id == position in from/to.
8333 assert_eq!(g.edge(0).unwrap(), (0, 1));
8334 assert_eq!(g.edge(1).unwrap(), (2, 0));
8335 assert_eq!(g.edge(2).unwrap(), (1, 2));
8336 assert_eq!(g.edge_source(1).unwrap(), 2);
8337 assert_eq!(g.edge_target(1).unwrap(), 0);
8338 }
8339
8340 #[test]
8341 fn edge_other_endpoint() {
8342 let mut g = Graph::with_vertices(3);
8343 g.add_edge(0, 2).unwrap();
8344 assert_eq!(g.edge_other(0, 0).unwrap(), 2);
8345 assert_eq!(g.edge_other(0, 2).unwrap(), 0);
8346 // Vertex not on the edge: error.
8347 let err = g.edge_other(0, 1).unwrap_err();
8348 assert!(matches!(err, IgraphError::InvalidArgument(_)));
8349 }
8350
8351 #[test]
8352 fn edge_out_of_range() {
8353 let mut g = Graph::with_vertices(2);
8354 g.add_edge(0, 1).unwrap();
8355 let err = g.edge(5).unwrap_err();
8356 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 1 }));
8357 }
8358
8359 #[test]
8360 fn incident_returns_edge_ids_matching_neighbors_order() {
8361 let mut g = Graph::with_vertices(4);
8362 g.add_edges(vec![(0, 1), (0, 2), (3, 0)]).unwrap();
8363 let eids = g.incident(0).unwrap();
8364 // Expect three incident edges; resolving back to neighbours
8365 // must equal `neighbors(0)` exactly (same iteration order).
8366 let resolved: Vec<u32> = eids.iter().map(|&e| g.edge_other(e, 0).unwrap()).collect();
8367 assert_eq!(resolved, g.neighbors(0).unwrap());
8368 }
8369
8370 #[test]
8371 fn incident_self_loop_appears_twice_undirected() {
8372 let mut g = Graph::with_vertices(1);
8373 g.add_edge(0, 0).unwrap();
8374 let eids = g.incident(0).unwrap();
8375 // Undirected self-loop appears once on the out side and once on
8376 // the in side — same edge id, twice. Mirrors `neighbors`.
8377 assert_eq!(eids, vec![0, 0]);
8378 assert_eq!(g.degree(0).unwrap(), 2);
8379 }
8380
8381 #[test]
8382 fn incident_directed_returns_outgoing_only() {
8383 let mut g = Graph::new(3, true).unwrap();
8384 g.add_edges(vec![(0, 1), (2, 0)]).unwrap();
8385 // Directed `incident` mirrors directed `neighbors` (out only).
8386 assert_eq!(g.incident(0).unwrap(), vec![0]);
8387 assert_eq!(g.incident(2).unwrap(), vec![1]);
8388 assert!(g.incident(1).unwrap().is_empty());
8389 }
8390
8391 #[test]
8392 fn get_eid_undirected_finds_edge_either_way() {
8393 let mut g = Graph::with_vertices(3);
8394 g.add_edge(0, 1).unwrap(); // edge 0
8395 g.add_edge(1, 2).unwrap(); // edge 1
8396 assert_eq!(g.get_eid(0, 1).unwrap(), 0);
8397 assert_eq!(g.get_eid(1, 0).unwrap(), 0);
8398 assert_eq!(g.get_eid(1, 2).unwrap(), 1);
8399 assert_eq!(g.get_eid(2, 1).unwrap(), 1);
8400 }
8401
8402 #[test]
8403 fn get_eid_directed_respects_direction() {
8404 let mut g = Graph::new(3, true).unwrap();
8405 g.add_edge(0, 1).unwrap(); // edge 0
8406 assert_eq!(g.get_eid(0, 1).unwrap(), 0);
8407 assert!(g.get_eid(1, 0).is_err()); // reverse direction has no edge
8408 }
8409
8410 #[test]
8411 fn find_eid_returns_none_for_missing() {
8412 let mut g = Graph::with_vertices(3);
8413 g.add_edge(0, 1).unwrap();
8414 assert_eq!(g.find_eid(0, 2).unwrap(), None);
8415 assert!(g.find_eid(0, 99).is_err()); // out-of-range vertex
8416 }
8417
8418 #[test]
8419 fn get_eid_self_loop() {
8420 let mut g = Graph::with_vertices(2);
8421 g.add_edge(0, 0).unwrap(); // self-loop, edge 0
8422 g.add_edge(0, 1).unwrap(); // edge 1
8423 assert_eq!(g.get_eid(0, 0).unwrap(), 0);
8424 assert_eq!(g.get_eid(0, 1).unwrap(), 1);
8425 }
8426
8427 #[test]
8428 fn get_all_eids_between_returns_all_parallel() {
8429 let mut g = Graph::with_vertices(2);
8430 g.add_edge(0, 1).unwrap(); // edge 0
8431 g.add_edge(0, 1).unwrap(); // edge 1
8432 g.add_edge(0, 1).unwrap(); // edge 2
8433 let eids = g.get_all_eids_between(0, 1).unwrap();
8434 assert_eq!(eids, vec![0, 1, 2]);
8435 // Reverse direction yields the same edges on undirected.
8436 let eids = g.get_all_eids_between(1, 0).unwrap();
8437 assert_eq!(eids, vec![0, 1, 2]);
8438 }
8439
8440 #[test]
8441 fn get_all_eids_between_directed_one_way_only() {
8442 let mut g = Graph::new(2, true).unwrap();
8443 g.add_edge(0, 1).unwrap(); // edge 0
8444 g.add_edge(0, 1).unwrap(); // edge 1 (parallel)
8445 assert_eq!(g.get_all_eids_between(0, 1).unwrap(), vec![0, 1]);
8446 // Reverse direction has no edges in directed graph.
8447 assert_eq!(g.get_all_eids_between(1, 0).unwrap(), Vec::<EdgeId>::new());
8448 }
8449
8450 #[test]
8451 fn get_eid_returns_lowest_id_for_parallel() {
8452 // Spec: with multiple edges, get_eid always returns the same
8453 // edge id (matches upstream's "ignored multi-edges" guarantee).
8454 // Our impl returns the lowest from the bucket.
8455 let mut g = Graph::with_vertices(2);
8456 g.add_edge(0, 1).unwrap(); // edge 0
8457 g.add_edge(0, 1).unwrap(); // edge 1
8458 assert_eq!(g.get_eid(0, 1).unwrap(), 0);
8459 }
8460
8461 // -------- ALGO-CORE-001c: delete_edges + delete_vertices --------
8462
8463 #[test]
8464 fn delete_edges_empty_input_is_noop() {
8465 let mut g = Graph::with_vertices(3);
8466 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8467 g.delete_edges(&[]).unwrap();
8468 assert_eq!(g.ecount(), 2);
8469 assert_eq!(g.degree(1).unwrap(), 2);
8470 }
8471
8472 #[test]
8473 fn delete_edges_single_edge_undirected() {
8474 let mut g = Graph::with_vertices(3);
8475 g.add_edges(vec![(0, 1), (1, 2), (0, 2)]).unwrap();
8476 // Remove edge id 1 (the (1,2) edge).
8477 g.delete_edges(&[1]).unwrap();
8478 assert_eq!(g.ecount(), 2);
8479 // Surviving edges renumbered to 0,1: (0,1) and (0,2).
8480 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
8481 assert_eq!(g.find_eid(0, 2).unwrap(), Some(1));
8482 assert_eq!(g.find_eid(1, 2).unwrap(), None);
8483 // Degrees consistent post-rebuild.
8484 assert_eq!(g.degree(1).unwrap(), 1);
8485 assert_eq!(g.degree(2).unwrap(), 1);
8486 }
8487
8488 #[test]
8489 fn delete_edges_duplicate_ids_tolerated() {
8490 let mut g = Graph::with_vertices(3);
8491 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8492 g.delete_edges(&[0, 0, 0]).unwrap();
8493 assert_eq!(g.ecount(), 1);
8494 assert_eq!(g.find_eid(1, 2).unwrap(), Some(0));
8495 }
8496
8497 #[test]
8498 fn delete_edges_all_edges_leaves_isolated_vertices() {
8499 let mut g = Graph::with_vertices(3);
8500 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8501 g.delete_edges(&[0, 1]).unwrap();
8502 assert_eq!(g.ecount(), 0);
8503 assert_eq!(g.vcount(), 3);
8504 for v in 0..3 {
8505 assert_eq!(g.degree(v).unwrap(), 0);
8506 }
8507 }
8508
8509 #[test]
8510 fn delete_edges_out_of_range_errors_and_preserves_state() {
8511 let mut g = Graph::with_vertices(3);
8512 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8513 let err = g.delete_edges(&[5]).unwrap_err();
8514 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 2 }));
8515 // Graph unchanged.
8516 assert_eq!(g.ecount(), 2);
8517 }
8518
8519 #[test]
8520 fn delete_edges_self_loop_directed() {
8521 let mut g = Graph::new(2, true).unwrap();
8522 g.add_edges(vec![(0, 0), (0, 1)]).unwrap();
8523 g.delete_edges(&[0]).unwrap(); // remove the self-loop
8524 assert_eq!(g.ecount(), 1);
8525 assert_eq!(g.degree(0).unwrap(), 1);
8526 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
8527 }
8528
8529 #[test]
8530 fn delete_vertices_empty_input_is_noop() {
8531 let mut g = Graph::with_vertices(3);
8532 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8533 g.delete_vertices(&[]).unwrap();
8534 assert_eq!(g.vcount(), 3);
8535 assert_eq!(g.ecount(), 2);
8536 }
8537
8538 #[test]
8539 fn delete_vertices_single_renumbers() {
8540 let mut g = Graph::with_vertices(4);
8541 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (0, 3)]).unwrap();
8542 // Remove vertex 1: edges (0,1) and (1,2) go with it. (2,3),(0,3)
8543 // survive but get renumbered: 2 → 1, 3 → 2.
8544 g.delete_vertices(&[1]).unwrap();
8545 assert_eq!(g.vcount(), 3);
8546 assert_eq!(g.ecount(), 2);
8547 // (2,3) → (1,2); (0,3) → (0,2).
8548 assert!(g.find_eid(1, 2).unwrap().is_some());
8549 assert!(g.find_eid(0, 2).unwrap().is_some());
8550 assert_eq!(g.find_eid(0, 1).unwrap(), None);
8551 }
8552
8553 #[test]
8554 fn delete_vertices_duplicate_ids_tolerated() {
8555 let mut g = Graph::with_vertices(3);
8556 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8557 g.delete_vertices(&[1, 1, 1]).unwrap();
8558 assert_eq!(g.vcount(), 2);
8559 assert_eq!(g.ecount(), 0);
8560 }
8561
8562 #[test]
8563 fn delete_vertices_all_yields_empty_graph() {
8564 let mut g = Graph::with_vertices(3);
8565 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8566 g.delete_vertices(&[0, 1, 2]).unwrap();
8567 assert_eq!(g.vcount(), 0);
8568 assert_eq!(g.ecount(), 0);
8569 }
8570
8571 #[test]
8572 fn delete_vertices_out_of_range_errors_and_preserves_state() {
8573 let mut g = Graph::with_vertices(3);
8574 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
8575 let err = g.delete_vertices(&[5]).unwrap_err();
8576 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 3 }));
8577 assert_eq!(g.vcount(), 3);
8578 assert_eq!(g.ecount(), 2);
8579 }
8580
8581 #[test]
8582 fn delete_vertices_map_returns_correct_mappings() {
8583 let mut g = Graph::with_vertices(5);
8584 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (3, 4)]).unwrap();
8585 let (map, invmap) = g.delete_vertices_map(&[1, 3]).unwrap();
8586 // Removed: 1 and 3. Retained: 0 → 0, 2 → 1, 4 → 2.
8587 assert_eq!(map, vec![Some(0), None, Some(1), None, Some(2)]);
8588 assert_eq!(invmap, vec![0, 2, 4]);
8589 assert_eq!(g.vcount(), 3);
8590 // Only edges between retained vertices survive — none do here.
8591 assert_eq!(g.ecount(), 0);
8592 }
8593
8594 #[test]
8595 fn delete_vertices_directed_preserves_direction() {
8596 let mut g = Graph::new(4, true).unwrap();
8597 g.add_edges(vec![(0, 1), (1, 2), (2, 0), (3, 0)]).unwrap();
8598 g.delete_vertices(&[3]).unwrap();
8599 assert_eq!(g.vcount(), 3);
8600 assert!(g.is_directed());
8601 // Surviving directed edges (3 → 0) gone; (0,1),(1,2),(2,0) keep direction.
8602 assert!(g.get_eid(0, 1).is_ok());
8603 assert!(g.get_eid(1, 0).is_err()); // wrong direction
8604 }
8605
8606 #[test]
8607 fn delete_vertices_self_loop_on_removed_vertex() {
8608 let mut g = Graph::with_vertices(3);
8609 g.add_edges(vec![(0, 0), (0, 1), (1, 2)]).unwrap();
8610 g.delete_vertices(&[0]).unwrap();
8611 // Self-loop and edges to vertex 0 gone; only (1,2) → (0,1) survives.
8612 assert_eq!(g.vcount(), 2);
8613 assert_eq!(g.ecount(), 1);
8614 assert!(g.find_eid(0, 1).unwrap().is_some());
8615 }
8616
8617 #[test]
8618 fn delete_vertices_preserves_parallel_edges() {
8619 let mut g = Graph::with_vertices(3);
8620 g.add_edges(vec![(0, 1), (0, 1), (1, 2)]).unwrap();
8621 g.delete_vertices(&[2]).unwrap();
8622 assert_eq!(g.vcount(), 2);
8623 assert_eq!(g.ecount(), 2); // both parallel (0,1) edges retained
8624 assert_eq!(g.degree(0).unwrap(), 2);
8625 assert_eq!(g.degree(1).unwrap(), 2);
8626 }
8627
8628 #[test]
8629 fn add_edges_after_delete_works() {
8630 let mut g = Graph::with_vertices(4);
8631 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
8632 g.delete_vertices(&[0]).unwrap(); // now n=3, vertices 0,1,2
8633 // Add a new edge and check indexes still work.
8634 g.add_edge(0, 2).unwrap();
8635 assert_eq!(g.ecount(), 3);
8636 assert_eq!(g.degree(0).unwrap(), 2); // (0,1)+(0,2)
8637 assert!(g.find_eid(0, 2).unwrap().is_some());
8638 }
8639
8640 #[test]
8641 fn from_adjacency_matrix_undirected_triangle() {
8642 let adj = vec![
8643 vec![0.0, 1.0, 1.0],
8644 vec![1.0, 0.0, 1.0],
8645 vec![1.0, 1.0, 0.0],
8646 ];
8647 let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
8648 assert_eq!(g.vcount(), 3);
8649 assert_eq!(g.ecount(), 3);
8650 assert!(!g.is_directed());
8651 }
8652
8653 #[test]
8654 fn from_adjacency_matrix_directed() {
8655 let adj = vec![
8656 vec![0.0, 1.0, 0.0],
8657 vec![0.0, 0.0, 1.0],
8658 vec![1.0, 0.0, 0.0],
8659 ];
8660 let g = Graph::from_adjacency_matrix(&adj, true).unwrap();
8661 assert_eq!(g.vcount(), 3);
8662 assert_eq!(g.ecount(), 3);
8663 assert!(g.is_directed());
8664 }
8665
8666 #[test]
8667 fn from_adjacency_matrix_with_self_loop() {
8668 let adj = vec![vec![1.0, 1.0], vec![1.0, 0.0]];
8669 let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
8670 assert_eq!(g.vcount(), 2);
8671 assert_eq!(g.ecount(), 2); // self-loop on 0 + edge 0-1
8672 }
8673
8674 #[test]
8675 fn from_adjacency_matrix_empty() {
8676 let adj: Vec<Vec<f64>> = Vec::new();
8677 let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
8678 assert_eq!(g.vcount(), 0);
8679 assert_eq!(g.ecount(), 0);
8680 }
8681
8682 #[test]
8683 fn from_adjacency_matrix_non_square_error() {
8684 let adj = vec![vec![0.0, 1.0], vec![1.0, 0.0, 1.0]];
8685 assert!(Graph::from_adjacency_matrix(&adj, false).is_err());
8686 }
8687
8688 #[test]
8689 fn from_adjacency_matrix_weighted_basic() {
8690 let adj = vec![
8691 vec![0.0, 2.5, 0.0],
8692 vec![2.5, 0.0, 1.0],
8693 vec![0.0, 1.0, 0.0],
8694 ];
8695 let (g, weights) = Graph::from_adjacency_matrix_weighted(&adj, false).unwrap();
8696 assert_eq!(g.vcount(), 3);
8697 assert_eq!(g.ecount(), 2);
8698 assert_eq!(weights.len(), 2);
8699 assert!((weights[0] - 2.5).abs() < 1e-10);
8700 assert!((weights[1] - 1.0).abs() < 1e-10);
8701 }
8702
8703 #[test]
8704 fn from_adjacency_matrix_weighted_directed() {
8705 let adj = vec![
8706 vec![0.0, 3.0, 0.0],
8707 vec![0.0, 0.0, 2.0],
8708 vec![1.5, 0.0, 0.0],
8709 ];
8710 let (g, weights) = Graph::from_adjacency_matrix_weighted(&adj, true).unwrap();
8711 assert_eq!(g.vcount(), 3);
8712 assert_eq!(g.ecount(), 3);
8713 assert_eq!(weights.len(), 3);
8714 assert!((weights[0] - 3.0).abs() < 1e-10);
8715 assert!((weights[1] - 2.0).abs() < 1e-10);
8716 assert!((weights[2] - 1.5).abs() < 1e-10);
8717 }
8718
8719 #[test]
8720 fn from_adjacency_matrix_multi_edges() {
8721 let adj = vec![vec![0.0, 3.0], vec![3.0, 0.0]];
8722 let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
8723 assert_eq!(g.vcount(), 2);
8724 assert_eq!(g.ecount(), 3); // 3 parallel edges
8725 }
8726
8727 #[test]
8728 fn from_adjacency_list_undirected_triangle() {
8729 let adj = vec![vec![1, 2], vec![0, 2], vec![0, 1]];
8730 let g = Graph::from_adjacency_list(&adj, false).unwrap();
8731 assert_eq!(g.vcount(), 3);
8732 assert_eq!(g.ecount(), 3);
8733 assert!(!g.is_directed());
8734 }
8735
8736 #[test]
8737 fn from_adjacency_list_directed() {
8738 let adj = vec![vec![1, 2], vec![2], vec![]];
8739 let g = Graph::from_adjacency_list(&adj, true).unwrap();
8740 assert_eq!(g.vcount(), 3);
8741 assert_eq!(g.ecount(), 3);
8742 assert!(g.is_directed());
8743 }
8744
8745 #[test]
8746 fn from_adjacency_list_empty() {
8747 let adj: Vec<Vec<u32>> = Vec::new();
8748 let g = Graph::from_adjacency_list(&adj, false).unwrap();
8749 assert_eq!(g.vcount(), 0);
8750 assert_eq!(g.ecount(), 0);
8751 }
8752
8753 #[test]
8754 fn from_adjacency_list_isolated_vertices() {
8755 let adj = vec![vec![], vec![], vec![]];
8756 let g = Graph::from_adjacency_list(&adj, false).unwrap();
8757 assert_eq!(g.vcount(), 3);
8758 assert_eq!(g.ecount(), 0);
8759 }
8760
8761 #[test]
8762 fn from_adjacency_list_self_loop() {
8763 let adj = vec![vec![0, 1], vec![0]];
8764 let g = Graph::from_adjacency_list(&adj, false).unwrap();
8765 assert_eq!(g.vcount(), 2);
8766 assert_eq!(g.ecount(), 2); // self-loop on 0 + edge 0-1
8767 }
8768
8769 #[test]
8770 fn from_adjacency_list_out_of_range_error() {
8771 let adj = vec![vec![5]]; // only 1 vertex but references vertex 5
8772 assert!(Graph::from_adjacency_list(&adj, false).is_err());
8773 }
8774
8775 #[test]
8776 fn neighbors_iter_matches_neighbors_undirected() {
8777 let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 3), (2, 3), (3, 4)], false, None).unwrap();
8778 for v in 0..g.vcount() {
8779 let from_vec = g.neighbors(v).unwrap();
8780 let from_iter: Vec<VertexId> = g.neighbors_iter(v).unwrap().collect();
8781 assert_eq!(from_vec, from_iter, "mismatch at vertex {v}");
8782 }
8783 }
8784
8785 #[test]
8786 fn neighbors_iter_matches_neighbors_directed() {
8787 let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 3), (2, 3), (3, 4)], true, None).unwrap();
8788 for v in 0..g.vcount() {
8789 let from_vec = g.neighbors(v).unwrap();
8790 let from_iter: Vec<VertexId> = g.neighbors_iter(v).unwrap().collect();
8791 assert_eq!(from_vec, from_iter, "mismatch at vertex {v}");
8792 }
8793 }
8794
8795 #[test]
8796 fn neighbors_iter_exact_size() {
8797 let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], false, None).unwrap();
8798 let iter = g.neighbors_iter(0).unwrap();
8799 assert_eq!(iter.len(), 3);
8800 }
8801
8802 #[test]
8803 fn neighbors_iter_invalid_vertex() {
8804 let g = Graph::with_vertices(3);
8805 assert!(g.neighbors_iter(5).is_err());
8806 }
8807}