1use rand::prelude::*;
10use std::collections::HashSet;
11
12use crate::base::{DiGraph, Graph};
13use crate::error::{GraphError, Result};
14use rand::seq::SliceRandom;
15
16#[allow(dead_code)]
18pub fn create_graph<N: crate::base::Node + std::fmt::Debug, E: crate::base::EdgeWeight>(
19) -> Graph<N, E> {
20 Graph::new()
21}
22
23#[allow(dead_code)]
25pub fn create_digraph<N: crate::base::Node + std::fmt::Debug, E: crate::base::EdgeWeight>(
26) -> DiGraph<N, E> {
27 DiGraph::new()
28}
29
30#[allow(dead_code)]
40pub fn erdos_renyi_graph<R: Rng>(n: usize, p: f64, rng: &mut R) -> Result<Graph<usize, f64>> {
41 if !(0.0..=1.0).contains(&p) {
42 return Err(GraphError::InvalidGraph(
43 "Probability must be between 0 and 1".to_string(),
44 ));
45 }
46
47 let mut graph = Graph::new();
48
49 for i in 0..n {
51 graph.add_node(i);
52 }
53
54 for i in 0..n {
56 for j in i + 1..n {
57 if rng.random::<f64>() < p {
58 graph.add_edge(i, j, 1.0)?;
59 }
60 }
61 }
62
63 Ok(graph)
64}
65
66#[allow(dead_code)]
76pub fn barabasi_albert_graph<R: Rng>(n: usize, m: usize, rng: &mut R) -> Result<Graph<usize, f64>> {
77 if m >= n {
78 return Err(GraphError::InvalidGraph(
79 "m must be less than n".to_string(),
80 ));
81 }
82 if m == 0 {
83 return Err(GraphError::InvalidGraph("m must be positive".to_string()));
84 }
85
86 let mut graph = Graph::new();
87
88 for i in 0..=m {
90 graph.add_node(i);
91 }
92
93 for i in 0..=m {
94 for j in i + 1..=m {
95 graph.add_edge(i, j, 1.0)?;
96 }
97 }
98
99 let mut degrees = vec![m; m + 1];
101 let mut total_degree = m * (m + 1);
102
103 for new_node in (m + 1)..n {
105 graph.add_node(new_node);
106
107 let mut targets = HashSet::new();
108
109 while targets.len() < m {
111 let mut cumulative_prob = 0.0;
112 let random_value = rng.random::<f64>() * total_degree as f64;
113
114 for (node_id, °ree) in degrees.iter().enumerate() {
115 cumulative_prob += degree as f64;
116 if random_value <= cumulative_prob && !targets.contains(&node_id) {
117 targets.insert(node_id);
118 break;
119 }
120 }
121 }
122
123 for &target in &targets {
125 graph.add_edge(new_node, target, 1.0)?;
126 degrees[target] += 1;
127 total_degree += 2; }
129
130 degrees.push(m); }
132
133 Ok(graph)
134}
135
136#[allow(dead_code)]
144pub fn complete_graph(n: usize) -> Result<Graph<usize, f64>> {
145 let mut graph = Graph::new();
146
147 for i in 0..n {
149 graph.add_node(i);
150 }
151
152 for i in 0..n {
154 for j in i + 1..n {
155 graph.add_edge(i, j, 1.0)?;
156 }
157 }
158
159 Ok(graph)
160}
161
162#[allow(dead_code)]
170pub fn star_graph(n: usize) -> Result<Graph<usize, f64>> {
171 if n == 0 {
172 return Err(GraphError::InvalidGraph(
173 "Star graph must have at least 1 node".to_string(),
174 ));
175 }
176
177 let mut graph = Graph::new();
178
179 for i in 0..n {
181 graph.add_node(i);
182 }
183
184 for i in 1..n {
186 graph.add_edge(0, i, 1.0)?;
187 }
188
189 Ok(graph)
190}
191
192#[allow(dead_code)]
200pub fn path_graph(n: usize) -> Result<Graph<usize, f64>> {
201 let mut graph = Graph::new();
202
203 for i in 0..n {
205 graph.add_node(i);
206 }
207
208 for i in 0..n.saturating_sub(1) {
210 graph.add_edge(i, i + 1, 1.0)?;
211 }
212
213 Ok(graph)
214}
215
216#[allow(dead_code)]
228pub fn tree_graph<R: Rng>(n: usize, rng: &mut R) -> Result<Graph<usize, f64>> {
229 if n == 0 {
230 return Ok(Graph::new());
231 }
232 if n == 1 {
233 let mut graph = Graph::new();
234 graph.add_node(0);
235 return Ok(graph);
236 }
237
238 let mut graph = Graph::new();
239
240 for i in 0..n {
242 graph.add_node(i);
243 }
244
245 let mut in_tree = vec![false; n];
247 let mut tree_nodes = Vec::new();
248
249 let start = rng.gen_range(0..n);
251 in_tree[start] = true;
252 tree_nodes.push(start);
253
254 for _ in 1..n {
256 let tree_node = tree_nodes[rng.gen_range(0..tree_nodes.len())];
258
259 let candidates: Vec<usize> = (0..n).filter(|&i| !in_tree[i]).collect();
261 if candidates.is_empty() {
262 break;
263 }
264
265 let new_node = candidates[rng.gen_range(0..candidates.len())];
266
267 graph.add_edge(tree_node, new_node, 1.0)?;
269 in_tree[new_node] = true;
270 tree_nodes.push(new_node);
271 }
272
273 Ok(graph)
274}
275
276#[allow(dead_code)]
288pub fn random_spanning_tree<N, E, Ix, R>(
289 graph: &Graph<N, E, Ix>,
290 rng: &mut R,
291) -> Result<Graph<N, E, Ix>>
292where
293 N: crate::base::Node + std::fmt::Debug,
294 E: crate::base::EdgeWeight + Clone,
295 Ix: petgraph::graph::IndexType,
296 R: Rng,
297{
298 let nodes: Vec<N> = graph.nodes().into_iter().cloned().collect();
299 if nodes.is_empty() {
300 return Ok(Graph::new());
301 }
302 if nodes.len() == 1 {
303 let mut tree = Graph::new();
304 tree.add_node(nodes[0].clone());
305 return Ok(tree);
306 }
307
308 let mut edges: Vec<_> = graph.edges().into_iter().collect();
310 edges.shuffle(rng);
311
312 let mut tree = Graph::new();
313
314 for node in &nodes {
316 tree.add_node(node.clone());
317 }
318
319 let mut parent: std::collections::HashMap<N, N> =
321 nodes.iter().map(|n| (n.clone(), n.clone())).collect();
322 let mut rank: std::collections::HashMap<N, usize> =
323 nodes.iter().map(|n| (n.clone(), 0)).collect();
324
325 fn find<N: crate::base::Node>(parent: &mut std::collections::HashMap<N, N>, node: &N) -> N {
326 if parent[node] != *node {
327 let root = find(parent, &parent[node].clone());
328 parent.insert(node.clone(), root.clone());
329 }
330 parent[node].clone()
331 }
332
333 fn union<N: crate::base::Node>(
334 parent: &mut std::collections::HashMap<N, N>,
335 rank: &mut std::collections::HashMap<N, usize>,
336 x: &N,
337 y: &N,
338 ) -> bool {
339 let root_x = find(parent, x);
340 let root_y = find(parent, y);
341
342 if root_x == root_y {
343 return false; }
345
346 match rank[&root_x].cmp(&rank[&root_y]) {
348 std::cmp::Ordering::Less => {
349 parent.insert(root_x, root_y);
350 }
351 std::cmp::Ordering::Greater => {
352 parent.insert(root_y, root_x);
353 }
354 std::cmp::Ordering::Equal => {
355 parent.insert(root_y, root_x.clone());
356 *rank.get_mut(&root_x).unwrap() += 1;
357 }
358 }
359 true
360 }
361
362 let mut edges_added = 0;
363
364 for edge in edges {
366 if union(&mut parent, &mut rank, &edge.source, &edge.target) {
367 tree.add_edge(edge.source, edge.target, edge.weight)?;
368 edges_added += 1;
369 if edges_added == nodes.len() - 1 {
370 break;
371 }
372 }
373 }
374
375 if edges_added != nodes.len() - 1 {
377 return Err(GraphError::InvalidGraph(
378 "Input graph is not connected - cannot create spanning tree".to_string(),
379 ));
380 }
381
382 Ok(tree)
383}
384
385#[allow(dead_code)]
397pub fn forest_graph<R: Rng>(
398 _tree_sizes: &[usize],
399 sizes: &[usize],
400 rng: &mut R,
401) -> Result<Graph<usize, f64>> {
402 let mut forest = Graph::new();
403 let mut node_offset = 0;
404
405 for &tree_size in _tree_sizes {
406 if tree_size == 0 {
407 continue;
408 }
409
410 let tree = tree_graph(tree_size, rng)?;
412
413 for i in 0..tree_size {
415 forest.add_node(node_offset + i);
416 }
417
418 for edge in tree.edges() {
420 forest.add_edge(
421 node_offset + edge.source,
422 node_offset + edge.target,
423 edge.weight,
424 )?;
425 }
426
427 node_offset += tree_size;
428 }
429
430 Ok(forest)
431}
432
433#[allow(dead_code)]
441pub fn cycle_graph(n: usize) -> Result<Graph<usize, f64>> {
442 if n < 3 {
443 return Err(GraphError::InvalidGraph(
444 "Cycle graph must have at least 3 nodes".to_string(),
445 ));
446 }
447
448 let mut graph = Graph::new();
449
450 for i in 0..n {
452 graph.add_node(i);
453 }
454
455 for i in 0..n {
457 graph.add_edge(i, (i + 1) % n, 1.0)?;
458 }
459
460 Ok(graph)
461}
462
463#[allow(dead_code)]
472pub fn grid_2d_graph(rows: usize, cols: usize) -> Result<Graph<usize, f64>> {
473 if rows == 0 || cols == 0 {
474 return Err(GraphError::InvalidGraph(
475 "Grid dimensions must be positive".to_string(),
476 ));
477 }
478
479 let mut graph = Graph::new();
480
481 for i in 0..(rows * cols) {
483 graph.add_node(i);
484 }
485
486 for row in 0..rows {
488 for col in 0..cols {
489 let node_id = row * cols + col;
490
491 if col + 1 < cols {
493 let right_neighbor = row * cols + (col + 1);
494 graph.add_edge(node_id, right_neighbor, 1.0)?;
495 }
496
497 if row + 1 < rows {
499 let bottom_neighbor = (row + 1) * cols + col;
500 graph.add_edge(node_id, bottom_neighbor, 1.0)?;
501 }
502 }
503 }
504
505 Ok(graph)
506}
507
508#[allow(dead_code)]
518pub fn grid_3d_graph(x_dim: usize, y_dim: usize, z_dim: usize) -> Result<Graph<usize, f64>> {
519 if x_dim == 0 || y_dim == 0 || z_dim == 0 {
520 return Err(GraphError::InvalidGraph(
521 "Grid dimensions must be positive".to_string(),
522 ));
523 }
524
525 let mut graph = Graph::new();
526
527 for i in 0..(x_dim * y_dim * z_dim) {
529 graph.add_node(i);
530 }
531
532 for z in 0..z_dim {
534 for y in 0..y_dim {
535 for x in 0..x_dim {
536 let node_id = z * x_dim * y_dim + y * x_dim + x;
537
538 if x + 1 < x_dim {
540 let right_neighbor = z * x_dim * y_dim + y * x_dim + (x + 1);
541 graph.add_edge(node_id, right_neighbor, 1.0)?;
542 }
543
544 if y + 1 < y_dim {
546 let front_neighbor = z * x_dim * y_dim + (y + 1) * x_dim + x;
547 graph.add_edge(node_id, front_neighbor, 1.0)?;
548 }
549
550 if z + 1 < z_dim {
552 let top_neighbor = (z + 1) * x_dim * y_dim + y * x_dim + x;
553 graph.add_edge(node_id, top_neighbor, 1.0)?;
554 }
555 }
556 }
557 }
558
559 Ok(graph)
560}
561
562#[allow(dead_code)]
571pub fn triangular_lattice_graph(rows: usize, cols: usize) -> Result<Graph<usize, f64>> {
572 if rows == 0 || cols == 0 {
573 return Err(GraphError::InvalidGraph(
574 "Lattice dimensions must be positive".to_string(),
575 ));
576 }
577
578 let mut graph = Graph::new();
579
580 for i in 0..(rows * cols) {
582 graph.add_node(i);
583 }
584
585 for row in 0..rows {
586 for col in 0..cols {
587 let node_id = row * cols + col;
588
589 if col + 1 < cols {
592 let right_neighbor = row * cols + (col + 1);
593 graph.add_edge(node_id, right_neighbor, 1.0)?;
594 }
595
596 if row + 1 < rows {
598 let bottom_neighbor = (row + 1) * cols + col;
599 graph.add_edge(node_id, bottom_neighbor, 1.0)?;
600 }
601
602 if row + 1 < rows && col + 1 < cols {
605 let diag_neighbor = (row + 1) * cols + (col + 1);
606 graph.add_edge(node_id, diag_neighbor, 1.0)?;
607 }
608
609 if row + 1 < rows && col > 0 && row % 2 == 0 {
611 let diag_neighbor = (row + 1) * cols + (col - 1);
612 graph.add_edge(node_id, diag_neighbor, 1.0)?;
613 }
614 }
615 }
616
617 Ok(graph)
618}
619
620#[allow(dead_code)]
629pub fn hexagonal_lattice_graph(rows: usize, cols: usize) -> Result<Graph<usize, f64>> {
630 if rows == 0 || cols == 0 {
631 return Err(GraphError::InvalidGraph(
632 "Lattice dimensions must be positive".to_string(),
633 ));
634 }
635
636 let mut graph = Graph::new();
637
638 for i in 0..(rows * cols) {
640 graph.add_node(i);
641 }
642
643 for row in 0..rows {
644 for col in 0..cols {
645 let node_id = row * cols + col;
646
647 if col + 1 < cols {
652 let right_neighbor = row * cols + (col + 1);
653 graph.add_edge(node_id, right_neighbor, 1.0)?;
654 }
655
656 if row % 2 == 0 {
658 if row + 1 < rows {
660 if col > 0 {
661 let down_left = (row + 1) * cols + (col - 1);
662 graph.add_edge(node_id, down_left, 1.0)?;
663 }
664 if col < cols {
665 let down_right = (row + 1) * cols + col;
666 graph.add_edge(node_id, down_right, 1.0)?;
667 }
668 }
669 } else {
670 if row + 1 < rows {
672 let down_left = (row + 1) * cols + col;
673 graph.add_edge(node_id, down_left, 1.0)?;
674
675 if col + 1 < cols {
676 let down_right = (row + 1) * cols + (col + 1);
677 graph.add_edge(node_id, down_right, 1.0)?;
678 }
679 }
680 }
681 }
682 }
683
684 Ok(graph)
685}
686
687#[allow(dead_code)]
698pub fn watts_strogatz_graph<R: Rng>(
699 n: usize,
700 k: usize,
701 p: f64,
702 rng: &mut R,
703) -> Result<Graph<usize, f64>> {
704 if k >= n || k % 2 != 0 {
705 return Err(GraphError::InvalidGraph(
706 "k must be even and less than n".to_string(),
707 ));
708 }
709 if !(0.0..=1.0).contains(&p) {
710 return Err(GraphError::InvalidGraph(
711 "Probability must be between 0 and 1".to_string(),
712 ));
713 }
714
715 let mut graph = Graph::new();
716
717 for i in 0..n {
719 graph.add_node(i);
720 }
721
722 for i in 0..n {
724 for j in 1..=(k / 2) {
725 let neighbor = (i + j) % n;
726 graph.add_edge(i, neighbor, 1.0)?;
727 }
728 }
729
730 let edges_to_process: Vec<_> = graph.edges().into_iter().collect();
732
733 for edge in edges_to_process {
734 if rng.random::<f64>() < p {
735 let mut new_graph = Graph::new();
737
738 for i in 0..n {
740 new_graph.add_node(i);
741 }
742
743 for existing_edge in graph.edges() {
745 if (existing_edge.source != edge.source || existing_edge.target != edge.target)
746 && (existing_edge.source != edge.target || existing_edge.target != edge.source)
747 {
748 new_graph.add_edge(
749 existing_edge.source,
750 existing_edge.target,
751 existing_edge.weight,
752 )?;
753 }
754 }
755
756 let mut new_target = rng.gen_range(0..n);
758 while new_target == edge.source || new_graph.has_node(&new_target) {
759 new_target = rng.gen_range(0..n);
760 }
761
762 new_graph.add_edge(edge.source, new_target, 1.0)?;
763 graph = new_graph;
764 }
765 }
766
767 Ok(graph)
768}
769
770#[allow(dead_code)]
785pub fn stochastic_block_model<R: Rng>(
786 block_sizes: &[usize],
787 block_matrix: &[Vec<f64>],
788 rng: &mut R,
789) -> Result<Graph<usize, f64>> {
790 if block_sizes.is_empty() {
791 return Err(GraphError::InvalidGraph(
792 "At least one block must be specified".to_string(),
793 ));
794 }
795
796 if block_matrix.len() != block_sizes.len() {
797 return Err(GraphError::InvalidGraph(
798 "Block _matrix dimensions must match number of blocks".to_string(),
799 ));
800 }
801
802 for row in block_matrix {
803 if row.len() != block_sizes.len() {
804 return Err(GraphError::InvalidGraph(
805 "Block _matrix must be square".to_string(),
806 ));
807 }
808 for &prob in row {
809 if !(0.0..=1.0).contains(&prob) {
810 return Err(GraphError::InvalidGraph(
811 "All probabilities must be between 0 and 1".to_string(),
812 ));
813 }
814 }
815 }
816
817 let total_nodes: usize = block_sizes.iter().sum();
818 let mut graph = Graph::new();
819
820 for i in 0..total_nodes {
822 graph.add_node(i);
823 }
824
825 let mut node_to_block = vec![0; total_nodes];
827 let mut current_node = 0;
828 for (block_id, &block_size) in block_sizes.iter().enumerate() {
829 for _ in 0..block_size {
830 node_to_block[current_node] = block_id;
831 current_node += 1;
832 }
833 }
834
835 for i in 0..total_nodes {
837 for j in (i + 1)..total_nodes {
838 let block_i = node_to_block[i];
839 let block_j = node_to_block[j];
840 let prob = block_matrix[block_i][block_j];
841
842 if rng.random::<f64>() < prob {
843 graph.add_edge(i, j, 1.0)?;
844 }
845 }
846 }
847
848 Ok(graph)
849}
850
851#[allow(dead_code)]
866pub fn two_community_sbm<R: Rng>(
867 n1: usize,
868 n2: usize,
869 p_in: f64,
870 p_out: f64,
871 rng: &mut R,
872) -> Result<Graph<usize, f64>> {
873 let block_sizes = vec![n1, n2];
874 let block_matrix = vec![vec![p_in, p_out], vec![p_out, p_in]];
875
876 stochastic_block_model(&block_sizes, &block_matrix, rng)
877}
878
879#[allow(dead_code)]
894pub fn planted_partition_model<R: Rng>(
895 n: usize,
896 k: usize,
897 p_in: f64,
898 p_out: f64,
899 rng: &mut R,
900) -> Result<Graph<usize, f64>> {
901 if n % k != 0 {
902 return Err(GraphError::InvalidGraph(
903 "Number of nodes must be divisible by number of communities".to_string(),
904 ));
905 }
906
907 let community_size = n / k;
908 let block_sizes = vec![community_size; k];
909
910 let mut block_matrix = vec![vec![p_out; k]; k];
912 for (i, row) in block_matrix.iter_mut().enumerate().take(k) {
913 row[i] = p_in;
914 }
915
916 stochastic_block_model(&block_sizes, &block_matrix, rng)
917}
918
919#[allow(dead_code)]
939pub fn configuration_model<R: Rng>(
940 degree_sequence: &[usize],
941 rng: &mut R,
942) -> Result<Graph<usize, f64>> {
943 if degree_sequence.is_empty() {
944 return Ok(Graph::new());
945 }
946
947 let total_degree: usize = degree_sequence.iter().sum();
949 if total_degree % 2 != 0 {
950 return Err(GraphError::InvalidGraph(
951 "Sum of degrees must be even".to_string(),
952 ));
953 }
954
955 let n = degree_sequence.len();
956 let mut graph = Graph::new();
957
958 for i in 0..n {
960 graph.add_node(i);
961 }
962
963 let mut stubs = Vec::new();
965 for (node_id, °ree) in degree_sequence.iter().enumerate() {
966 for _ in 0..degree {
967 stubs.push(node_id);
968 }
969 }
970
971 while stubs.len() >= 2 {
973 let idx1 = rng.gen_range(0..stubs.len());
975 let stub1 = stubs.remove(idx1);
976
977 let idx2 = rng.gen_range(0..stubs.len());
978 let stub2 = stubs.remove(idx2);
979
980 graph.add_edge(stub1, stub2, 1.0)?;
982 }
983
984 Ok(graph)
985}
986
987#[allow(dead_code)]
1001pub fn simple_configuration_model<R: Rng>(
1002 degree_sequence: &[usize],
1003 rng: &mut R,
1004 max_attempts: usize,
1005) -> Result<Graph<usize, f64>> {
1006 if degree_sequence.is_empty() {
1007 return Ok(Graph::new());
1008 }
1009
1010 let total_degree: usize = degree_sequence.iter().sum();
1012 if total_degree % 2 != 0 {
1013 return Err(GraphError::InvalidGraph(
1014 "Sum of degrees must be even".to_string(),
1015 ));
1016 }
1017
1018 let n = degree_sequence.len();
1019
1020 for °ree in degree_sequence {
1022 if degree >= n {
1023 return Err(GraphError::InvalidGraph(
1024 "Node degree cannot exceed n-1 in a simple graph".to_string(),
1025 ));
1026 }
1027 }
1028
1029 let mut _attempts = 0;
1030
1031 while _attempts < max_attempts {
1032 let mut graph = Graph::new();
1033
1034 for i in 0..n {
1036 graph.add_node(i);
1037 }
1038
1039 let mut stubs = Vec::new();
1041 for (node_id, °ree) in degree_sequence.iter().enumerate() {
1042 for _ in 0..degree {
1043 stubs.push(node_id);
1044 }
1045 }
1046
1047 let mut success = true;
1048
1049 while stubs.len() >= 2 && success {
1051 let idx1 = rng.gen_range(0..stubs.len());
1053 let stub1 = stubs[idx1];
1054
1055 let idx2 = rng.gen_range(0..stubs.len());
1056 let stub2 = stubs[idx2];
1057
1058 if stub1 == stub2 || graph.has_edge(&stub1, &stub2) {
1060 let mut retries = 0;
1062 let mut found_valid = false;
1063
1064 while retries < 50 && !found_valid {
1065 let new_idx2 = rng.gen_range(0..stubs.len());
1066 let new_stub2 = stubs[new_idx2];
1067
1068 if stub1 != new_stub2 && !graph.has_edge(&stub1, &new_stub2) {
1069 if idx1 > new_idx2 {
1072 stubs.remove(idx1);
1073 stubs.remove(new_idx2);
1074 } else {
1075 stubs.remove(new_idx2);
1076 stubs.remove(idx1);
1077 }
1078 graph.add_edge(stub1, new_stub2, 1.0)?;
1079 found_valid = true;
1080 }
1081 retries += 1;
1082 }
1083
1084 if !found_valid {
1085 success = false;
1086 }
1087 } else {
1088 if idx1 > idx2 {
1091 stubs.remove(idx1);
1092 stubs.remove(idx2);
1093 } else {
1094 stubs.remove(idx2);
1095 stubs.remove(idx1);
1096 }
1097 graph.add_edge(stub1, stub2, 1.0)?;
1098 }
1099 }
1100
1101 if success && stubs.is_empty() {
1102 return Ok(graph);
1103 }
1104
1105 _attempts += 1;
1106 }
1107
1108 Err(GraphError::InvalidGraph(
1109 "Could not generate simple graph with given degree _sequence after maximum _attempts"
1110 .to_string(),
1111 ))
1112}
1113
1114#[cfg(test)]
1115mod tests {
1116 use super::*;
1117
1118 #[test]
1119 fn test_erdos_renyi_graph() {
1120 let mut rng = StdRng::seed_from_u64(42);
1121 let graph = erdos_renyi_graph(10, 0.3, &mut rng).unwrap();
1122
1123 assert_eq!(graph.node_count(), 10);
1124 assert!(graph.edge_count() <= 45);
1127 }
1128
1129 #[test]
1130 fn test_complete_graph() {
1131 let graph = complete_graph(5).unwrap();
1132
1133 assert_eq!(graph.node_count(), 5);
1134 assert_eq!(graph.edge_count(), 10); }
1136
1137 #[test]
1138 fn test_star_graph() {
1139 let graph = star_graph(6).unwrap();
1140
1141 assert_eq!(graph.node_count(), 6);
1142 assert_eq!(graph.edge_count(), 5); }
1144
1145 #[test]
1146 fn test_path_graph() {
1147 let graph = path_graph(5).unwrap();
1148
1149 assert_eq!(graph.node_count(), 5);
1150 assert_eq!(graph.edge_count(), 4); }
1152
1153 #[test]
1154 fn test_cycle_graph() {
1155 let graph = cycle_graph(5).unwrap();
1156
1157 assert_eq!(graph.node_count(), 5);
1158 assert_eq!(graph.edge_count(), 5); assert!(cycle_graph(2).is_err());
1162 }
1163
1164 #[test]
1165 fn test_grid_2d_graph() {
1166 let graph = grid_2d_graph(3, 4).unwrap();
1167
1168 assert_eq!(graph.node_count(), 12); assert_eq!(graph.edge_count(), 17); }
1171
1172 #[test]
1173 fn test_grid_3d_graph() {
1174 let graph = grid_3d_graph(2, 2, 2).unwrap();
1175
1176 assert_eq!(graph.node_count(), 8); assert_eq!(graph.edge_count(), 12);
1180 }
1181
1182 #[test]
1183 fn test_triangular_lattice_graph() {
1184 let graph = triangular_lattice_graph(3, 3).unwrap();
1185
1186 assert_eq!(graph.node_count(), 9); assert!(graph.edge_count() > 12); }
1190
1191 #[test]
1192 fn test_hexagonal_lattice_graph() {
1193 let graph = hexagonal_lattice_graph(3, 3).unwrap();
1194
1195 assert_eq!(graph.node_count(), 9); assert!(graph.edge_count() >= 6);
1198 }
1199
1200 #[test]
1201 fn test_barabasi_albert_graph() {
1202 let mut rng = StdRng::seed_from_u64(42);
1203 let graph = barabasi_albert_graph(10, 2, &mut rng).unwrap();
1204
1205 assert_eq!(graph.node_count(), 10);
1206 assert_eq!(graph.edge_count(), 17);
1208 }
1209
1210 #[test]
1211 fn test_stochastic_block_model() {
1212 let mut rng = StdRng::seed_from_u64(42);
1213
1214 let block_sizes = vec![3, 4];
1216 let block_matrix = vec![vec![0.8, 0.1], vec![0.1, 0.8]];
1218
1219 let graph = stochastic_block_model(&block_sizes, &block_matrix, &mut rng).unwrap();
1220
1221 assert_eq!(graph.node_count(), 7); for i in 0..7 {
1225 assert!(graph.has_node(&i));
1226 }
1227 }
1228
1229 #[test]
1230 fn test_two_community_sbm() {
1231 let mut rng = StdRng::seed_from_u64(42);
1232
1233 let graph = two_community_sbm(5, 5, 0.8, 0.1, &mut rng).unwrap();
1234
1235 assert_eq!(graph.node_count(), 10);
1236
1237 assert!(graph.edge_count() > 0);
1240 }
1241
1242 #[test]
1243 fn test_planted_partition_model() {
1244 let mut rng = StdRng::seed_from_u64(42);
1245
1246 let graph = planted_partition_model(12, 3, 0.7, 0.1, &mut rng).unwrap();
1247
1248 assert_eq!(graph.node_count(), 12); assert!(graph.edge_count() > 0);
1253 }
1254
1255 #[test]
1256 fn test_stochastic_block_model_errors() {
1257 let mut rng = StdRng::seed_from_u64(42);
1258
1259 assert!(stochastic_block_model(&[], &[], &mut rng).is_err());
1261
1262 let block_sizes = vec![3, 4];
1264 let wrong_matrix = vec![vec![0.5]];
1265 assert!(stochastic_block_model(&block_sizes, &wrong_matrix, &mut rng).is_err());
1266
1267 let bad_matrix = vec![vec![1.5, 0.5], vec![0.5, 0.5]];
1269 assert!(stochastic_block_model(&block_sizes, &bad_matrix, &mut rng).is_err());
1270
1271 assert!(planted_partition_model(10, 3, 0.5, 0.1, &mut rng).is_err());
1273 }
1274
1275 #[test]
1276 fn test_configuration_model() {
1277 let mut rng = StdRng::seed_from_u64(42);
1278
1279 let degree_sequence = vec![2, 2, 2, 2]; let graph = configuration_model(°ree_sequence, &mut rng).unwrap();
1282
1283 assert_eq!(graph.node_count(), 4);
1284 assert_eq!(graph.edge_count(), 4);
1286
1287 for (i, &expected_degree) in degree_sequence.iter().enumerate() {
1289 let actual_degree = graph.degree(&i);
1290 assert_eq!(actual_degree, expected_degree);
1291 }
1292 }
1293
1294 #[test]
1295 fn test_configuration_model_errors() {
1296 let mut rng = StdRng::seed_from_u64(42);
1297
1298 let odd_degree_sequence = vec![1, 2, 2]; assert!(configuration_model(&odd_degree_sequence, &mut rng).is_err());
1301
1302 let empty_sequence = vec![];
1304 let graph = configuration_model(&empty_sequence, &mut rng).unwrap();
1305 assert_eq!(graph.node_count(), 0);
1306 }
1307
1308 #[test]
1309 fn test_simple_configuration_model() {
1310 let mut rng = StdRng::seed_from_u64(42);
1311
1312 let degree_sequence = vec![2, 2, 2, 2]; let graph = simple_configuration_model(°ree_sequence, &mut rng, 100).unwrap();
1315
1316 assert_eq!(graph.node_count(), 4);
1317 assert_eq!(graph.edge_count(), 4);
1318
1319 for i in 0..4 {
1321 assert!(!graph.has_edge(&i, &i), "Graph should not have self-loops");
1322 }
1323
1324 for (i, &expected_degree) in degree_sequence.iter().enumerate() {
1326 let actual_degree = graph.degree(&i);
1327 assert_eq!(actual_degree, expected_degree);
1328 }
1329 }
1330
1331 #[test]
1332 fn test_simple_configuration_model_errors() {
1333 let mut rng = StdRng::seed_from_u64(42);
1334
1335 let invalid_degree_sequence = vec![4, 2, 2, 2]; assert!(simple_configuration_model(&invalid_degree_sequence, &mut rng, 10).is_err());
1338
1339 let odd_degree_sequence = vec![1, 2, 2]; assert!(simple_configuration_model(&odd_degree_sequence, &mut rng, 10).is_err());
1342 }
1343
1344 #[test]
1345 fn test_tree_graph() {
1346 let mut rng = StdRng::seed_from_u64(42);
1347
1348 let empty_tree = tree_graph(0, &mut rng).unwrap();
1350 assert_eq!(empty_tree.node_count(), 0);
1351 assert_eq!(empty_tree.edge_count(), 0);
1352
1353 let single_tree = tree_graph(1, &mut rng).unwrap();
1355 assert_eq!(single_tree.node_count(), 1);
1356 assert_eq!(single_tree.edge_count(), 0);
1357
1358 let tree = tree_graph(5, &mut rng).unwrap();
1360 assert_eq!(tree.node_count(), 5);
1361 assert_eq!(tree.edge_count(), 4); for i in 0..5 {
1365 assert!(tree.has_node(&i));
1366 }
1367 }
1368
1369 #[test]
1370 fn test_random_spanning_tree() {
1371 let mut rng = StdRng::seed_from_u64(42);
1372
1373 let complete = complete_graph(4).unwrap();
1375
1376 let spanning_tree = random_spanning_tree(&complete, &mut rng).unwrap();
1378
1379 assert_eq!(spanning_tree.node_count(), 4);
1380 assert_eq!(spanning_tree.edge_count(), 3); for i in 0..4 {
1384 assert!(spanning_tree.has_node(&i));
1385 }
1386 }
1387
1388 #[test]
1389 fn test_forest_graph() {
1390 let mut rng = StdRng::seed_from_u64(42);
1391
1392 let tree_sizes = vec![3, 2, 4];
1394 let forest = forest_graph(&tree_sizes, &tree_sizes, &mut rng).unwrap();
1395
1396 assert_eq!(forest.node_count(), 9); assert_eq!(forest.edge_count(), 6); for i in 0..9 {
1401 assert!(forest.has_node(&i));
1402 }
1403
1404 let empty_forest = forest_graph(&[], &[], &mut rng).unwrap();
1406 assert_eq!(empty_forest.node_count(), 0);
1407 assert_eq!(empty_forest.edge_count(), 0);
1408
1409 let forest_with_zeros = forest_graph(&[0, 3, 0, 2], &[0, 3, 0, 2], &mut rng).unwrap();
1411 assert_eq!(forest_with_zeros.node_count(), 5); assert_eq!(forest_with_zeros.edge_count(), 3); }
1414}