use crate::PageRankConfiguration;
use crate::graph::Graph;
use crate::graph::ranked_nodes::RankedNodes;
use crate::node::NodeId;
use core::hash::Hash;
impl<T: Eq + Hash + Clone> Graph<T> {
pub fn populate_for_textrank(&mut self, tokens: &[T], window_size: usize) {
let edges = tokens.iter().enumerate().flat_map(|(i, from)| {
tokens[i + 1..]
.iter()
.take(window_size)
.filter(move |to| &from != to)
.map(move |to| (from, to))
});
self.modify(|mut graph| {
for (from, to) in edges {
let from_id = graph.fetch_id_for_data(from);
let to_id = graph.fetch_id_for_data(to);
graph.add_edge(from_id, to_id);
graph.add_edge(to_id, from_id);
}
});
}
pub fn run_pagerank(&self, config: &PageRankConfiguration) -> RankedNodes<'_, T> {
let mut scores = vec![1.0_f32; self.nodes_length];
loop {
let prev_scores = scores.to_owned();
let max_diff;
(scores, max_diff) = self.iterate_pagerank_once(&prev_scores, config.damping);
if max_diff < config.tolerance {
break;
}
}
RankedNodes {
graph: self,
scores,
}
}
fn iterate_pagerank_once(&self, prev_scores: &[f32], damping: f32) -> (Vec<f32>, f32) {
let mut new_scores = Vec::with_capacity(prev_scores.len());
let mut max_diff = 0.0_f32;
for (i, incoming_edges) in self.graph.iter().enumerate() {
let new_score = self.pagerank_score_node(incoming_edges, prev_scores, damping);
let diff = (prev_scores[i] - new_score).abs();
if diff > max_diff {
max_diff = diff;
}
new_scores.push(new_score);
}
(new_scores, max_diff)
}
fn pagerank_score_node(
&self,
incoming_edges: &[(NodeId, f32)],
prev_scores: &[f32],
damping: f32,
) -> f32 {
let new_score = incoming_edges
.iter()
.map(|(from, weight)| {
let from_outgoing_sum = self.outgoing_weight_sums[from.0];
(prev_scores[from.0] / from_outgoing_sum) * weight
})
.sum::<f32>();
(1.0 - damping) + damping * new_score
}
}