use super::modularity::modularity;
use super::types::{CommunityResult, CommunityStructure};
use crate::base::{EdgeWeight, Graph, IndexType, Node};
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
#[deprecated(note = "Use `hierarchical_communities_result` instead")]
#[allow(dead_code)]
pub fn hierarchical_communities<N, E, Ix>(
graph: &Graph<N, E, Ix>,
linkage: &str,
) -> Vec<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 {
return vec![];
}
let mut results = Vec::new();
let mut current_communities: HashMap<N, usize> = nodes
.iter()
.enumerate()
.map(|(i, node)| (node.clone(), i))
.collect();
let initial_mod = modularity(graph, ¤t_communities);
results.push(CommunityStructure {
node_communities: current_communities.clone(),
modularity: initial_mod,
});
let mut active_communities: HashSet<usize> = (0..n).collect();
while active_communities.len() > 1 {
let mut best_merge: Option<(usize, usize)> = None;
let mut best_modularity = f64::NEG_INFINITY;
let communities_vec: Vec<usize> = active_communities.iter().cloned().collect();
for i in 0..communities_vec.len() {
for j in (i + 1)..communities_vec.len() {
let comm1 = communities_vec[i];
let comm2 = communities_vec[j];
if are_communities_connected(graph, ¤t_communities, comm1, comm2) {
let mut test_communities = current_communities.clone();
for (_, community) in test_communities.iter_mut() {
if *community == comm2 {
*community = comm1;
}
}
let test_modularity = modularity(graph, &test_communities);
let score = match linkage {
"single" => {
calculate_single_linkage(graph, ¤t_communities, comm1, comm2)
}
"complete" => {
calculate_complete_linkage(graph, ¤t_communities, comm1, comm2)
}
"average" => {
calculate_average_linkage(graph, ¤t_communities, comm1, comm2)
}
_ => test_modularity, };
if score > best_modularity {
best_modularity = score;
best_merge = Some((comm1, comm2));
}
}
}
}
if let Some((comm1, comm2)) = best_merge {
for (_, community) in current_communities.iter_mut() {
if *community == comm2 {
*community = comm1;
}
}
active_communities.remove(&comm2);
let current_mod = modularity(graph, ¤t_communities);
results.push(CommunityStructure {
node_communities: current_communities.clone(),
modularity: current_mod,
});
} else {
break;
}
}
for result in &mut results {
let mut community_map: HashMap<usize, usize> = HashMap::new();
let mut next_id = 0;
for &comm in result.node_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 result.node_communities.iter_mut() {
*comm = community_map[comm];
}
}
results
}
#[allow(dead_code)]
pub fn hierarchical_communities_result<N, E, Ix>(
graph: &Graph<N, E, Ix>,
linkage: &str,
) -> Vec<CommunityResult<N>>
where
N: Node + Clone + Hash + Eq + std::fmt::Debug,
E: EdgeWeight + Into<f64> + Copy,
Ix: IndexType,
{
#[allow(deprecated)]
let structures = hierarchical_communities(graph, linkage);
structures
.into_iter()
.map(CommunityResult::from_community_structure)
.collect()
}
#[allow(dead_code)]
fn are_communities_connected<N, E, Ix>(
graph: &Graph<N, E, Ix>,
communities: &HashMap<N, usize>,
comm1: usize,
comm2: usize,
) -> bool
where
N: Node + std::fmt::Debug,
E: EdgeWeight,
Ix: IndexType,
{
for (node, &node_comm) in communities {
if node_comm == comm1 {
if let Ok(neighbors) = graph.neighbors(node) {
for neighbor in neighbors {
if let Some(&neighbor_comm) = communities.get(&neighbor) {
if neighbor_comm == comm2 {
return true;
}
}
}
}
}
}
false
}
#[allow(dead_code)]
fn calculate_single_linkage<N, E, Ix>(
graph: &Graph<N, E, Ix>,
communities: &HashMap<N, usize>,
comm1: usize,
comm2: usize,
) -> f64
where
N: Node + std::fmt::Debug,
E: EdgeWeight + Into<f64> + Copy,
Ix: IndexType,
{
let mut min_distance = f64::INFINITY;
for (node1, &node1_comm) in communities {
if node1_comm == comm1 {
for (node2, &node2_comm) in communities {
if node2_comm == comm2 {
if let Ok(weight) = graph.edge_weight(node1, node2) {
let distance = 1.0 / (1.0 + weight.into()); min_distance = min_distance.min(distance);
}
}
}
}
}
if min_distance == f64::INFINITY {
0.0 } else {
1.0 / min_distance }
}
#[allow(dead_code)]
fn calculate_complete_linkage<N, E, Ix>(
graph: &Graph<N, E, Ix>,
communities: &HashMap<N, usize>,
comm1: usize,
comm2: usize,
) -> f64
where
N: Node + std::fmt::Debug,
E: EdgeWeight + Into<f64> + Copy,
Ix: IndexType,
{
let mut max_distance: f64 = 0.0;
let mut found_connection = false;
for (node1, &node1_comm) in communities {
if node1_comm == comm1 {
for (node2, &node2_comm) in communities {
if node2_comm == comm2 {
if let Ok(weight) = graph.edge_weight(node1, node2) {
let distance = 1.0 / (1.0 + weight.into());
max_distance = max_distance.max(distance);
found_connection = true;
}
}
}
}
}
if found_connection {
1.0 / max_distance
} else {
0.0
}
}
#[allow(dead_code)]
fn calculate_average_linkage<N, E, Ix>(
graph: &Graph<N, E, Ix>,
communities: &HashMap<N, usize>,
comm1: usize,
comm2: usize,
) -> f64
where
N: Node + std::fmt::Debug,
E: EdgeWeight + Into<f64> + Copy,
Ix: IndexType,
{
let mut total_distance = 0.0;
let mut count = 0;
for (node1, &node1_comm) in communities {
if node1_comm == comm1 {
for (node2, &node2_comm) in communities {
if node2_comm == comm2 {
if let Ok(weight) = graph.edge_weight(node1, node2) {
let distance = 1.0 / (1.0 + weight.into());
total_distance += distance;
count += 1;
}
}
}
}
}
if count > 0 {
1.0 / (total_distance / count as f64)
} else {
0.0
}
}