1pub mod advanced;
10pub mod random_graphs;
11pub mod temporal;
12
13pub use advanced::{forest_fire, lfr_benchmark, LfrParams};
14pub use temporal::{temporal_barabasi_albert, temporal_random_walk, TemporalWalk};
15
16pub use random_graphs::{
17 barabasi_albert, chung_lu, erdos_renyi_g_nm, erdos_renyi_g_np, hyperbolic_random_graph,
18 kronecker_graph, random_regular, watts_strogatz,
19};
20
21use scirs2_core::random::prelude::*;
22use std::collections::HashSet;
23
24use crate::base::{DiGraph, Graph};
25use crate::error::{GraphError, Result};
26use scirs2_core::random::seq::SliceRandom;
27
28use scirs2_core::rand_prelude::IndexedRandom;
30
31#[allow(dead_code)]
33pub fn create_graph<N: crate::base::Node + std::fmt::Debug, E: crate::base::EdgeWeight>(
34) -> Graph<N, E> {
35 Graph::new()
36}
37
38#[allow(dead_code)]
40pub fn create_digraph<N: crate::base::Node + std::fmt::Debug, E: crate::base::EdgeWeight>(
41) -> DiGraph<N, E> {
42 DiGraph::new()
43}
44
45#[allow(dead_code)]
55pub fn erdos_renyi_graph<R: Rng>(n: usize, p: f64, rng: &mut R) -> Result<Graph<usize, f64>> {
56 if !(0.0..=1.0).contains(&p) {
57 return Err(GraphError::InvalidGraph(
58 "Probability must be between 0 and 1".to_string(),
59 ));
60 }
61
62 let mut graph = Graph::new();
63
64 for i in 0..n {
66 graph.add_node(i);
67 }
68
69 for i in 0..n {
71 for j in i + 1..n {
72 if rng.random::<f64>() < p {
73 graph.add_edge(i, j, 1.0)?;
74 }
75 }
76 }
77
78 Ok(graph)
79}
80
81#[allow(dead_code)]
91pub fn barabasi_albert_graph<R: Rng>(n: usize, m: usize, rng: &mut R) -> Result<Graph<usize, f64>> {
92 if m >= n {
93 return Err(GraphError::InvalidGraph(
94 "m must be less than n".to_string(),
95 ));
96 }
97 if m == 0 {
98 return Err(GraphError::InvalidGraph("m must be positive".to_string()));
99 }
100
101 let mut graph = Graph::new();
102
103 for i in 0..=m {
105 graph.add_node(i);
106 }
107
108 for i in 0..=m {
109 for j in i + 1..=m {
110 graph.add_edge(i, j, 1.0)?;
111 }
112 }
113
114 let mut degrees = vec![m; m + 1];
116 let mut total_degree = m * (m + 1);
117
118 for new_node in (m + 1)..n {
120 graph.add_node(new_node);
121
122 let mut targets = HashSet::new();
123
124 while targets.len() < m {
126 let mut cumulative_prob = 0.0;
127 let random_value = rng.random::<f64>() * total_degree as f64;
128
129 for (node_id, °ree) in degrees.iter().enumerate() {
130 cumulative_prob += degree as f64;
131 if random_value <= cumulative_prob && !targets.contains(&node_id) {
132 targets.insert(node_id);
133 break;
134 }
135 }
136 }
137
138 for &target in &targets {
140 graph.add_edge(new_node, target, 1.0)?;
141 degrees[target] += 1;
142 total_degree += 2; }
144
145 degrees.push(m); }
147
148 Ok(graph)
149}
150
151#[allow(dead_code)]
159pub fn complete_graph(n: usize) -> Result<Graph<usize, f64>> {
160 let mut graph = Graph::new();
161
162 for i in 0..n {
164 graph.add_node(i);
165 }
166
167 for i in 0..n {
169 for j in i + 1..n {
170 graph.add_edge(i, j, 1.0)?;
171 }
172 }
173
174 Ok(graph)
175}
176
177#[allow(dead_code)]
185pub fn star_graph(n: usize) -> Result<Graph<usize, f64>> {
186 if n == 0 {
187 return Err(GraphError::InvalidGraph(
188 "Star graph must have at least 1 node".to_string(),
189 ));
190 }
191
192 let mut graph = Graph::new();
193
194 for i in 0..n {
196 graph.add_node(i);
197 }
198
199 for i in 1..n {
201 graph.add_edge(0, i, 1.0)?;
202 }
203
204 Ok(graph)
205}
206
207#[allow(dead_code)]
215pub fn path_graph(n: usize) -> Result<Graph<usize, f64>> {
216 let mut graph = Graph::new();
217
218 for i in 0..n {
220 graph.add_node(i);
221 }
222
223 for i in 0..n.saturating_sub(1) {
225 graph.add_edge(i, i + 1, 1.0)?;
226 }
227
228 Ok(graph)
229}
230
231#[allow(dead_code)]
243pub fn tree_graph<R: Rng>(n: usize, rng: &mut R) -> Result<Graph<usize, f64>> {
244 if n == 0 {
245 return Ok(Graph::new());
246 }
247 if n == 1 {
248 let mut graph = Graph::new();
249 graph.add_node(0);
250 return Ok(graph);
251 }
252
253 let mut graph = Graph::new();
254
255 for i in 0..n {
257 graph.add_node(i);
258 }
259
260 let mut in_tree = vec![false; n];
262 let mut tree_nodes = Vec::new();
263
264 let start = rng.random_range(0..n);
266 in_tree[start] = true;
267 tree_nodes.push(start);
268
269 for _ in 1..n {
271 let tree_node = tree_nodes[rng.random_range(0..tree_nodes.len())];
273
274 let candidates: Vec<usize> = (0..n).filter(|&i| !in_tree[i]).collect();
276 if candidates.is_empty() {
277 break;
278 }
279
280 let new_node = candidates[rng.random_range(0..candidates.len())];
281
282 graph.add_edge(tree_node, new_node, 1.0)?;
284 in_tree[new_node] = true;
285 tree_nodes.push(new_node);
286 }
287
288 Ok(graph)
289}
290
291#[allow(dead_code)]
303pub fn random_spanning_tree<N, E, Ix, R>(
304 graph: &Graph<N, E, Ix>,
305 rng: &mut R,
306) -> Result<Graph<N, E, Ix>>
307where
308 N: crate::base::Node + std::fmt::Debug,
309 E: crate::base::EdgeWeight + Clone,
310 Ix: petgraph::graph::IndexType,
311 R: Rng,
312{
313 let nodes: Vec<N> = graph.nodes().into_iter().cloned().collect();
314 if nodes.is_empty() {
315 return Ok(Graph::new());
316 }
317 if nodes.len() == 1 {
318 let mut tree = Graph::new();
319 tree.add_node(nodes[0].clone());
320 return Ok(tree);
321 }
322
323 let mut edges: Vec<_> = graph.edges().into_iter().collect();
325 edges.shuffle(rng);
326
327 let mut tree = Graph::new();
328
329 for node in &nodes {
331 tree.add_node(node.clone());
332 }
333
334 let mut parent: std::collections::HashMap<N, N> =
336 nodes.iter().map(|n| (n.clone(), n.clone())).collect();
337 let mut rank: std::collections::HashMap<N, usize> =
338 nodes.iter().map(|n| (n.clone(), 0)).collect();
339
340 fn find<N: crate::base::Node>(parent: &mut std::collections::HashMap<N, N>, node: &N) -> N {
341 if parent[node] != *node {
342 let root = find(parent, &parent[node].clone());
343 parent.insert(node.clone(), root.clone());
344 }
345 parent[node].clone()
346 }
347
348 fn union<N: crate::base::Node>(
349 parent: &mut std::collections::HashMap<N, N>,
350 rank: &mut std::collections::HashMap<N, usize>,
351 x: &N,
352 y: &N,
353 ) -> bool {
354 let root_x = find(parent, x);
355 let root_y = find(parent, y);
356
357 if root_x == root_y {
358 return false; }
360
361 match rank[&root_x].cmp(&rank[&root_y]) {
363 std::cmp::Ordering::Less => {
364 parent.insert(root_x, root_y);
365 }
366 std::cmp::Ordering::Greater => {
367 parent.insert(root_y, root_x);
368 }
369 std::cmp::Ordering::Equal => {
370 parent.insert(root_y, root_x.clone());
371 *rank.get_mut(&root_x).expect("Operation failed") += 1;
372 }
373 }
374 true
375 }
376
377 let mut edges_added = 0;
378
379 for edge in edges {
381 if union(&mut parent, &mut rank, &edge.source, &edge.target) {
382 tree.add_edge(edge.source, edge.target, edge.weight)?;
383 edges_added += 1;
384 if edges_added == nodes.len() - 1 {
385 break;
386 }
387 }
388 }
389
390 if edges_added != nodes.len() - 1 {
392 return Err(GraphError::InvalidGraph(
393 "Input graph is not connected - cannot create spanning tree".to_string(),
394 ));
395 }
396
397 Ok(tree)
398}
399
400#[allow(dead_code)]
412pub fn forest_graph<R: Rng>(
413 _tree_sizes: &[usize],
414 sizes: &[usize],
415 rng: &mut R,
416) -> Result<Graph<usize, f64>> {
417 let mut forest = Graph::new();
418 let mut node_offset = 0;
419
420 for &tree_size in _tree_sizes {
421 if tree_size == 0 {
422 continue;
423 }
424
425 let tree = tree_graph(tree_size, rng)?;
427
428 for i in 0..tree_size {
430 forest.add_node(node_offset + i);
431 }
432
433 for edge in tree.edges() {
435 forest.add_edge(
436 node_offset + edge.source,
437 node_offset + edge.target,
438 edge.weight,
439 )?;
440 }
441
442 node_offset += tree_size;
443 }
444
445 Ok(forest)
446}
447
448#[allow(dead_code)]
456pub fn cycle_graph(n: usize) -> Result<Graph<usize, f64>> {
457 if n < 3 {
458 return Err(GraphError::InvalidGraph(
459 "Cycle graph must have at least 3 nodes".to_string(),
460 ));
461 }
462
463 let mut graph = Graph::new();
464
465 for i in 0..n {
467 graph.add_node(i);
468 }
469
470 for i in 0..n {
472 graph.add_edge(i, (i + 1) % n, 1.0)?;
473 }
474
475 Ok(graph)
476}
477
478#[allow(dead_code)]
487pub fn grid_2d_graph(rows: usize, cols: usize) -> Result<Graph<usize, f64>> {
488 if rows == 0 || cols == 0 {
489 return Err(GraphError::InvalidGraph(
490 "Grid dimensions must be positive".to_string(),
491 ));
492 }
493
494 let mut graph = Graph::new();
495
496 for i in 0..(rows * cols) {
498 graph.add_node(i);
499 }
500
501 for row in 0..rows {
503 for col in 0..cols {
504 let node_id = row * cols + col;
505
506 if col + 1 < cols {
508 let right_neighbor = row * cols + (col + 1);
509 graph.add_edge(node_id, right_neighbor, 1.0)?;
510 }
511
512 if row + 1 < rows {
514 let bottom_neighbor = (row + 1) * cols + col;
515 graph.add_edge(node_id, bottom_neighbor, 1.0)?;
516 }
517 }
518 }
519
520 Ok(graph)
521}
522
523#[allow(dead_code)]
533pub fn grid_3d_graph(x_dim: usize, y_dim: usize, z_dim: usize) -> Result<Graph<usize, f64>> {
534 if x_dim == 0 || y_dim == 0 || z_dim == 0 {
535 return Err(GraphError::InvalidGraph(
536 "Grid dimensions must be positive".to_string(),
537 ));
538 }
539
540 let mut graph = Graph::new();
541
542 for i in 0..(x_dim * y_dim * z_dim) {
544 graph.add_node(i);
545 }
546
547 for z in 0..z_dim {
549 for y in 0..y_dim {
550 for x in 0..x_dim {
551 let node_id = z * x_dim * y_dim + y * x_dim + x;
552
553 if x + 1 < x_dim {
555 let right_neighbor = z * x_dim * y_dim + y * x_dim + (x + 1);
556 graph.add_edge(node_id, right_neighbor, 1.0)?;
557 }
558
559 if y + 1 < y_dim {
561 let front_neighbor = z * x_dim * y_dim + (y + 1) * x_dim + x;
562 graph.add_edge(node_id, front_neighbor, 1.0)?;
563 }
564
565 if z + 1 < z_dim {
567 let top_neighbor = (z + 1) * x_dim * y_dim + y * x_dim + x;
568 graph.add_edge(node_id, top_neighbor, 1.0)?;
569 }
570 }
571 }
572 }
573
574 Ok(graph)
575}
576
577#[allow(dead_code)]
586pub fn triangular_lattice_graph(rows: usize, cols: usize) -> Result<Graph<usize, f64>> {
587 if rows == 0 || cols == 0 {
588 return Err(GraphError::InvalidGraph(
589 "Lattice dimensions must be positive".to_string(),
590 ));
591 }
592
593 let mut graph = Graph::new();
594
595 for i in 0..(rows * cols) {
597 graph.add_node(i);
598 }
599
600 for row in 0..rows {
601 for col in 0..cols {
602 let node_id = row * cols + col;
603
604 if col + 1 < cols {
607 let right_neighbor = row * cols + (col + 1);
608 graph.add_edge(node_id, right_neighbor, 1.0)?;
609 }
610
611 if row + 1 < rows {
613 let bottom_neighbor = (row + 1) * cols + col;
614 graph.add_edge(node_id, bottom_neighbor, 1.0)?;
615 }
616
617 if row + 1 < rows && col + 1 < cols {
620 let diag_neighbor = (row + 1) * cols + (col + 1);
621 graph.add_edge(node_id, diag_neighbor, 1.0)?;
622 }
623
624 if row + 1 < rows && col > 0 && row % 2 == 0 {
626 let diag_neighbor = (row + 1) * cols + (col - 1);
627 graph.add_edge(node_id, diag_neighbor, 1.0)?;
628 }
629 }
630 }
631
632 Ok(graph)
633}
634
635#[allow(dead_code)]
644pub fn hexagonal_lattice_graph(rows: usize, cols: usize) -> Result<Graph<usize, f64>> {
645 if rows == 0 || cols == 0 {
646 return Err(GraphError::InvalidGraph(
647 "Lattice dimensions must be positive".to_string(),
648 ));
649 }
650
651 let mut graph = Graph::new();
652
653 for i in 0..(rows * cols) {
655 graph.add_node(i);
656 }
657
658 for row in 0..rows {
659 for col in 0..cols {
660 let node_id = row * cols + col;
661
662 if col + 1 < cols {
667 let right_neighbor = row * cols + (col + 1);
668 graph.add_edge(node_id, right_neighbor, 1.0)?;
669 }
670
671 if row % 2 == 0 {
673 if row + 1 < rows {
675 if col > 0 {
676 let down_left = (row + 1) * cols + (col - 1);
677 graph.add_edge(node_id, down_left, 1.0)?;
678 }
679 if col < cols {
680 let down_right = (row + 1) * cols + col;
681 graph.add_edge(node_id, down_right, 1.0)?;
682 }
683 }
684 } else {
685 if row + 1 < rows {
687 let down_left = (row + 1) * cols + col;
688 graph.add_edge(node_id, down_left, 1.0)?;
689
690 if col + 1 < cols {
691 let down_right = (row + 1) * cols + (col + 1);
692 graph.add_edge(node_id, down_right, 1.0)?;
693 }
694 }
695 }
696 }
697 }
698
699 Ok(graph)
700}
701
702#[allow(dead_code)]
713pub fn watts_strogatz_graph<R: Rng>(
714 n: usize,
715 k: usize,
716 p: f64,
717 rng: &mut R,
718) -> Result<Graph<usize, f64>> {
719 if k >= n || !k.is_multiple_of(2) {
720 return Err(GraphError::InvalidGraph(
721 "k must be even and less than n".to_string(),
722 ));
723 }
724 if !(0.0..=1.0).contains(&p) {
725 return Err(GraphError::InvalidGraph(
726 "Probability must be between 0 and 1".to_string(),
727 ));
728 }
729
730 let mut graph = Graph::new();
731
732 for i in 0..n {
734 graph.add_node(i);
735 }
736
737 for i in 0..n {
739 for j in 1..=(k / 2) {
740 let neighbor = (i + j) % n;
741 graph.add_edge(i, neighbor, 1.0)?;
742 }
743 }
744
745 let edges_to_process: Vec<_> = graph.edges().into_iter().collect();
747
748 for edge in edges_to_process {
749 if rng.random::<f64>() < p {
750 let mut new_graph = Graph::new();
752
753 for i in 0..n {
755 new_graph.add_node(i);
756 }
757
758 for existing_edge in graph.edges() {
760 if (existing_edge.source != edge.source || existing_edge.target != edge.target)
761 && (existing_edge.source != edge.target || existing_edge.target != edge.source)
762 {
763 new_graph.add_edge(
764 existing_edge.source,
765 existing_edge.target,
766 existing_edge.weight,
767 )?;
768 }
769 }
770
771 let mut new_target = rng.random_range(0..n);
782 while new_target == edge.source || new_graph.has_edge(&edge.source, &new_target) {
783 new_target = rng.random_range(0..n);
784 }
785
786 new_graph.add_edge(edge.source, new_target, 1.0)?;
787 graph = new_graph;
788 }
789 }
790
791 Ok(graph)
792}
793
794#[allow(dead_code)]
809pub fn stochastic_block_model<R: Rng>(
810 block_sizes: &[usize],
811 block_matrix: &[Vec<f64>],
812 rng: &mut R,
813) -> Result<Graph<usize, f64>> {
814 if block_sizes.is_empty() {
815 return Err(GraphError::InvalidGraph(
816 "At least one block must be specified".to_string(),
817 ));
818 }
819
820 if block_matrix.len() != block_sizes.len() {
821 return Err(GraphError::InvalidGraph(
822 "Block _matrix dimensions must match number of blocks".to_string(),
823 ));
824 }
825
826 for row in block_matrix {
827 if row.len() != block_sizes.len() {
828 return Err(GraphError::InvalidGraph(
829 "Block _matrix must be square".to_string(),
830 ));
831 }
832 for &prob in row {
833 if !(0.0..=1.0).contains(&prob) {
834 return Err(GraphError::InvalidGraph(
835 "All probabilities must be between 0 and 1".to_string(),
836 ));
837 }
838 }
839 }
840
841 let total_nodes: usize = block_sizes.iter().sum();
842 let mut graph = Graph::new();
843
844 for i in 0..total_nodes {
846 graph.add_node(i);
847 }
848
849 let mut node_to_block = vec![0; total_nodes];
851 let mut current_node = 0;
852 for (block_id, &block_size) in block_sizes.iter().enumerate() {
853 for _ in 0..block_size {
854 node_to_block[current_node] = block_id;
855 current_node += 1;
856 }
857 }
858
859 for i in 0..total_nodes {
861 for j in (i + 1)..total_nodes {
862 let block_i = node_to_block[i];
863 let block_j = node_to_block[j];
864 let prob = block_matrix[block_i][block_j];
865
866 if rng.random::<f64>() < prob {
867 graph.add_edge(i, j, 1.0)?;
868 }
869 }
870 }
871
872 Ok(graph)
873}
874
875#[allow(dead_code)]
890pub fn two_community_sbm<R: Rng>(
891 n1: usize,
892 n2: usize,
893 p_in: f64,
894 p_out: f64,
895 rng: &mut R,
896) -> Result<Graph<usize, f64>> {
897 let block_sizes = vec![n1, n2];
898 let block_matrix = vec![vec![p_in, p_out], vec![p_out, p_in]];
899
900 stochastic_block_model(&block_sizes, &block_matrix, rng)
901}
902
903#[allow(dead_code)]
918pub fn planted_partition_model<R: Rng>(
919 n: usize,
920 k: usize,
921 p_in: f64,
922 p_out: f64,
923 rng: &mut R,
924) -> Result<Graph<usize, f64>> {
925 if !n.is_multiple_of(k) {
926 return Err(GraphError::InvalidGraph(
927 "Number of nodes must be divisible by number of communities".to_string(),
928 ));
929 }
930
931 let community_size = n / k;
932 let block_sizes = vec![community_size; k];
933
934 let mut block_matrix = vec![vec![p_out; k]; k];
936 for (i, row) in block_matrix.iter_mut().enumerate().take(k) {
937 row[i] = p_in;
938 }
939
940 stochastic_block_model(&block_sizes, &block_matrix, rng)
941}
942
943#[allow(dead_code)]
963pub fn configuration_model<R: Rng>(
964 degree_sequence: &[usize],
965 rng: &mut R,
966) -> Result<Graph<usize, f64>> {
967 if degree_sequence.is_empty() {
968 return Ok(Graph::new());
969 }
970
971 let total_degree: usize = degree_sequence.iter().sum();
973 if !total_degree.is_multiple_of(2) {
974 return Err(GraphError::InvalidGraph(
975 "Sum of degrees must be even".to_string(),
976 ));
977 }
978
979 let n = degree_sequence.len();
980 let mut graph = Graph::new();
981
982 for i in 0..n {
984 graph.add_node(i);
985 }
986
987 let mut stubs = Vec::new();
989 for (node_id, °ree) in degree_sequence.iter().enumerate() {
990 for _ in 0..degree {
991 stubs.push(node_id);
992 }
993 }
994
995 while stubs.len() >= 2 {
997 let idx1 = rng.random_range(0..stubs.len());
999 let stub1 = stubs.remove(idx1);
1000
1001 let idx2 = rng.random_range(0..stubs.len());
1002 let stub2 = stubs.remove(idx2);
1003
1004 graph.add_edge(stub1, stub2, 1.0)?;
1006 }
1007
1008 Ok(graph)
1009}
1010
1011#[allow(dead_code)]
1025pub fn simple_configuration_model<R: Rng>(
1026 degree_sequence: &[usize],
1027 rng: &mut R,
1028 max_attempts: usize,
1029) -> Result<Graph<usize, f64>> {
1030 if degree_sequence.is_empty() {
1031 return Ok(Graph::new());
1032 }
1033
1034 let total_degree: usize = degree_sequence.iter().sum();
1036 if !total_degree.is_multiple_of(2) {
1037 return Err(GraphError::InvalidGraph(
1038 "Sum of degrees must be even".to_string(),
1039 ));
1040 }
1041
1042 let n = degree_sequence.len();
1043
1044 for °ree in degree_sequence {
1046 if degree >= n {
1047 return Err(GraphError::InvalidGraph(
1048 "Node degree cannot exceed n-1 in a simple graph".to_string(),
1049 ));
1050 }
1051 }
1052
1053 let mut _attempts = 0;
1054
1055 while _attempts < max_attempts {
1056 let mut graph = Graph::new();
1057
1058 for i in 0..n {
1060 graph.add_node(i);
1061 }
1062
1063 let mut stubs = Vec::new();
1065 for (node_id, °ree) in degree_sequence.iter().enumerate() {
1066 for _ in 0..degree {
1067 stubs.push(node_id);
1068 }
1069 }
1070
1071 let mut success = true;
1072
1073 while stubs.len() >= 2 && success {
1075 let idx1 = rng.random_range(0..stubs.len());
1077 let stub1 = stubs[idx1];
1078
1079 let idx2 = rng.random_range(0..stubs.len());
1080 let stub2 = stubs[idx2];
1081
1082 if stub1 == stub2 || graph.has_edge(&stub1, &stub2) {
1084 let mut retries = 0;
1086 let mut found_valid = false;
1087
1088 while retries < 50 && !found_valid {
1089 let new_idx2 = rng.random_range(0..stubs.len());
1090 let new_stub2 = stubs[new_idx2];
1091
1092 if stub1 != new_stub2 && !graph.has_edge(&stub1, &new_stub2) {
1093 if idx1 > new_idx2 {
1096 stubs.remove(idx1);
1097 stubs.remove(new_idx2);
1098 } else {
1099 stubs.remove(new_idx2);
1100 stubs.remove(idx1);
1101 }
1102 graph.add_edge(stub1, new_stub2, 1.0)?;
1103 found_valid = true;
1104 }
1105 retries += 1;
1106 }
1107
1108 if !found_valid {
1109 success = false;
1110 }
1111 } else {
1112 if idx1 > idx2 {
1115 stubs.remove(idx1);
1116 stubs.remove(idx2);
1117 } else {
1118 stubs.remove(idx2);
1119 stubs.remove(idx1);
1120 }
1121 graph.add_edge(stub1, stub2, 1.0)?;
1122 }
1123 }
1124
1125 if success && stubs.is_empty() {
1126 return Ok(graph);
1127 }
1128
1129 _attempts += 1;
1130 }
1131
1132 Err(GraphError::InvalidGraph(
1133 "Could not generate simple graph with given degree _sequence after maximum _attempts"
1134 .to_string(),
1135 ))
1136}
1137
1138#[allow(dead_code)]
1157pub fn random_geometric_graph<R: Rng>(
1158 n: usize,
1159 radius: f64,
1160 rng: &mut R,
1161) -> Result<Graph<usize, f64>> {
1162 if radius < 0.0 {
1163 return Err(GraphError::InvalidGraph(
1164 "Radius must be non-negative".to_string(),
1165 ));
1166 }
1167
1168 let mut graph = Graph::new();
1169
1170 for i in 0..n {
1172 graph.add_node(i);
1173 }
1174
1175 if n == 0 {
1176 return Ok(graph);
1177 }
1178
1179 let positions: Vec<(f64, f64)> = (0..n)
1181 .map(|_| (rng.random::<f64>(), rng.random::<f64>()))
1182 .collect();
1183
1184 let radius_sq = radius * radius;
1185
1186 for i in 0..n {
1188 for j in (i + 1)..n {
1189 let dx = positions[i].0 - positions[j].0;
1190 let dy = positions[i].1 - positions[j].1;
1191 let dist_sq = dx * dx + dy * dy;
1192
1193 if dist_sq <= radius_sq {
1194 let dist = dist_sq.sqrt();
1195 graph.add_edge(i, j, dist)?;
1196 }
1197 }
1198 }
1199
1200 Ok(graph)
1201}
1202
1203#[allow(dead_code)]
1228pub fn power_law_cluster_graph<R: Rng>(
1229 n: usize,
1230 m: usize,
1231 p_triangle: f64,
1232 rng: &mut R,
1233) -> Result<Graph<usize, f64>> {
1234 if m == 0 {
1235 return Err(GraphError::InvalidGraph("m must be positive".to_string()));
1236 }
1237 if m >= n {
1238 return Err(GraphError::InvalidGraph(
1239 "m must be less than n".to_string(),
1240 ));
1241 }
1242 if !(0.0..=1.0).contains(&p_triangle) {
1243 return Err(GraphError::InvalidGraph(
1244 "p_triangle must be between 0 and 1".to_string(),
1245 ));
1246 }
1247
1248 let mut graph = Graph::new();
1249
1250 for i in 0..=m {
1252 graph.add_node(i);
1253 }
1254 for i in 0..=m {
1255 for j in (i + 1)..=m {
1256 graph.add_edge(i, j, 1.0)?;
1257 }
1258 }
1259
1260 let mut degrees = vec![m; m + 1];
1262 let mut total_degree = m * (m + 1);
1263
1264 for new_node in (m + 1)..n {
1266 graph.add_node(new_node);
1267
1268 let mut targets_added: HashSet<usize> = HashSet::new();
1269 let mut edges_to_add = m;
1270
1271 if edges_to_add > 0 {
1273 let target = select_preferential_attachment(
1274 °rees,
1275 total_degree,
1276 &targets_added,
1277 new_node,
1278 rng,
1279 );
1280 if let Some(t) = target {
1281 graph.add_edge(new_node, t, 1.0)?;
1282 targets_added.insert(t);
1283 degrees[t] += 1;
1284 total_degree += 2;
1285 edges_to_add -= 1;
1286 }
1287 }
1288
1289 while edges_to_add > 0 {
1291 if rng.random::<f64>() < p_triangle && !targets_added.is_empty() {
1292 let last_target = *targets_added.iter().last().unwrap_or(&0);
1295 let neighbors_of_target = graph.neighbors(&last_target).unwrap_or_default();
1296
1297 let candidates: Vec<usize> = neighbors_of_target
1299 .into_iter()
1300 .filter(|nb| *nb != new_node && !targets_added.contains(nb))
1301 .collect();
1302
1303 if let Some(&chosen) = candidates.choose(rng) {
1304 graph.add_edge(new_node, chosen, 1.0)?;
1305 targets_added.insert(chosen);
1306 degrees[chosen] += 1;
1307 total_degree += 2;
1308 edges_to_add -= 1;
1309 continue;
1310 }
1311 }
1313
1314 let target = select_preferential_attachment(
1316 °rees,
1317 total_degree,
1318 &targets_added,
1319 new_node,
1320 rng,
1321 );
1322 if let Some(t) = target {
1323 graph.add_edge(new_node, t, 1.0)?;
1324 targets_added.insert(t);
1325 degrees[t] += 1;
1326 total_degree += 2;
1327 edges_to_add -= 1;
1328 } else {
1329 break;
1331 }
1332 }
1333
1334 degrees.push(targets_added.len());
1335 }
1336
1337 Ok(graph)
1338}
1339
1340fn select_preferential_attachment<R: Rng>(
1342 degrees: &[usize],
1343 total_degree: usize,
1344 excluded: &HashSet<usize>,
1345 new_node: usize,
1346 rng: &mut R,
1347) -> Option<usize> {
1348 if total_degree == 0 {
1349 return None;
1350 }
1351
1352 for _ in 0..100 {
1354 let mut cumulative = 0.0;
1355 let random_value = rng.random::<f64>() * total_degree as f64;
1356
1357 for (node_id, °ree) in degrees.iter().enumerate() {
1358 if node_id == new_node {
1359 continue;
1360 }
1361 cumulative += degree as f64;
1362 if random_value <= cumulative && !excluded.contains(&node_id) {
1363 return Some(node_id);
1364 }
1365 }
1366 }
1367 None
1368}
1369
1370#[cfg(test)]
1371mod tests {
1372 use super::*;
1373
1374 #[test]
1375 fn test_erdos_renyi_graph() {
1376 let mut rng = StdRng::seed_from_u64(42);
1377 let graph = erdos_renyi_graph(10, 0.3, &mut rng).expect("Operation failed");
1378
1379 assert_eq!(graph.node_count(), 10);
1380 assert!(graph.edge_count() <= 45);
1383 }
1384
1385 #[test]
1386 fn test_complete_graph() {
1387 let graph = complete_graph(5).expect("Operation failed");
1388
1389 assert_eq!(graph.node_count(), 5);
1390 assert_eq!(graph.edge_count(), 10); }
1392
1393 #[test]
1394 fn test_star_graph() {
1395 let graph = star_graph(6).expect("Operation failed");
1396
1397 assert_eq!(graph.node_count(), 6);
1398 assert_eq!(graph.edge_count(), 5); }
1400
1401 #[test]
1402 fn test_path_graph() {
1403 let graph = path_graph(5).expect("Operation failed");
1404
1405 assert_eq!(graph.node_count(), 5);
1406 assert_eq!(graph.edge_count(), 4); }
1408
1409 #[test]
1410 fn test_cycle_graph() {
1411 let graph = cycle_graph(5).expect("Operation failed");
1412
1413 assert_eq!(graph.node_count(), 5);
1414 assert_eq!(graph.edge_count(), 5); assert!(cycle_graph(2).is_err());
1418 }
1419
1420 #[test]
1421 fn test_grid_2d_graph() {
1422 let graph = grid_2d_graph(3, 4).expect("Operation failed");
1423
1424 assert_eq!(graph.node_count(), 12); assert_eq!(graph.edge_count(), 17); }
1427
1428 #[test]
1429 fn test_grid_3d_graph() {
1430 let graph = grid_3d_graph(2, 2, 2).expect("Operation failed");
1431
1432 assert_eq!(graph.node_count(), 8); assert_eq!(graph.edge_count(), 12);
1436 }
1437
1438 #[test]
1439 fn test_triangular_lattice_graph() {
1440 let graph = triangular_lattice_graph(3, 3).expect("Operation failed");
1441
1442 assert_eq!(graph.node_count(), 9); assert!(graph.edge_count() > 12); }
1446
1447 #[test]
1448 fn test_hexagonal_lattice_graph() {
1449 let graph = hexagonal_lattice_graph(3, 3).expect("Operation failed");
1450
1451 assert_eq!(graph.node_count(), 9); assert!(graph.edge_count() >= 6);
1454 }
1455
1456 #[test]
1457 fn test_barabasi_albert_graph() {
1458 let mut rng = StdRng::seed_from_u64(42);
1459 let graph = barabasi_albert_graph(10, 2, &mut rng).expect("Operation failed");
1460
1461 assert_eq!(graph.node_count(), 10);
1462 assert_eq!(graph.edge_count(), 17);
1464 }
1465
1466 #[test]
1467 fn test_stochastic_block_model() {
1468 let mut rng = StdRng::seed_from_u64(42);
1469
1470 let block_sizes = vec![3, 4];
1472 let block_matrix = vec![vec![0.8, 0.1], vec![0.1, 0.8]];
1474
1475 let graph = stochastic_block_model(&block_sizes, &block_matrix, &mut rng)
1476 .expect("Operation failed");
1477
1478 assert_eq!(graph.node_count(), 7); for i in 0..7 {
1482 assert!(graph.has_node(&i));
1483 }
1484 }
1485
1486 #[test]
1487 fn test_two_community_sbm() {
1488 let mut rng = StdRng::seed_from_u64(42);
1489
1490 let graph = two_community_sbm(5, 5, 0.8, 0.1, &mut rng).expect("Operation failed");
1491
1492 assert_eq!(graph.node_count(), 10);
1493
1494 assert!(graph.edge_count() > 0);
1497 }
1498
1499 #[test]
1500 fn test_planted_partition_model() {
1501 let mut rng = StdRng::seed_from_u64(42);
1502
1503 let graph = planted_partition_model(12, 3, 0.7, 0.1, &mut rng).expect("Operation failed");
1504
1505 assert_eq!(graph.node_count(), 12); assert!(graph.edge_count() > 0);
1510 }
1511
1512 #[test]
1513 fn test_stochastic_block_model_errors() {
1514 let mut rng = StdRng::seed_from_u64(42);
1515
1516 assert!(stochastic_block_model(&[], &[], &mut rng).is_err());
1518
1519 let block_sizes = vec![3, 4];
1521 let wrong_matrix = vec![vec![0.5]];
1522 assert!(stochastic_block_model(&block_sizes, &wrong_matrix, &mut rng).is_err());
1523
1524 let bad_matrix = vec![vec![1.5, 0.5], vec![0.5, 0.5]];
1526 assert!(stochastic_block_model(&block_sizes, &bad_matrix, &mut rng).is_err());
1527
1528 assert!(planted_partition_model(10, 3, 0.5, 0.1, &mut rng).is_err());
1530 }
1531
1532 #[test]
1533 fn test_configuration_model() {
1534 let mut rng = StdRng::seed_from_u64(42);
1535
1536 let degree_sequence = vec![2, 2, 2, 2]; let graph = configuration_model(°ree_sequence, &mut rng).expect("Operation failed");
1539
1540 assert_eq!(graph.node_count(), 4);
1541 assert_eq!(graph.edge_count(), 4);
1543
1544 for (i, &expected_degree) in degree_sequence.iter().enumerate() {
1546 let actual_degree = graph.degree(&i);
1547 assert_eq!(actual_degree, expected_degree);
1548 }
1549 }
1550
1551 #[test]
1552 fn test_configuration_model_errors() {
1553 let mut rng = StdRng::seed_from_u64(42);
1554
1555 let odd_degree_sequence = vec![1, 2, 2]; assert!(configuration_model(&odd_degree_sequence, &mut rng).is_err());
1558
1559 let empty_sequence = vec![];
1561 let graph = configuration_model(&empty_sequence, &mut rng).expect("Operation failed");
1562 assert_eq!(graph.node_count(), 0);
1563 }
1564
1565 #[test]
1566 fn test_simple_configuration_model() {
1567 let mut rng = StdRng::seed_from_u64(42);
1568
1569 let degree_sequence = vec![2, 2, 2, 2]; let graph =
1572 simple_configuration_model(°ree_sequence, &mut rng, 100).expect("Operation failed");
1573
1574 assert_eq!(graph.node_count(), 4);
1575 assert_eq!(graph.edge_count(), 4);
1576
1577 for i in 0..4 {
1579 assert!(!graph.has_edge(&i, &i), "Graph should not have self-loops");
1580 }
1581
1582 for (i, &expected_degree) in degree_sequence.iter().enumerate() {
1584 let actual_degree = graph.degree(&i);
1585 assert_eq!(actual_degree, expected_degree);
1586 }
1587 }
1588
1589 #[test]
1590 fn test_simple_configuration_model_errors() {
1591 let mut rng = StdRng::seed_from_u64(42);
1592
1593 let invalid_degree_sequence = vec![4, 2, 2, 2]; assert!(simple_configuration_model(&invalid_degree_sequence, &mut rng, 10).is_err());
1596
1597 let odd_degree_sequence = vec![1, 2, 2]; assert!(simple_configuration_model(&odd_degree_sequence, &mut rng, 10).is_err());
1600 }
1601
1602 #[test]
1603 fn test_tree_graph() {
1604 let mut rng = StdRng::seed_from_u64(42);
1605
1606 let empty_tree = tree_graph(0, &mut rng).expect("Operation failed");
1608 assert_eq!(empty_tree.node_count(), 0);
1609 assert_eq!(empty_tree.edge_count(), 0);
1610
1611 let single_tree = tree_graph(1, &mut rng).expect("Operation failed");
1613 assert_eq!(single_tree.node_count(), 1);
1614 assert_eq!(single_tree.edge_count(), 0);
1615
1616 let tree = tree_graph(5, &mut rng).expect("Operation failed");
1618 assert_eq!(tree.node_count(), 5);
1619 assert_eq!(tree.edge_count(), 4); for i in 0..5 {
1623 assert!(tree.has_node(&i));
1624 }
1625 }
1626
1627 #[test]
1628 fn test_random_spanning_tree() {
1629 let mut rng = StdRng::seed_from_u64(42);
1630
1631 let complete = complete_graph(4).expect("Operation failed");
1633
1634 let spanning_tree = random_spanning_tree(&complete, &mut rng).expect("Operation failed");
1636
1637 assert_eq!(spanning_tree.node_count(), 4);
1638 assert_eq!(spanning_tree.edge_count(), 3); for i in 0..4 {
1642 assert!(spanning_tree.has_node(&i));
1643 }
1644 }
1645
1646 #[test]
1647 fn test_forest_graph() {
1648 let mut rng = StdRng::seed_from_u64(42);
1649
1650 let tree_sizes = vec![3, 2, 4];
1652 let forest = forest_graph(&tree_sizes, &tree_sizes, &mut rng).expect("Operation failed");
1653
1654 assert_eq!(forest.node_count(), 9); assert_eq!(forest.edge_count(), 6); for i in 0..9 {
1659 assert!(forest.has_node(&i));
1660 }
1661
1662 let empty_forest = forest_graph(&[], &[], &mut rng).expect("Operation failed");
1664 assert_eq!(empty_forest.node_count(), 0);
1665 assert_eq!(empty_forest.edge_count(), 0);
1666
1667 let forest_with_zeros =
1669 forest_graph(&[0, 3, 0, 2], &[0, 3, 0, 2], &mut rng).expect("Operation failed");
1670 assert_eq!(forest_with_zeros.node_count(), 5); assert_eq!(forest_with_zeros.edge_count(), 3); }
1673
1674 #[test]
1675 fn test_random_geometric_graph() {
1676 let mut rng = StdRng::seed_from_u64(42);
1677
1678 let graph = random_geometric_graph(20, 0.4, &mut rng).expect("Operation failed");
1679 assert_eq!(graph.node_count(), 20);
1680 assert!(graph.edge_count() > 0);
1682 assert!(graph.edge_count() < 20 * 19 / 2);
1683
1684 for edge in graph.edges() {
1686 assert!(edge.weight > 0.0);
1687 assert!(edge.weight <= 0.4 + 1e-10); }
1689 }
1690
1691 #[test]
1692 fn test_random_geometric_graph_large_radius() {
1693 let mut rng = StdRng::seed_from_u64(42);
1694
1695 let graph = random_geometric_graph(5, 2.0, &mut rng).expect("Operation failed");
1697 assert_eq!(graph.node_count(), 5);
1698 assert_eq!(graph.edge_count(), 10); }
1701
1702 #[test]
1703 fn test_random_geometric_graph_zero_radius() {
1704 let mut rng = StdRng::seed_from_u64(42);
1705
1706 let graph = random_geometric_graph(10, 0.0, &mut rng).expect("Operation failed");
1707 assert_eq!(graph.node_count(), 10);
1708 assert_eq!(graph.edge_count(), 0); }
1710
1711 #[test]
1712 fn test_random_geometric_graph_errors() {
1713 let mut rng = StdRng::seed_from_u64(42);
1714 assert!(random_geometric_graph(10, -0.1, &mut rng).is_err());
1715 }
1716
1717 #[test]
1718 fn test_random_geometric_graph_empty() {
1719 let mut rng = StdRng::seed_from_u64(42);
1720 let graph = random_geometric_graph(0, 0.5, &mut rng).expect("Operation failed");
1721 assert_eq!(graph.node_count(), 0);
1722 assert_eq!(graph.edge_count(), 0);
1723 }
1724
1725 #[test]
1726 fn test_power_law_cluster_graph() {
1727 let mut rng = StdRng::seed_from_u64(42);
1728
1729 let graph = power_law_cluster_graph(20, 2, 0.5, &mut rng).expect("Operation failed");
1730 assert_eq!(graph.node_count(), 20);
1731 assert!(graph.edge_count() > 0);
1733 }
1734
1735 #[test]
1736 fn test_power_law_cluster_no_triangle() {
1737 let mut rng = StdRng::seed_from_u64(42);
1738
1739 let graph = power_law_cluster_graph(15, 2, 0.0, &mut rng).expect("Operation failed");
1741 assert_eq!(graph.node_count(), 15);
1742 assert!(graph.edge_count() > 0);
1743 }
1744
1745 #[test]
1746 fn test_power_law_cluster_max_triangle() {
1747 let mut rng = StdRng::seed_from_u64(42);
1748
1749 let graph = power_law_cluster_graph(15, 2, 1.0, &mut rng).expect("Operation failed");
1751 assert_eq!(graph.node_count(), 15);
1752 assert!(graph.edge_count() > 0);
1753 }
1754
1755 #[test]
1756 fn test_power_law_cluster_errors() {
1757 let mut rng = StdRng::seed_from_u64(42);
1758
1759 assert!(power_law_cluster_graph(10, 0, 0.5, &mut rng).is_err());
1761 assert!(power_law_cluster_graph(5, 5, 0.5, &mut rng).is_err());
1763 assert!(power_law_cluster_graph(10, 2, 1.5, &mut rng).is_err());
1765 assert!(power_law_cluster_graph(10, 2, -0.1, &mut rng).is_err());
1766 }
1767}