impl SimpleSparseMatrix {
pub fn new(nrows: usize, ncols: usize) -> Self {
Self {
nrows,
ncols,
triplets: Vec::new(),
}
}
pub fn push(&mut self, row: usize, col: usize, value: f64) {
self.triplets.push((row, col, value));
}
pub fn row_values(&self, row: usize) -> impl Iterator<Item = (usize, f64)> + '_ {
self.triplets
.iter()
.filter(move |(r, _, _)| *r == row)
.map(|(_, c, v)| (*c, *v))
}
pub fn row_as_vec(&self, row: usize) -> Vec<(usize, f64)> {
self.row_values(row).collect()
}
}
impl EdgeData {
pub fn to_numeric_weight(&self) -> f64 {
match self {
EdgeData::Import { weight, .. } => *weight * 2.0, EdgeData::FunctionCall { count, .. } => *count as f64,
EdgeData::TypeDependency { strength, .. } => *strength * 1.5,
EdgeData::DataFlow { confidence, .. } => *confidence,
EdgeData::Inheritance { depth } => 3.0 / (*depth as f64 + 1.0),
}
}
}
impl From<&DependencyGraph> for GraphMatrices {
fn from(graph: &DependencyGraph) -> Self {
let n = graph.node_count();
let mut adjacency = SimpleSparseMatrix::new(n, n);
let mut out_degrees = vec![0.0; n];
let mut edges = Vec::new();
for edge in graph.edge_references() {
let weight = edge.weight().to_numeric_weight();
let source = edge.source().0 as usize;
let target = edge.target().0 as usize;
if source < n && target < n {
edges.push((source, target, weight));
adjacency.push(source, target, weight);
out_degrees[source] += weight;
}
}
let transition = Self::normalize_columns(&adjacency, &out_degrees);
let laplacian = Self::compute_laplacian(&adjacency);
GraphMatrices {
adjacency,
transition,
laplacian,
out_degrees,
node_count: n,
edges,
}
}
}
impl GraphMatrices {
fn normalize_columns(
adjacency: &SimpleSparseMatrix,
out_degrees: &[f64],
) -> SimpleSparseMatrix {
let n = adjacency.nrows;
let mut result = SimpleSparseMatrix::new(n, n);
for i in 0..n {
if out_degrees[i] > 0.0 {
for (col, value) in adjacency.row_values(i) {
result.push(i, col, value / out_degrees[i]);
}
}
}
result
}
fn compute_laplacian(adjacency: &SimpleSparseMatrix) -> SimpleSparseMatrix {
let n = adjacency.nrows;
let mut result = SimpleSparseMatrix::new(n, n);
for i in 0..n {
let row_vals: Vec<_> = adjacency.row_values(i).collect();
let degree: f64 = row_vals.iter().map(|(_, v)| v).sum();
result.push(i, i, degree);
for (col, value) in row_vals {
result.push(i, col, -value);
}
}
result
}
}