petgraph/algo/
page_rank.rs1use crate::visit::{EdgeRef, IntoEdges, NodeCount, NodeIndexable};
2
3#[cfg(feature = "rayon")]
4use rayon::prelude::*;
5
6use super::UnitMeasure;
7pub fn page_rank<G, D>(graph: G, damping_factor: D, nb_iter: usize) -> Vec<D>
54where
55    G: NodeCount + IntoEdges + NodeIndexable,
56    D: UnitMeasure + Copy,
57{
58    let node_count = graph.node_count();
59    if node_count == 0 {
60        return vec![];
61    }
62    assert!(
63        D::zero() <= damping_factor && damping_factor <= D::one(),
64        "Damping factor should be between 0 et 1."
65    );
66    let nb = D::from_usize(node_count);
67    let mut ranks = vec![D::one() / nb; node_count];
68    let nodeix = |i| graph.from_index(i);
69    let out_degrees: Vec<D> = (0..node_count)
70        .map(|i| graph.edges(nodeix(i)).map(|_| D::one()).sum::<D>())
71        .collect();
72
73    for _ in 0..nb_iter {
74        let pi = (0..node_count)
75            .enumerate()
76            .map(|(v, _)| {
77                ranks
78                    .iter()
79                    .enumerate()
80                    .map(|(w, r)| {
81                        let mut w_out_edges = graph.edges(nodeix(w));
82                        if w_out_edges.any(|e| e.target() == nodeix(v)) {
83                            damping_factor * *r / out_degrees[w]
84                        } else if out_degrees[w] == D::zero() {
85                            damping_factor * *r / nb } else {
87                            (D::one() - damping_factor) * *r / nb }
89                    })
90                    .sum::<D>()
91            })
92            .collect::<Vec<D>>();
93        let sum = pi.iter().copied().sum::<D>();
94        ranks = pi.iter().map(|r| *r / sum).collect::<Vec<D>>();
95    }
96    ranks
97}
98
99#[allow(dead_code)]
100fn out_edges_info<G, D>(graph: G, index_w: usize, index_v: usize) -> (D, bool)
101where
102    G: NodeCount + IntoEdges + NodeIndexable + std::marker::Sync,
103    D: UnitMeasure + Copy + std::marker::Send + std::marker::Sync,
104{
105    let node_w = graph.from_index(index_w);
106    let node_v = graph.from_index(index_v);
107    let mut out_edges = graph.edges(node_w);
108    let mut out_edge = out_edges.next();
109    let mut out_degree = D::zero();
110    let mut flag_points_to = false;
111    while let Some(edge) = out_edge {
112        out_degree = out_degree + D::one();
113        if edge.target() == node_v {
114            flag_points_to = true;
115        }
116        out_edge = out_edges.next();
117    }
118    (out_degree, flag_points_to)
119}
120#[cfg(feature = "rayon")]
124pub fn parallel_page_rank<G, D>(
125    graph: G,
126    damping_factor: D,
127    nb_iter: usize,
128    tol: Option<D>,
129) -> Vec<D>
130where
131    G: NodeCount + IntoEdges + NodeIndexable + std::marker::Sync,
132    D: UnitMeasure + Copy + std::marker::Send + std::marker::Sync,
133{
134    let node_count = graph.node_count();
135    if node_count == 0 {
136        return vec![];
137    }
138    assert!(
139        D::zero() <= damping_factor && damping_factor <= D::one(),
140        "Damping factor should be between 0 et 1."
141    );
142    let mut tolerance = D::default_tol();
143    if let Some(_tol) = tol {
144        tolerance = _tol;
145    }
146    let nb = D::from_usize(node_count);
147    let mut ranks: Vec<D> = (0..node_count)
148        .into_par_iter()
149        .map(|i| D::one() / nb)
150        .collect();
151    for _ in 0..nb_iter {
152        let pi = (0..node_count)
153            .into_par_iter()
154            .map(|v| {
155                ranks
156                    .iter()
157                    .enumerate()
158                    .map(|(w, r)| {
159                        let (out_deg, w_points_to_v) = out_edges_info(graph, w, v);
160                        if w_points_to_v {
161                            damping_factor * *r / out_deg
162                        } else if out_deg == D::zero() {
163                            damping_factor * *r / nb } else {
165                            (D::one() - damping_factor) * *r / nb }
167                    })
168                    .sum::<D>()
169            })
170            .collect::<Vec<D>>();
171        let sum = pi.par_iter().map(|score| *score).sum::<D>();
172        let new_ranks = pi.par_iter().map(|r| *r / sum).collect::<Vec<D>>();
173        let squared_norm_2 = new_ranks
174            .par_iter()
175            .zip(&ranks)
176            .map(|(new, old)| (*new - *old) * (*new - *old))
177            .sum::<D>();
178        if squared_norm_2 <= tolerance {
179            return ranks;
180        } else {
181            ranks = new_ranks;
182        }
183    }
184    ranks
185}