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);
773 while new_target == edge.source || new_graph.has_node(&new_target) {
774 new_target = rng.random_range(0..n);
775 }
776
777 new_graph.add_edge(edge.source, new_target, 1.0)?;
778 graph = new_graph;
779 }
780 }
781
782 Ok(graph)
783}
784
785#[allow(dead_code)]
800pub fn stochastic_block_model<R: Rng>(
801 block_sizes: &[usize],
802 block_matrix: &[Vec<f64>],
803 rng: &mut R,
804) -> Result<Graph<usize, f64>> {
805 if block_sizes.is_empty() {
806 return Err(GraphError::InvalidGraph(
807 "At least one block must be specified".to_string(),
808 ));
809 }
810
811 if block_matrix.len() != block_sizes.len() {
812 return Err(GraphError::InvalidGraph(
813 "Block _matrix dimensions must match number of blocks".to_string(),
814 ));
815 }
816
817 for row in block_matrix {
818 if row.len() != block_sizes.len() {
819 return Err(GraphError::InvalidGraph(
820 "Block _matrix must be square".to_string(),
821 ));
822 }
823 for &prob in row {
824 if !(0.0..=1.0).contains(&prob) {
825 return Err(GraphError::InvalidGraph(
826 "All probabilities must be between 0 and 1".to_string(),
827 ));
828 }
829 }
830 }
831
832 let total_nodes: usize = block_sizes.iter().sum();
833 let mut graph = Graph::new();
834
835 for i in 0..total_nodes {
837 graph.add_node(i);
838 }
839
840 let mut node_to_block = vec![0; total_nodes];
842 let mut current_node = 0;
843 for (block_id, &block_size) in block_sizes.iter().enumerate() {
844 for _ in 0..block_size {
845 node_to_block[current_node] = block_id;
846 current_node += 1;
847 }
848 }
849
850 for i in 0..total_nodes {
852 for j in (i + 1)..total_nodes {
853 let block_i = node_to_block[i];
854 let block_j = node_to_block[j];
855 let prob = block_matrix[block_i][block_j];
856
857 if rng.random::<f64>() < prob {
858 graph.add_edge(i, j, 1.0)?;
859 }
860 }
861 }
862
863 Ok(graph)
864}
865
866#[allow(dead_code)]
881pub fn two_community_sbm<R: Rng>(
882 n1: usize,
883 n2: usize,
884 p_in: f64,
885 p_out: f64,
886 rng: &mut R,
887) -> Result<Graph<usize, f64>> {
888 let block_sizes = vec![n1, n2];
889 let block_matrix = vec![vec![p_in, p_out], vec![p_out, p_in]];
890
891 stochastic_block_model(&block_sizes, &block_matrix, rng)
892}
893
894#[allow(dead_code)]
909pub fn planted_partition_model<R: Rng>(
910 n: usize,
911 k: usize,
912 p_in: f64,
913 p_out: f64,
914 rng: &mut R,
915) -> Result<Graph<usize, f64>> {
916 if !n.is_multiple_of(k) {
917 return Err(GraphError::InvalidGraph(
918 "Number of nodes must be divisible by number of communities".to_string(),
919 ));
920 }
921
922 let community_size = n / k;
923 let block_sizes = vec![community_size; k];
924
925 let mut block_matrix = vec![vec![p_out; k]; k];
927 for (i, row) in block_matrix.iter_mut().enumerate().take(k) {
928 row[i] = p_in;
929 }
930
931 stochastic_block_model(&block_sizes, &block_matrix, rng)
932}
933
934#[allow(dead_code)]
954pub fn configuration_model<R: Rng>(
955 degree_sequence: &[usize],
956 rng: &mut R,
957) -> Result<Graph<usize, f64>> {
958 if degree_sequence.is_empty() {
959 return Ok(Graph::new());
960 }
961
962 let total_degree: usize = degree_sequence.iter().sum();
964 if !total_degree.is_multiple_of(2) {
965 return Err(GraphError::InvalidGraph(
966 "Sum of degrees must be even".to_string(),
967 ));
968 }
969
970 let n = degree_sequence.len();
971 let mut graph = Graph::new();
972
973 for i in 0..n {
975 graph.add_node(i);
976 }
977
978 let mut stubs = Vec::new();
980 for (node_id, °ree) in degree_sequence.iter().enumerate() {
981 for _ in 0..degree {
982 stubs.push(node_id);
983 }
984 }
985
986 while stubs.len() >= 2 {
988 let idx1 = rng.random_range(0..stubs.len());
990 let stub1 = stubs.remove(idx1);
991
992 let idx2 = rng.random_range(0..stubs.len());
993 let stub2 = stubs.remove(idx2);
994
995 graph.add_edge(stub1, stub2, 1.0)?;
997 }
998
999 Ok(graph)
1000}
1001
1002#[allow(dead_code)]
1016pub fn simple_configuration_model<R: Rng>(
1017 degree_sequence: &[usize],
1018 rng: &mut R,
1019 max_attempts: usize,
1020) -> Result<Graph<usize, f64>> {
1021 if degree_sequence.is_empty() {
1022 return Ok(Graph::new());
1023 }
1024
1025 let total_degree: usize = degree_sequence.iter().sum();
1027 if !total_degree.is_multiple_of(2) {
1028 return Err(GraphError::InvalidGraph(
1029 "Sum of degrees must be even".to_string(),
1030 ));
1031 }
1032
1033 let n = degree_sequence.len();
1034
1035 for °ree in degree_sequence {
1037 if degree >= n {
1038 return Err(GraphError::InvalidGraph(
1039 "Node degree cannot exceed n-1 in a simple graph".to_string(),
1040 ));
1041 }
1042 }
1043
1044 let mut _attempts = 0;
1045
1046 while _attempts < max_attempts {
1047 let mut graph = Graph::new();
1048
1049 for i in 0..n {
1051 graph.add_node(i);
1052 }
1053
1054 let mut stubs = Vec::new();
1056 for (node_id, °ree) in degree_sequence.iter().enumerate() {
1057 for _ in 0..degree {
1058 stubs.push(node_id);
1059 }
1060 }
1061
1062 let mut success = true;
1063
1064 while stubs.len() >= 2 && success {
1066 let idx1 = rng.random_range(0..stubs.len());
1068 let stub1 = stubs[idx1];
1069
1070 let idx2 = rng.random_range(0..stubs.len());
1071 let stub2 = stubs[idx2];
1072
1073 if stub1 == stub2 || graph.has_edge(&stub1, &stub2) {
1075 let mut retries = 0;
1077 let mut found_valid = false;
1078
1079 while retries < 50 && !found_valid {
1080 let new_idx2 = rng.random_range(0..stubs.len());
1081 let new_stub2 = stubs[new_idx2];
1082
1083 if stub1 != new_stub2 && !graph.has_edge(&stub1, &new_stub2) {
1084 if idx1 > new_idx2 {
1087 stubs.remove(idx1);
1088 stubs.remove(new_idx2);
1089 } else {
1090 stubs.remove(new_idx2);
1091 stubs.remove(idx1);
1092 }
1093 graph.add_edge(stub1, new_stub2, 1.0)?;
1094 found_valid = true;
1095 }
1096 retries += 1;
1097 }
1098
1099 if !found_valid {
1100 success = false;
1101 }
1102 } else {
1103 if idx1 > idx2 {
1106 stubs.remove(idx1);
1107 stubs.remove(idx2);
1108 } else {
1109 stubs.remove(idx2);
1110 stubs.remove(idx1);
1111 }
1112 graph.add_edge(stub1, stub2, 1.0)?;
1113 }
1114 }
1115
1116 if success && stubs.is_empty() {
1117 return Ok(graph);
1118 }
1119
1120 _attempts += 1;
1121 }
1122
1123 Err(GraphError::InvalidGraph(
1124 "Could not generate simple graph with given degree _sequence after maximum _attempts"
1125 .to_string(),
1126 ))
1127}
1128
1129#[allow(dead_code)]
1148pub fn random_geometric_graph<R: Rng>(
1149 n: usize,
1150 radius: f64,
1151 rng: &mut R,
1152) -> Result<Graph<usize, f64>> {
1153 if radius < 0.0 {
1154 return Err(GraphError::InvalidGraph(
1155 "Radius must be non-negative".to_string(),
1156 ));
1157 }
1158
1159 let mut graph = Graph::new();
1160
1161 for i in 0..n {
1163 graph.add_node(i);
1164 }
1165
1166 if n == 0 {
1167 return Ok(graph);
1168 }
1169
1170 let positions: Vec<(f64, f64)> = (0..n)
1172 .map(|_| (rng.random::<f64>(), rng.random::<f64>()))
1173 .collect();
1174
1175 let radius_sq = radius * radius;
1176
1177 for i in 0..n {
1179 for j in (i + 1)..n {
1180 let dx = positions[i].0 - positions[j].0;
1181 let dy = positions[i].1 - positions[j].1;
1182 let dist_sq = dx * dx + dy * dy;
1183
1184 if dist_sq <= radius_sq {
1185 let dist = dist_sq.sqrt();
1186 graph.add_edge(i, j, dist)?;
1187 }
1188 }
1189 }
1190
1191 Ok(graph)
1192}
1193
1194#[allow(dead_code)]
1219pub fn power_law_cluster_graph<R: Rng>(
1220 n: usize,
1221 m: usize,
1222 p_triangle: f64,
1223 rng: &mut R,
1224) -> Result<Graph<usize, f64>> {
1225 if m == 0 {
1226 return Err(GraphError::InvalidGraph("m must be positive".to_string()));
1227 }
1228 if m >= n {
1229 return Err(GraphError::InvalidGraph(
1230 "m must be less than n".to_string(),
1231 ));
1232 }
1233 if !(0.0..=1.0).contains(&p_triangle) {
1234 return Err(GraphError::InvalidGraph(
1235 "p_triangle must be between 0 and 1".to_string(),
1236 ));
1237 }
1238
1239 let mut graph = Graph::new();
1240
1241 for i in 0..=m {
1243 graph.add_node(i);
1244 }
1245 for i in 0..=m {
1246 for j in (i + 1)..=m {
1247 graph.add_edge(i, j, 1.0)?;
1248 }
1249 }
1250
1251 let mut degrees = vec![m; m + 1];
1253 let mut total_degree = m * (m + 1);
1254
1255 for new_node in (m + 1)..n {
1257 graph.add_node(new_node);
1258
1259 let mut targets_added: HashSet<usize> = HashSet::new();
1260 let mut edges_to_add = m;
1261
1262 if edges_to_add > 0 {
1264 let target = select_preferential_attachment(
1265 °rees,
1266 total_degree,
1267 &targets_added,
1268 new_node,
1269 rng,
1270 );
1271 if let Some(t) = target {
1272 graph.add_edge(new_node, t, 1.0)?;
1273 targets_added.insert(t);
1274 degrees[t] += 1;
1275 total_degree += 2;
1276 edges_to_add -= 1;
1277 }
1278 }
1279
1280 while edges_to_add > 0 {
1282 if rng.random::<f64>() < p_triangle && !targets_added.is_empty() {
1283 let last_target = *targets_added.iter().last().unwrap_or(&0);
1286 let neighbors_of_target = graph.neighbors(&last_target).unwrap_or_default();
1287
1288 let candidates: Vec<usize> = neighbors_of_target
1290 .into_iter()
1291 .filter(|nb| *nb != new_node && !targets_added.contains(nb))
1292 .collect();
1293
1294 if let Some(&chosen) = candidates.choose(rng) {
1295 graph.add_edge(new_node, chosen, 1.0)?;
1296 targets_added.insert(chosen);
1297 degrees[chosen] += 1;
1298 total_degree += 2;
1299 edges_to_add -= 1;
1300 continue;
1301 }
1302 }
1304
1305 let target = select_preferential_attachment(
1307 °rees,
1308 total_degree,
1309 &targets_added,
1310 new_node,
1311 rng,
1312 );
1313 if let Some(t) = target {
1314 graph.add_edge(new_node, t, 1.0)?;
1315 targets_added.insert(t);
1316 degrees[t] += 1;
1317 total_degree += 2;
1318 edges_to_add -= 1;
1319 } else {
1320 break;
1322 }
1323 }
1324
1325 degrees.push(targets_added.len());
1326 }
1327
1328 Ok(graph)
1329}
1330
1331fn select_preferential_attachment<R: Rng>(
1333 degrees: &[usize],
1334 total_degree: usize,
1335 excluded: &HashSet<usize>,
1336 new_node: usize,
1337 rng: &mut R,
1338) -> Option<usize> {
1339 if total_degree == 0 {
1340 return None;
1341 }
1342
1343 for _ in 0..100 {
1345 let mut cumulative = 0.0;
1346 let random_value = rng.random::<f64>() * total_degree as f64;
1347
1348 for (node_id, °ree) in degrees.iter().enumerate() {
1349 if node_id == new_node {
1350 continue;
1351 }
1352 cumulative += degree as f64;
1353 if random_value <= cumulative && !excluded.contains(&node_id) {
1354 return Some(node_id);
1355 }
1356 }
1357 }
1358 None
1359}
1360
1361#[cfg(test)]
1362mod tests {
1363 use super::*;
1364
1365 #[test]
1366 fn test_erdos_renyi_graph() {
1367 let mut rng = StdRng::seed_from_u64(42);
1368 let graph = erdos_renyi_graph(10, 0.3, &mut rng).expect("Operation failed");
1369
1370 assert_eq!(graph.node_count(), 10);
1371 assert!(graph.edge_count() <= 45);
1374 }
1375
1376 #[test]
1377 fn test_complete_graph() {
1378 let graph = complete_graph(5).expect("Operation failed");
1379
1380 assert_eq!(graph.node_count(), 5);
1381 assert_eq!(graph.edge_count(), 10); }
1383
1384 #[test]
1385 fn test_star_graph() {
1386 let graph = star_graph(6).expect("Operation failed");
1387
1388 assert_eq!(graph.node_count(), 6);
1389 assert_eq!(graph.edge_count(), 5); }
1391
1392 #[test]
1393 fn test_path_graph() {
1394 let graph = path_graph(5).expect("Operation failed");
1395
1396 assert_eq!(graph.node_count(), 5);
1397 assert_eq!(graph.edge_count(), 4); }
1399
1400 #[test]
1401 fn test_cycle_graph() {
1402 let graph = cycle_graph(5).expect("Operation failed");
1403
1404 assert_eq!(graph.node_count(), 5);
1405 assert_eq!(graph.edge_count(), 5); assert!(cycle_graph(2).is_err());
1409 }
1410
1411 #[test]
1412 fn test_grid_2d_graph() {
1413 let graph = grid_2d_graph(3, 4).expect("Operation failed");
1414
1415 assert_eq!(graph.node_count(), 12); assert_eq!(graph.edge_count(), 17); }
1418
1419 #[test]
1420 fn test_grid_3d_graph() {
1421 let graph = grid_3d_graph(2, 2, 2).expect("Operation failed");
1422
1423 assert_eq!(graph.node_count(), 8); assert_eq!(graph.edge_count(), 12);
1427 }
1428
1429 #[test]
1430 fn test_triangular_lattice_graph() {
1431 let graph = triangular_lattice_graph(3, 3).expect("Operation failed");
1432
1433 assert_eq!(graph.node_count(), 9); assert!(graph.edge_count() > 12); }
1437
1438 #[test]
1439 fn test_hexagonal_lattice_graph() {
1440 let graph = hexagonal_lattice_graph(3, 3).expect("Operation failed");
1441
1442 assert_eq!(graph.node_count(), 9); assert!(graph.edge_count() >= 6);
1445 }
1446
1447 #[test]
1448 fn test_barabasi_albert_graph() {
1449 let mut rng = StdRng::seed_from_u64(42);
1450 let graph = barabasi_albert_graph(10, 2, &mut rng).expect("Operation failed");
1451
1452 assert_eq!(graph.node_count(), 10);
1453 assert_eq!(graph.edge_count(), 17);
1455 }
1456
1457 #[test]
1458 fn test_stochastic_block_model() {
1459 let mut rng = StdRng::seed_from_u64(42);
1460
1461 let block_sizes = vec![3, 4];
1463 let block_matrix = vec![vec![0.8, 0.1], vec![0.1, 0.8]];
1465
1466 let graph = stochastic_block_model(&block_sizes, &block_matrix, &mut rng)
1467 .expect("Operation failed");
1468
1469 assert_eq!(graph.node_count(), 7); for i in 0..7 {
1473 assert!(graph.has_node(&i));
1474 }
1475 }
1476
1477 #[test]
1478 fn test_two_community_sbm() {
1479 let mut rng = StdRng::seed_from_u64(42);
1480
1481 let graph = two_community_sbm(5, 5, 0.8, 0.1, &mut rng).expect("Operation failed");
1482
1483 assert_eq!(graph.node_count(), 10);
1484
1485 assert!(graph.edge_count() > 0);
1488 }
1489
1490 #[test]
1491 fn test_planted_partition_model() {
1492 let mut rng = StdRng::seed_from_u64(42);
1493
1494 let graph = planted_partition_model(12, 3, 0.7, 0.1, &mut rng).expect("Operation failed");
1495
1496 assert_eq!(graph.node_count(), 12); assert!(graph.edge_count() > 0);
1501 }
1502
1503 #[test]
1504 fn test_stochastic_block_model_errors() {
1505 let mut rng = StdRng::seed_from_u64(42);
1506
1507 assert!(stochastic_block_model(&[], &[], &mut rng).is_err());
1509
1510 let block_sizes = vec![3, 4];
1512 let wrong_matrix = vec![vec![0.5]];
1513 assert!(stochastic_block_model(&block_sizes, &wrong_matrix, &mut rng).is_err());
1514
1515 let bad_matrix = vec![vec![1.5, 0.5], vec![0.5, 0.5]];
1517 assert!(stochastic_block_model(&block_sizes, &bad_matrix, &mut rng).is_err());
1518
1519 assert!(planted_partition_model(10, 3, 0.5, 0.1, &mut rng).is_err());
1521 }
1522
1523 #[test]
1524 fn test_configuration_model() {
1525 let mut rng = StdRng::seed_from_u64(42);
1526
1527 let degree_sequence = vec![2, 2, 2, 2]; let graph = configuration_model(°ree_sequence, &mut rng).expect("Operation failed");
1530
1531 assert_eq!(graph.node_count(), 4);
1532 assert_eq!(graph.edge_count(), 4);
1534
1535 for (i, &expected_degree) in degree_sequence.iter().enumerate() {
1537 let actual_degree = graph.degree(&i);
1538 assert_eq!(actual_degree, expected_degree);
1539 }
1540 }
1541
1542 #[test]
1543 fn test_configuration_model_errors() {
1544 let mut rng = StdRng::seed_from_u64(42);
1545
1546 let odd_degree_sequence = vec![1, 2, 2]; assert!(configuration_model(&odd_degree_sequence, &mut rng).is_err());
1549
1550 let empty_sequence = vec![];
1552 let graph = configuration_model(&empty_sequence, &mut rng).expect("Operation failed");
1553 assert_eq!(graph.node_count(), 0);
1554 }
1555
1556 #[test]
1557 fn test_simple_configuration_model() {
1558 let mut rng = StdRng::seed_from_u64(42);
1559
1560 let degree_sequence = vec![2, 2, 2, 2]; let graph =
1563 simple_configuration_model(°ree_sequence, &mut rng, 100).expect("Operation failed");
1564
1565 assert_eq!(graph.node_count(), 4);
1566 assert_eq!(graph.edge_count(), 4);
1567
1568 for i in 0..4 {
1570 assert!(!graph.has_edge(&i, &i), "Graph should not have self-loops");
1571 }
1572
1573 for (i, &expected_degree) in degree_sequence.iter().enumerate() {
1575 let actual_degree = graph.degree(&i);
1576 assert_eq!(actual_degree, expected_degree);
1577 }
1578 }
1579
1580 #[test]
1581 fn test_simple_configuration_model_errors() {
1582 let mut rng = StdRng::seed_from_u64(42);
1583
1584 let invalid_degree_sequence = vec![4, 2, 2, 2]; assert!(simple_configuration_model(&invalid_degree_sequence, &mut rng, 10).is_err());
1587
1588 let odd_degree_sequence = vec![1, 2, 2]; assert!(simple_configuration_model(&odd_degree_sequence, &mut rng, 10).is_err());
1591 }
1592
1593 #[test]
1594 fn test_tree_graph() {
1595 let mut rng = StdRng::seed_from_u64(42);
1596
1597 let empty_tree = tree_graph(0, &mut rng).expect("Operation failed");
1599 assert_eq!(empty_tree.node_count(), 0);
1600 assert_eq!(empty_tree.edge_count(), 0);
1601
1602 let single_tree = tree_graph(1, &mut rng).expect("Operation failed");
1604 assert_eq!(single_tree.node_count(), 1);
1605 assert_eq!(single_tree.edge_count(), 0);
1606
1607 let tree = tree_graph(5, &mut rng).expect("Operation failed");
1609 assert_eq!(tree.node_count(), 5);
1610 assert_eq!(tree.edge_count(), 4); for i in 0..5 {
1614 assert!(tree.has_node(&i));
1615 }
1616 }
1617
1618 #[test]
1619 fn test_random_spanning_tree() {
1620 let mut rng = StdRng::seed_from_u64(42);
1621
1622 let complete = complete_graph(4).expect("Operation failed");
1624
1625 let spanning_tree = random_spanning_tree(&complete, &mut rng).expect("Operation failed");
1627
1628 assert_eq!(spanning_tree.node_count(), 4);
1629 assert_eq!(spanning_tree.edge_count(), 3); for i in 0..4 {
1633 assert!(spanning_tree.has_node(&i));
1634 }
1635 }
1636
1637 #[test]
1638 fn test_forest_graph() {
1639 let mut rng = StdRng::seed_from_u64(42);
1640
1641 let tree_sizes = vec![3, 2, 4];
1643 let forest = forest_graph(&tree_sizes, &tree_sizes, &mut rng).expect("Operation failed");
1644
1645 assert_eq!(forest.node_count(), 9); assert_eq!(forest.edge_count(), 6); for i in 0..9 {
1650 assert!(forest.has_node(&i));
1651 }
1652
1653 let empty_forest = forest_graph(&[], &[], &mut rng).expect("Operation failed");
1655 assert_eq!(empty_forest.node_count(), 0);
1656 assert_eq!(empty_forest.edge_count(), 0);
1657
1658 let forest_with_zeros =
1660 forest_graph(&[0, 3, 0, 2], &[0, 3, 0, 2], &mut rng).expect("Operation failed");
1661 assert_eq!(forest_with_zeros.node_count(), 5); assert_eq!(forest_with_zeros.edge_count(), 3); }
1664
1665 #[test]
1666 fn test_random_geometric_graph() {
1667 let mut rng = StdRng::seed_from_u64(42);
1668
1669 let graph = random_geometric_graph(20, 0.4, &mut rng).expect("Operation failed");
1670 assert_eq!(graph.node_count(), 20);
1671 assert!(graph.edge_count() > 0);
1673 assert!(graph.edge_count() < 20 * 19 / 2);
1674
1675 for edge in graph.edges() {
1677 assert!(edge.weight > 0.0);
1678 assert!(edge.weight <= 0.4 + 1e-10); }
1680 }
1681
1682 #[test]
1683 fn test_random_geometric_graph_large_radius() {
1684 let mut rng = StdRng::seed_from_u64(42);
1685
1686 let graph = random_geometric_graph(5, 2.0, &mut rng).expect("Operation failed");
1688 assert_eq!(graph.node_count(), 5);
1689 assert_eq!(graph.edge_count(), 10); }
1692
1693 #[test]
1694 fn test_random_geometric_graph_zero_radius() {
1695 let mut rng = StdRng::seed_from_u64(42);
1696
1697 let graph = random_geometric_graph(10, 0.0, &mut rng).expect("Operation failed");
1698 assert_eq!(graph.node_count(), 10);
1699 assert_eq!(graph.edge_count(), 0); }
1701
1702 #[test]
1703 fn test_random_geometric_graph_errors() {
1704 let mut rng = StdRng::seed_from_u64(42);
1705 assert!(random_geometric_graph(10, -0.1, &mut rng).is_err());
1706 }
1707
1708 #[test]
1709 fn test_random_geometric_graph_empty() {
1710 let mut rng = StdRng::seed_from_u64(42);
1711 let graph = random_geometric_graph(0, 0.5, &mut rng).expect("Operation failed");
1712 assert_eq!(graph.node_count(), 0);
1713 assert_eq!(graph.edge_count(), 0);
1714 }
1715
1716 #[test]
1717 fn test_power_law_cluster_graph() {
1718 let mut rng = StdRng::seed_from_u64(42);
1719
1720 let graph = power_law_cluster_graph(20, 2, 0.5, &mut rng).expect("Operation failed");
1721 assert_eq!(graph.node_count(), 20);
1722 assert!(graph.edge_count() > 0);
1724 }
1725
1726 #[test]
1727 fn test_power_law_cluster_no_triangle() {
1728 let mut rng = StdRng::seed_from_u64(42);
1729
1730 let graph = power_law_cluster_graph(15, 2, 0.0, &mut rng).expect("Operation failed");
1732 assert_eq!(graph.node_count(), 15);
1733 assert!(graph.edge_count() > 0);
1734 }
1735
1736 #[test]
1737 fn test_power_law_cluster_max_triangle() {
1738 let mut rng = StdRng::seed_from_u64(42);
1739
1740 let graph = power_law_cluster_graph(15, 2, 1.0, &mut rng).expect("Operation failed");
1742 assert_eq!(graph.node_count(), 15);
1743 assert!(graph.edge_count() > 0);
1744 }
1745
1746 #[test]
1747 fn test_power_law_cluster_errors() {
1748 let mut rng = StdRng::seed_from_u64(42);
1749
1750 assert!(power_law_cluster_graph(10, 0, 0.5, &mut rng).is_err());
1752 assert!(power_law_cluster_graph(5, 5, 0.5, &mut rng).is_err());
1754 assert!(power_law_cluster_graph(10, 2, 1.5, &mut rng).is_err());
1756 assert!(power_law_cluster_graph(10, 2, -0.1, &mut rng).is_err());
1757 }
1758}