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(|_| 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}