use super::types::{CommunityResult, CommunityStructure};
use crate::base::{EdgeWeight, Graph, IndexType, Node};
use std::collections::HashMap;
use std::hash::Hash;
#[deprecated(note = "Use `fluid_communities_result` instead")]
#[allow(dead_code)]
pub fn fluid_communities<N, E, Ix>(
graph: &Graph<N, E, Ix>,
num_communities: usize,
max_iterations: usize,
) -> CommunityStructure<N>
where
N: Node + Clone + Hash + Eq + std::fmt::Debug,
E: EdgeWeight + Into<f64> + Copy,
Ix: IndexType,
{
let nodes: Vec<N> = graph.nodes().into_iter().cloned().collect();
let n = nodes.len();
if n == 0 || num_communities == 0 {
return CommunityStructure {
node_communities: HashMap::new(),
modularity: 0.0,
};
}
let num_communities = num_communities.min(n);
let mut rng = scirs2_core::random::rng();
let mut node_fluids: HashMap<N, Vec<f64>> = HashMap::new();
for node in &nodes {
let mut fluids = vec![0.0; num_communities];
use scirs2_core::random::{Rng, RngExt};
let initial_fluid = rng.random_range(0..num_communities);
fluids[initial_fluid] = 1.0;
node_fluids.insert(node.clone(), fluids);
}
for _iteration in 0..max_iterations {
let mut new_fluids: HashMap<N, Vec<f64>> = HashMap::new();
for node in &nodes {
let mut fluid_sums = vec![0.0; num_communities];
if let Ok(neighbors) = graph.neighbors(node) {
let neighbor_count = neighbors.len();
if neighbor_count > 0 {
for neighbor in neighbors {
if let Some(neighbor_fluids) = node_fluids.get(&neighbor) {
for (i, &fluid_amount) in neighbor_fluids.iter().enumerate() {
fluid_sums[i] += fluid_amount;
}
}
}
for fluid_sum in fluid_sums.iter_mut() {
*fluid_sum /= neighbor_count as f64;
}
} else {
if let Some(current_fluids) = node_fluids.get(node) {
fluid_sums = current_fluids.clone();
}
}
} else {
if let Some(current_fluids) = node_fluids.get(node) {
fluid_sums = current_fluids.clone();
}
}
let total: f64 = fluid_sums.iter().sum();
if total > 0.0 {
for fluid in fluid_sums.iter_mut() {
*fluid /= total;
}
} else {
use scirs2_core::random::{Rng, RngExt};
let random_fluid = rng.random_range(0..num_communities);
fluid_sums[random_fluid] = 1.0;
}
new_fluids.insert(node.clone(), fluid_sums);
}
node_fluids = new_fluids;
}
let mut communities: HashMap<N, usize> = HashMap::new();
for (node, fluids) in &node_fluids {
let max_fluid_idx = fluids
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| i)
.unwrap_or(0);
communities.insert(node.clone(), max_fluid_idx);
}
let mut community_map: HashMap<usize, usize> = HashMap::new();
let mut next_id = 0;
for &comm in communities.values() {
if let std::collections::hash_map::Entry::Vacant(e) = community_map.entry(comm) {
e.insert(next_id);
next_id += 1;
}
}
for (_, comm) in communities.iter_mut() {
*comm = community_map[comm];
}
let mod_score = super::modularity(graph, &communities);
CommunityStructure {
node_communities: communities,
modularity: mod_score,
}
}
#[allow(dead_code)]
pub fn fluid_communities_result<N, E, Ix>(
graph: &Graph<N, E, Ix>,
num_communities: usize,
max_iterations: usize,
) -> CommunityResult<N>
where
N: Node + Clone + Hash + Eq + std::fmt::Debug,
E: EdgeWeight + Into<f64> + Copy,
Ix: IndexType,
{
#[allow(deprecated)]
let structure = fluid_communities(graph, num_communities, max_iterations);
let mut result = CommunityResult::from_node_map(structure.node_communities.clone());
let mod_score = super::modularity(graph, &structure.node_communities);
result.quality_score = Some(mod_score);
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result as GraphResult;
use crate::generators::create_graph;
#[test]
fn test_fluid_communities() -> GraphResult<()> {
let mut graph = create_graph::<i32, f64>();
graph.add_edge(0, 1, 1.0)?;
graph.add_edge(1, 2, 1.0)?;
graph.add_edge(2, 0, 1.0)?;
graph.add_edge(3, 4, 1.0)?;
graph.add_edge(4, 5, 1.0)?;
graph.add_edge(5, 3, 1.0)?;
graph.add_edge(2, 3, 0.1)?;
let result = fluid_communities_result(&graph, 2, 30);
assert_eq!(result.node_communities.len(), 6);
assert!(result.num_communities <= 2);
assert!(result.num_communities > 0);
assert!(result.quality_score.is_some());
Ok(())
}
#[test]
fn test_fluid_communities_empty_graph() {
let graph = create_graph::<i32, f64>();
let result = fluid_communities_result(&graph, 2, 10);
assert!(result.node_communities.is_empty());
assert_eq!(result.quality_score.unwrap_or(0.0), 0.0);
}
#[test]
fn test_fluid_communities_single_node() -> GraphResult<()> {
let mut graph = create_graph::<&str, f64>();
graph.add_node("A");
let result = fluid_communities_result(&graph, 1, 10);
assert_eq!(result.node_communities.len(), 1);
assert!(result.node_communities.contains_key(&"A"));
assert_eq!(result.node_communities[&"A"], 0);
Ok(())
}
}