use crate::star_graph;
use petgraph::graph::{IndexType, NodeIndex};
use petgraph::prelude::EdgeRef;
use petgraph::{EdgeType, Graph};
use rand::distributions::Distribution;
use rand::distributions::Uniform;
use rand::Rng;
pub fn barabasi_albert_graph<
R: Rng + ?Sized,
Ty: EdgeType,
Ix: IndexType,
G: Into<Option<Graph<(), (), Ty, Ix>>>,
>(
rng: &mut R,
n: usize,
m: usize,
initial_graph: G,
) -> Graph<(), (), Ty, Ix> {
assert!(m >= 1, "Parameter m must be greater than 0");
assert!(m < n, "Parameter m must be less than n");
let mut graph = initial_graph.into().unwrap_or_else(|| star_graph(m));
let initial_node_count = graph.node_count();
assert!(
graph.edge_count() > 0,
"Initial graph must have at least one edge"
);
assert!(
initial_node_count >= m,
"Initial graph must have at least m nodes"
);
assert!(
initial_node_count <= n,
"Initial graph must have at most n nodes"
);
graph.reserve_edges((n - initial_node_count) * m);
graph.reserve_nodes(n - initial_node_count);
let mut repeated_nodes = Vec::with_capacity((n - m) * m * 2);
for node in graph.node_indices() {
for edge in graph.edges(node) {
repeated_nodes.push(edge.source());
repeated_nodes.push(edge.target());
}
}
let mut picked = vec![false; n];
let mut targets = vec![NodeIndex::new(0); m];
for _ in initial_node_count..n {
let node = graph.add_node(());
let uniform_distribution = Uniform::new(0, repeated_nodes.len());
let mut i = 0;
while i < m {
let random_index = uniform_distribution.sample(rng);
let target = repeated_nodes[random_index];
if !picked[target.index()] {
picked[target.index()] = true;
targets[i] = target;
i += 1;
}
}
for target in &targets {
graph.add_edge(node, *target, ());
repeated_nodes.push(node);
repeated_nodes.push(*target);
picked[target.index()] = false;
}
}
graph
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{complete_graph, empty_graph};
use petgraph::graph::DiGraph;
use rand::prelude::SmallRng;
use rand::SeedableRng;
#[test]
fn test_directed_barabasi_albert_graph_nodes_have_at_most_m_outgoing_edges() {
let mut rng = SmallRng::from_entropy();
let graph: DiGraph<(), ()> = barabasi_albert_graph(&mut rng, 100, 3, None);
graph.node_indices().for_each(|node| {
let outgoing_edges = graph.edges_directed(node, petgraph::Outgoing).count();
assert!(outgoing_edges <= 3);
});
}
#[test]
fn test_directed_barabasi_albert_graph_with_initial_graph() {
let mut rng = SmallRng::from_entropy();
let graph: DiGraph<(), ()> = barabasi_albert_graph(&mut rng, 100, 3, complete_graph(4));
assert_eq!(graph.node_count(), 100);
assert_eq!(graph.edge_count(), 300);
graph.node_indices().for_each(|node| {
let outgoing_edges = graph.edges_directed(node, petgraph::Outgoing).count();
assert_eq!(outgoing_edges, 3);
});
}
#[test]
fn test_barabasi_albert_graph_with_maximum_sized_initial_graph() {
let mut rng = SmallRng::from_entropy();
let star_graph: Graph<(), ()> = barabasi_albert_graph(&mut rng, 5, 4, star_graph(4));
assert_eq!(star_graph.node_count(), 5);
assert_eq!(star_graph.edge_count(), 4);
assert_eq!(star_graph.edges(NodeIndex::new(0)).count(), 4);
}
#[test]
#[should_panic(expected = "m must be greater than 0")]
fn test_barabasi_albert_graph_panics_if_m_equals_0() {
let mut rng = SmallRng::from_entropy();
let _: Graph<(), ()> = barabasi_albert_graph(&mut rng, 5, 0, None);
}
#[test]
#[should_panic(expected = "m must be less than n")]
fn test_barabasi_albert_graph_panics_if_m_is_greater_than_or_equal_to_n() {
let mut rng = SmallRng::from_entropy();
let _: Graph<(), ()> = barabasi_albert_graph(&mut rng, 5, 5, None);
}
#[test]
#[should_panic(expected = "must have at least one edge")]
fn test_barabasi_albert_graph_panics_if_initial_graph_has_no_edges() {
let mut rng = SmallRng::from_entropy();
let _: Graph<(), ()> = barabasi_albert_graph(&mut rng, 5, 3, empty_graph(3));
}
#[test]
#[should_panic(expected = "must have at least m nodes")]
fn test_barabasi_albert_graph_panics_if_initial_graph_has_less_than_m_nodes() {
let mut rng = SmallRng::from_entropy();
let _: Graph<(), ()> = barabasi_albert_graph(&mut rng, 5, 3, complete_graph(2));
}
#[test]
#[should_panic(expected = "must have at most n nodes")]
fn test_barabasi_albert_graph_panics_if_initial_graph_has_more_than_n_nodes() {
let mut rng = SmallRng::from_entropy();
let _: Graph<(), ()> = barabasi_albert_graph(&mut rng, 5, 3, complete_graph(6));
}
}