erpc_analysis/algorithms/
centrality.rs
use log::info;
use std::sync::Arc;
use crate::db_trait::{AnalysisDatabase, AnalysisError};
use crate::models::metrics::CentralityAnalysisResult;
pub struct CentralityAnalyzer {
db_client: Arc<dyn AnalysisDatabase>,
}
impl CentralityAnalyzer {
pub fn new(db_client: Arc<dyn AnalysisDatabase>) -> Self {
Self { db_client }
}
pub async fn analyze_betweenness_centrality(
&self,
projection_name: &str,
sampling_size: Option<usize>,
sampling_seed: Option<u64>,
) -> Result<CentralityAnalysisResult, AnalysisError> {
info!("=== Starting Betweenness Centrality Analysis ===");
if let Some(sample_size) = sampling_size {
info!("Using sampling size: {} nodes", sample_size);
}
let result = self
.db_client
.calculate_betweenness_centrality(
projection_name,
sampling_size,
sampling_seed,
)
.await?;
info!("=== Betweenness Centrality Analysis Complete ===");
Ok(result)
}
pub async fn analyze_closeness_centrality(
&self,
projection_name: &str,
use_wasserman_faust: Option<bool>,
) -> Result<CentralityAnalysisResult, AnalysisError> {
info!("=== Starting Closeness Centrality Analysis ===");
if let Some(use_wf) = use_wasserman_faust {
info!("Using Wasserman-Faust formula: {}", use_wf);
}
let result = self
.db_client
.calculate_closeness_centrality(
projection_name,
use_wasserman_faust,
)
.await?;
info!("=== Closeness Centrality Analysis Complete ===");
Ok(result)
}
pub async fn analyze_combined_centrality(
&self,
projection_name: &str,
betweenness_sampling_size: Option<usize>,
betweenness_sampling_seed: Option<u64>,
use_wasserman_faust: Option<bool>,
) -> Result<CentralityAnalysisResult, AnalysisError> {
info!("=== Starting Combined Centrality Analysis ===");
let result = self
.db_client
.calculate_combined_centrality(
projection_name,
betweenness_sampling_size,
betweenness_sampling_seed,
use_wasserman_faust,
)
.await?;
info!("=== Combined Centrality Analysis Complete ===");
Ok(result)
}
pub fn display_centrality_results(
&self,
result: &CentralityAnalysisResult,
centrality_type: &str,
config: &crate::config::AnalysisSettings,
) {
info!("=== {} Centrality Analysis Results ===", centrality_type);
info!(
"Total nodes analyzed: {}",
result.total_nodes_analyzed.unwrap_or(0)
);
if let Some(betweenness_dist) = &result.betweenness_distribution {
info!("Betweenness Centrality Distribution:");
info!(" Min: {:.6}", betweenness_dist.min);
info!(" Max: {:.6}", betweenness_dist.max);
info!(" Mean: {:.6}", betweenness_dist.mean);
info!(" Median (p50): {:.6}", betweenness_dist.p50);
info!(" p75: {:.6}", betweenness_dist.p75);
info!(" p90: {:.6}", betweenness_dist.p90);
info!(" p95: {:.6}", betweenness_dist.p95);
info!(" p99: {:.6}", betweenness_dist.p99);
info!(" p999: {:.6}", betweenness_dist.p999);
}
if let Some(closeness_dist) = &result.closeness_distribution {
info!("Closeness Centrality Distribution:");
info!(" Min: {:.6}", closeness_dist.min);
info!(" Max: {:.6}", closeness_dist.max);
info!(" Mean: {:.6}", closeness_dist.mean);
info!(" Median (p50): {:.6}", closeness_dist.p50);
info!(" p75: {:.6}", closeness_dist.p75);
info!(" p90: {:.6}", closeness_dist.p90);
info!(" p95: {:.6}", closeness_dist.p95);
info!(" p99: {:.6}", closeness_dist.p99);
info!(" p999: {:.6}", closeness_dist.p999);
}
let display_count = config.max_display_components;
info!("=== Top {} Nodes by Centrality ===", display_count);
self.display_top_nodes(&result.centrality_metrics, display_count);
}
fn display_top_nodes(
&self,
metrics: &[crate::models::metrics::CentralityMetrics],
top_count: usize,
) {
if metrics.iter().any(|m| m.betweenness_centrality.is_some()) {
let mut sorted_by_betweenness = metrics.to_vec();
sorted_by_betweenness.sort_by(|a, b| {
b.betweenness_centrality
.unwrap_or(0.0)
.partial_cmp(&a.betweenness_centrality.unwrap_or(0.0))
.unwrap_or(std::cmp::Ordering::Equal)
});
info!("Top {} nodes by Betweenness Centrality:", top_count);
for (i, metric) in
sorted_by_betweenness.iter().take(top_count).enumerate()
{
let betweenness = metric.betweenness_centrality.unwrap_or(0.0);
info!(
" {}. {} - {:.6}",
i + 1,
metric.fingerprint,
betweenness
);
}
}
if metrics.iter().any(|m| m.closeness_centrality.is_some()) {
let mut sorted_by_closeness = metrics.to_vec();
sorted_by_closeness.sort_by(|a, b| {
b.closeness_centrality
.unwrap_or(0.0)
.partial_cmp(&a.closeness_centrality.unwrap_or(0.0))
.unwrap_or(std::cmp::Ordering::Equal)
});
info!("Top {} nodes by Closeness Centrality:", top_count);
for (i, metric) in
sorted_by_closeness.iter().take(top_count).enumerate()
{
let closeness = metric.closeness_centrality.unwrap_or(0.0);
info!(
" {}. {} - {:.6}",
i + 1,
metric.fingerprint,
closeness
);
}
}
}
}