#![cfg_attr(coverage_nightly, coverage(off))]
use super::aprender_adapter::to_aprender_graph;
use super::*;
use aprender::graph::GraphCentrality;
pub struct PageRankComputer {
pub damping: f64,
pub tolerance: f64,
pub max_iterations: usize,
}
impl Default for PageRankComputer {
fn default() -> Self {
PageRankComputer {
damping: 0.85,
tolerance: 1e-6,
max_iterations: 100,
}
}
}
impl PageRankComputer {
pub fn new() -> Self {
Self::default()
}
pub fn with_damping(mut self, damping: f64) -> Self {
self.damping = damping;
self
}
pub fn compute(&self, graph: &DependencyGraph) -> Vec<f64> {
if graph.node_count() == 0 {
return Vec::new();
}
let aprender_graph = to_aprender_graph(graph, true);
aprender_graph
.pagerank(self.damping, self.max_iterations, self.tolerance)
.unwrap_or_else(|_| vec![0.0; aprender_graph.num_nodes()])
}
#[deprecated(since = "2.201.0", note = "Use compute(&DependencyGraph) instead")]
pub fn compute_legacy(&self, matrices: &GraphMatrices) -> Vec<f64> {
let mut graph = DependencyGraph::new();
for _ in 0..matrices.node_count {
graph.add_node(NodeData {
path: std::path::PathBuf::from("legacy"),
module: "legacy".to_string(),
symbols: vec![],
loc: 0,
complexity: 0.0,
ast_hash: 0,
});
}
for (from, to, weight) in &matrices.edges {
let from_idx = trueno_graph::NodeId(*from as u32);
let to_idx = trueno_graph::NodeId(*to as u32);
graph.add_edge(
from_idx,
to_idx,
EdgeData::Import {
weight: *weight,
visibility: Visibility::Public,
},
);
}
self.compute(&graph)
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pagerank_computer_default() {
let computer = PageRankComputer::default();
assert_eq!(computer.damping, 0.85);
assert_eq!(computer.tolerance, 1e-6);
assert_eq!(computer.max_iterations, 100);
}
#[test]
fn test_pagerank_computer_new() {
let computer = PageRankComputer::new();
assert_eq!(computer.damping, 0.85);
}
#[test]
fn test_pagerank_computer_with_damping() {
let computer = PageRankComputer::new().with_damping(0.9);
assert_eq!(computer.damping, 0.9);
}
#[test]
fn test_compute_empty_graph() {
let computer = PageRankComputer::new();
let graph = DependencyGraph::new();
let scores = computer.compute(&graph);
assert!(scores.is_empty());
}
#[test]
fn test_compute_single_node() {
let computer = PageRankComputer::new();
let mut graph = DependencyGraph::new();
graph.add_node(NodeData {
path: std::path::PathBuf::from("test.rs"),
module: "test".to_string(),
symbols: vec![],
loc: 100,
complexity: 5.0,
ast_hash: 12345,
});
let scores = computer.compute(&graph);
assert!(scores.is_empty() || scores.len() == 1);
}
#[test]
fn test_compute_two_nodes_with_edge() {
let computer = PageRankComputer::new();
let mut graph = DependencyGraph::new();
let n1 = graph.add_node(NodeData {
path: std::path::PathBuf::from("a.rs"),
module: "a".to_string(),
symbols: vec![],
loc: 50,
complexity: 2.0,
ast_hash: 1,
});
let n2 = graph.add_node(NodeData {
path: std::path::PathBuf::from("b.rs"),
module: "b".to_string(),
symbols: vec![],
loc: 60,
complexity: 3.0,
ast_hash: 2,
});
graph.add_edge(
n1,
n2,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
let scores = computer.compute(&graph);
assert!(!scores.is_empty());
}
#[test]
fn test_compute_custom_damping() {
let computer = PageRankComputer::new().with_damping(0.5);
let mut graph = DependencyGraph::new();
graph.add_node(NodeData {
path: std::path::PathBuf::from("x.rs"),
module: "x".to_string(),
symbols: vec![],
loc: 10,
complexity: 1.0,
ast_hash: 99,
});
let scores = computer.compute(&graph);
assert!(scores.is_empty() || scores.len() == 1);
}
#[test]
fn test_compute_chain_graph() {
let computer = PageRankComputer::new();
let mut graph = DependencyGraph::new();
let n1 = graph.add_node(NodeData {
path: std::path::PathBuf::from("a.rs"),
module: "a".to_string(),
symbols: vec![],
loc: 10,
complexity: 1.0,
ast_hash: 1,
});
let n2 = graph.add_node(NodeData {
path: std::path::PathBuf::from("b.rs"),
module: "b".to_string(),
symbols: vec![],
loc: 20,
complexity: 2.0,
ast_hash: 2,
});
let n3 = graph.add_node(NodeData {
path: std::path::PathBuf::from("c.rs"),
module: "c".to_string(),
symbols: vec![],
loc: 30,
complexity: 3.0,
ast_hash: 3,
});
graph.add_edge(
n1,
n2,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
graph.add_edge(
n2,
n3,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
let scores = computer.compute(&graph);
assert!(!scores.is_empty());
}
#[test]
fn test_compute_cyclic_graph() {
let computer = PageRankComputer::new();
let mut graph = DependencyGraph::new();
let n1 = graph.add_node(NodeData {
path: std::path::PathBuf::from("a.rs"),
module: "a".to_string(),
symbols: vec![],
loc: 10,
complexity: 1.0,
ast_hash: 1,
});
let n2 = graph.add_node(NodeData {
path: std::path::PathBuf::from("b.rs"),
module: "b".to_string(),
symbols: vec![],
loc: 20,
complexity: 2.0,
ast_hash: 2,
});
graph.add_edge(
n1,
n2,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
graph.add_edge(
n2,
n1,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
let scores = computer.compute(&graph);
assert!(!scores.is_empty());
if scores.len() >= 2 {
assert!((scores[0] - scores[1]).abs() < 0.1);
}
}
#[test]
#[allow(deprecated)]
fn test_compute_legacy_from_graph() {
let computer = PageRankComputer::new();
let mut graph = DependencyGraph::new();
let n1 = graph.add_node(NodeData {
path: std::path::PathBuf::from("legacy1.rs"),
module: "legacy1".to_string(),
symbols: vec![],
loc: 10,
complexity: 1.0,
ast_hash: 1,
});
let n2 = graph.add_node(NodeData {
path: std::path::PathBuf::from("legacy2.rs"),
module: "legacy2".to_string(),
symbols: vec![],
loc: 20,
complexity: 2.0,
ast_hash: 2,
});
graph.add_edge(
n1,
n2,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
let matrices: GraphMatrices = GraphMatrices::from(&graph);
let scores = computer.compute_legacy(&matrices);
assert!(!scores.is_empty());
}
#[test]
fn test_pagerank_convergence_parameters() {
let computer = PageRankComputer {
damping: 0.99,
tolerance: 1e-10,
max_iterations: 1000,
};
assert_eq!(computer.damping, 0.99);
assert_eq!(computer.tolerance, 1e-10);
assert_eq!(computer.max_iterations, 1000);
}
#[test]
fn test_pagerank_with_multiple_edges() {
let computer = PageRankComputer::new();
let mut graph = DependencyGraph::new();
let n1 = graph.add_node(NodeData {
path: std::path::PathBuf::from("hub.rs"),
module: "hub".to_string(),
symbols: vec![],
loc: 100,
complexity: 10.0,
ast_hash: 1,
});
let n2 = graph.add_node(NodeData {
path: std::path::PathBuf::from("spoke1.rs"),
module: "spoke1".to_string(),
symbols: vec![],
loc: 10,
complexity: 1.0,
ast_hash: 2,
});
let n3 = graph.add_node(NodeData {
path: std::path::PathBuf::from("spoke2.rs"),
module: "spoke2".to_string(),
symbols: vec![],
loc: 10,
complexity: 1.0,
ast_hash: 3,
});
let n4 = graph.add_node(NodeData {
path: std::path::PathBuf::from("spoke3.rs"),
module: "spoke3".to_string(),
symbols: vec![],
loc: 10,
complexity: 1.0,
ast_hash: 4,
});
graph.add_edge(
n2,
n1,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
graph.add_edge(
n3,
n1,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
graph.add_edge(
n4,
n1,
EdgeData::Import {
weight: 1.0,
visibility: Visibility::Public,
},
);
let scores = computer.compute(&graph);
assert!(!scores.is_empty());
}
}