Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions raphtory/src/graphgen/erdos_renyi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//! Generates a graph using the Erdős-Rényi model
//!
//! # Examples
//!
//! ```
//! use raphtory::graphgen::erdos_renyi::erdos_renyi;
//! let graph = erdos_renyi(1000, 0.1, None).unwrap();
//! ```

use crate::{
db::{
api::{mutation::AdditionOps, view::*},
graph::graph::Graph,
}, errors::GraphError, prelude::{NO_PROPS, NodeStateOps}
};
use rand::{rngs::StdRng, Rng, SeedableRng};
use raphtory_api::core::storage::timeindex::AsTime;
use raphtory_core::entities::GID;
use tracing::error;

/// Generates an Erdős-Rényi random graph and returns it.
///
/// The Erdős-Rényi model creates a random graph by connecting each pair of nodes
/// with a given probability. This implementation creates an undirected graph.
///
/// # Arguments
/// * `nodes_to_add` - Number of nodes to create in the graph.
/// * `p` - Probability of edge creation between any two nodes (0.0 = no edges, 1.0 = fully connected).
/// * `seed` - Optional 64-bit seed for deterministic random generation. If `None`, uses entropy.
///
/// # Returns
/// * `Result<Graph, GraphError>` - A new graph with the generated nodes and edges.
///
/// # Behavior
/// - Creates a new graph and adds `nodes_to_add` nodes with sequential u64 IDs (0, 1, 2, ...).
/// - For each pair of distinct nodes, adds an undirected edge with probability `p`.
/// - Uses the provided seed for reproducible random generation if given.
/// - All nodes and edges are timestamped with incrementing time values.
///
/// # Example
/// ```
/// use raphtory::graphgen::erdos_renyi::erdos_renyi;
///
/// // Create a random graph with 10 nodes and 20% edge probability
/// let graph = erdos_renyi(10, 0.2, Some(42)).unwrap();
/// ```
pub fn erdos_renyi(nodes_to_add: usize, p: f64, seed: Option<u64>) -> Result<Graph, GraphError> {
let graph = Graph::new();
let mut rng;
if let Some(seed_value) = seed {
rng = StdRng::seed_from_u64(seed_value);
} else {
rng = StdRng::from_entropy();
}
let mut latest_time = graph.latest_time().map_or(0, |t| t.t());
for i in 0..nodes_to_add {
let id = GID::U64(i as u64);
latest_time += 1;
graph
.add_node(latest_time, &id, NO_PROPS, None)?;
}
for i in 0..nodes_to_add {
let source_id = GID::U64(i as u64);
for j in (i + 1)..nodes_to_add {
let dst_id = GID::U64(j as u64);
let create_edge = rng.gen_bool(p);
if create_edge {
latest_time += 1;
graph.add_edge(latest_time, &source_id, &dst_id, NO_PROPS, None)?;
graph.add_edge(latest_time, &dst_id, &source_id, NO_PROPS, None)?;
}
}
}
Ok(graph)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::graphgen::erdos_renyi::erdos_renyi;

#[test]
fn test_erdos_renyi_half_probability() {
let n_nodes = 20;
let p = 0.5;
let seed = Some(42);
let graph = erdos_renyi(n_nodes, p, seed).unwrap();
let node_count = graph.nodes().id().iter_values().count();
let edge_count = graph.edges().into_iter().count();
assert_eq!(node_count, n_nodes);
assert!(edge_count > 0);
assert!(edge_count <= n_nodes * (n_nodes - 1));
}

#[test]
fn test_erdos_renyi_zero_probability() {
let n_nodes = 20;
let p = 0.0;
let seed = Some(42);
let graph = erdos_renyi(n_nodes, p, seed).unwrap();
let edge_count = graph.edges().into_iter().count();
let node_count = graph.nodes().id().iter_values().count();
assert_eq!(node_count, n_nodes);
assert_eq!(edge_count, 0);
}

#[test]
fn test_erdos_renyi_full_probability() {
let n_nodes = 20;
let p = 1.0;
let seed = Some(42);
let graph = erdos_renyi(n_nodes, p, seed).unwrap();
let edge_count = graph.edges().into_iter().count();
let node_count = graph.nodes().id().iter_values().count();
assert_eq!(node_count, n_nodes);
assert_eq!(edge_count, (n_nodes * (n_nodes - 1)));
}
}
1 change: 1 addition & 0 deletions raphtory/src/graphgen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::prelude::*;

pub mod preferential_attachment;
pub mod random_attachment;
pub mod erdos_renyi;

pub(crate) fn next_id<'graph, G: GraphViewOps<'graph>>(g: &G, max_gid: Option<GID>) -> GID {
let max_gid = max_gid.unwrap_or_else(|| g.nodes().id().max().unwrap_or(GID::U64(0)));
Expand Down