use super::common::{GraphView, NodeId};
use std::collections::HashMap;
pub struct PageRankConfig {
pub damping_factor: f64,
pub iterations: usize,
pub tolerance: f64,
pub dangling_redistribution: bool,
}
impl Default for PageRankConfig {
fn default() -> Self {
Self {
damping_factor: 0.85,
iterations: 20,
tolerance: 0.0001,
dangling_redistribution: true,
}
}
}
pub fn page_rank(
view: &GraphView,
config: PageRankConfig,
) -> HashMap<NodeId, f64> {
let n = view.node_count;
if n == 0 {
return HashMap::new();
}
let initial_score = 1.0 / n as f64;
let mut scores = vec![initial_score; n];
let mut next_scores = vec![0.0; n];
let d = config.damping_factor;
let base_score = (1.0 - d) / n as f64;
for _ in 0..config.iterations {
let mut total_diff = 0.0;
let dangling_contrib = if config.dangling_redistribution {
let dangling_sum: f64 = (0..n)
.filter(|&i| view.out_degree(i) == 0)
.map(|i| scores[i])
.sum();
dangling_sum / n as f64
} else {
0.0
};
for i in 0..n {
let mut sum_incoming = 0.0;
for &source_idx in view.predecessors(i) {
let out_degree = view.out_degree(source_idx);
if out_degree > 0 {
sum_incoming += scores[source_idx] / out_degree as f64;
}
}
next_scores[i] = base_score + d * (sum_incoming + dangling_contrib);
total_diff += (next_scores[i] - scores[i]).abs();
}
scores.copy_from_slice(&next_scores);
if total_diff < config.tolerance {
break;
}
}
let mut result = HashMap::with_capacity(n);
for (idx, score) in scores.into_iter().enumerate() {
result.insert(view.index_to_node[idx], score);
}
result
}