use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::random::{Random, rng};
use sklears_core::error::{Result, SklearsError};
pub fn make_erdos_renyi_graph(
n_nodes: usize,
edge_probability: f64,
directed: bool,
random_state: Option<u64>,
) -> Result<Array2<f64>> {
if n_nodes == 0 {
return Err(SklearsError::InvalidInput(
"n_nodes must be positive".to_string(),
));
}
if !(0.0..=1.0).contains(&edge_probability) {
return Err(SklearsError::InvalidInput(
"edge_probability must be between 0.0 and 1.0".to_string(),
));
}
let mut rng = Random::from_seed(random_state.unwrap_or_else(|| rng().gen()));
let mut adjacency = Array2::zeros((n_nodes, n_nodes));
for i in 0..n_nodes {
let start_j = if directed { 0 } else { i + 1 };
for j in start_j..n_nodes {
if i != j && rng.gen() < edge_probability {
adjacency[[i, j]] = 1.0;
if !directed {
adjacency[[j, i]] = 1.0;
}
}
}
}
Ok(adjacency)
}
pub fn make_barabasi_albert_graph(
n_nodes: usize,
m_edges: usize,
random_state: Option<u64>,
) -> Result<Array2<f64>> {
if n_nodes == 0 {
return Err(SklearsError::InvalidInput(
"n_nodes must be positive".to_string(),
));
}
if m_edges == 0 || m_edges >= n_nodes {
return Err(SklearsError::InvalidInput(
"m_edges must be positive and less than n_nodes".to_string(),
));
}
let mut rng = Random::from_seed(random_state.unwrap_or_else(|| rng().gen()));
let mut adjacency = Array2::zeros((n_nodes, n_nodes));
let mut degrees = vec![0; n_nodes];
let initial_nodes = m_edges + 1;
for i in 0..initial_nodes {
for j in (i + 1)..initial_nodes {
adjacency[[i, j]] = 1.0;
adjacency[[j, i]] = 1.0;
degrees[i] += 1;
degrees[j] += 1;
}
}
for new_node in initial_nodes..n_nodes {
let total_degree: usize = degrees.iter().sum();
let mut targets = Vec::new();
while targets.len() < m_edges {
let mut cumulative_prob = 0.0;
let rand_val = rng.gen();
for (node, °ree) in degrees.iter().enumerate().take(new_node) {
if targets.contains(&node) {
continue;
}
cumulative_prob += degree as f64 / total_degree as f64;
if rand_val <= cumulative_prob {
targets.push(node);
break;
}
}
}
for &target in &targets {
adjacency[[new_node, target]] = 1.0;
adjacency[[target, new_node]] = 1.0;
degrees[new_node] += 1;
degrees[target] += 1;
}
}
Ok(adjacency)
}
pub fn make_watts_strogatz_graph(
n_nodes: usize,
k_neighbors: usize,
p_rewire: f64,
random_state: Option<u64>,
) -> Result<Array2<f64>> {
if n_nodes == 0 {
return Err(SklearsError::InvalidInput(
"n_nodes must be positive".to_string(),
));
}
if k_neighbors >= n_nodes || k_neighbors % 2 != 0 {
return Err(SklearsError::InvalidInput(
"k_neighbors must be even and less than n_nodes".to_string(),
));
}
if !(0.0..=1.0).contains(&p_rewire) {
return Err(SklearsError::InvalidInput(
"p_rewire must be between 0.0 and 1.0".to_string(),
));
}
let mut rng = Random::from_seed(random_state.unwrap_or_else(|| rng().gen()));
let mut adjacency = Array2::zeros((n_nodes, n_nodes));
for i in 0..n_nodes {
for j in 1..=(k_neighbors / 2) {
let neighbor = (i + j) % n_nodes;
adjacency[[i, neighbor]] = 1.0;
adjacency[[neighbor, i]] = 1.0;
}
}
for i in 0..n_nodes {
for j in 1..=(k_neighbors / 2) {
if rng.gen() < p_rewire {
let old_neighbor = (i + j) % n_nodes;
adjacency[[i, old_neighbor]] = 0.0;
adjacency[[old_neighbor, i]] = 0.0;
loop {
let new_neighbor = rng.gen_range(0..n_nodes);
if new_neighbor != i && adjacency[[i, new_neighbor]] == 0.0 {
adjacency[[i, new_neighbor]] = 1.0;
adjacency[[new_neighbor, i]] = 1.0;
break;
}
}
}
}
}
Ok(adjacency)
}
pub fn make_stochastic_block_graph(
community_sizes: &[usize],
p_within: f64,
p_between: f64,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<i32>)> {
if community_sizes.is_empty() {
return Err(SklearsError::InvalidInput(
"community_sizes cannot be empty".to_string(),
));
}
if community_sizes.iter().any(|&size| size == 0) {
return Err(SklearsError::InvalidInput(
"All community sizes must be positive".to_string(),
));
}
if !(0.0..=1.0).contains(&p_within) || !(0.0..=1.0).contains(&p_between) {
return Err(SklearsError::InvalidInput(
"Probabilities must be between 0.0 and 1.0".to_string(),
));
}
let mut rng = Random::from_seed(random_state.unwrap_or_else(|| rng().gen()));
let n_nodes: usize = community_sizes.iter().sum();
let mut adjacency = Array2::zeros((n_nodes, n_nodes));
let mut labels = Array1::zeros(n_nodes);
let mut node_idx = 0;
for (community_id, &size) in community_sizes.iter().enumerate() {
for _ in 0..size {
labels[node_idx] = community_id as i32;
node_idx += 1;
}
}
for i in 0..n_nodes {
for j in (i + 1)..n_nodes {
let same_community = labels[i] == labels[j];
let edge_prob = if same_community { p_within } else { p_between };
if rng.gen() < edge_prob {
adjacency[[i, j]] = 1.0;
adjacency[[j, i]] = 1.0;
}
}
}
Ok((adjacency, labels))
}
pub fn make_random_tree(n_nodes: usize, random_state: Option<u64>) -> Result<Array2<f64>> {
if n_nodes == 0 {
return Err(SklearsError::InvalidInput(
"n_nodes must be positive".to_string(),
));
}
if n_nodes == 1 {
return Ok(Array2::zeros((1, 1)));
}
let mut rng = Random::from_seed(random_state.unwrap_or_else(|| rng().gen()));
let mut adjacency = Array2::zeros((n_nodes, n_nodes));
for i in 1..n_nodes {
let parent = rng.gen_range(0..i);
adjacency[[i, parent]] = 1.0;
adjacency[[parent, i]] = 1.0;
}
Ok(adjacency)
}