// Copyright (C) 2017-2019 The AXIOM TEAM Association. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . //! Provide a trait and implementations to find paths between nodes. use crate::data::WebOfTrust; use crate::data::WotId; use std::collections::{HashMap, VecDeque}; /// Find paths between 2 nodes of a `WebOfTrust`. pub trait CentralitiesCalculator { /// Compute betweenness centrality of all members. fn betweenness_centralities(&self, wot: &T) -> Vec; /// Compute stress centrality of all members. fn stress_centralities(&self, wot: &T) -> Vec; /// Compute distance stress centrality of all members. fn distance_stress_centralities(&self, wot: &T, step_max: usize) -> Vec; } /// An implementation based on "Ulrik brandes" algo. #[derive(Debug, Clone, Copy)] pub struct UlrikBrandesCentralityCalculator; impl CentralitiesCalculator for UlrikBrandesCentralityCalculator { fn betweenness_centralities(&self, wot: &T) -> Vec { let wot_size = wot.size(); let mut centralities = vec![0.0; wot_size]; let enabled_nodes = wot.get_enabled(); // The source of any path belongs to enabled_nodes for s in enabled_nodes.clone() { let mut stack: Vec = Vec::with_capacity(wot_size); let mut paths: HashMap> = HashMap::with_capacity(wot_size); let mut sigma = vec![0.0; wot_size]; let mut d: Vec = vec![-1; wot_size]; let mut q: VecDeque = VecDeque::with_capacity(wot_size); sigma[s.0] = 1.0; d[s.0] = 0; q.push_back(s); while let Some(v) = q.pop_front() { stack.push(v); for w in wot.get_links_source(v).expect("v don't have any source !") { // w found for the first time ? if d[w.0] < 0 { q.push_back(w); d[w.0] = d[v.0] + 1; } // Shortest path to w via v if d[w.0] == d[v.0] + 1 { sigma[w.0] += sigma[v.0]; paths.entry(w).or_insert_with(Vec::new).push(v); } } } let mut delta = vec![0.0; wot_size]; // stack returns vertices in order of non-increasing distance from s while let Some(w) = stack.pop() { if paths.contains_key(&w) { for v in paths.get(&w).expect("Not found w in p !") { if enabled_nodes.contains(&w) { delta[v.0] += (sigma[v.0] / sigma[w.0]) * (1.0 + delta[w.0]); } else { // If w not in enabled_nodes, no path can end at w delta[v.0] += (sigma[v.0] / sigma[w.0]) * delta[w.0]; } } } if w != s { centralities[w.0] += delta[w.0]; } } } centralities.into_iter().map(|c| c as u64).collect() } fn stress_centralities(&self, wot: &T) -> Vec { let wot_size = wot.size(); let mut centralities = vec![0.0; wot_size]; let enabled_nodes = wot.get_enabled(); // The source of any path belongs to enabled_nodes for s in enabled_nodes.clone() { let mut stack: Vec = Vec::with_capacity(wot_size); let mut paths: HashMap> = HashMap::with_capacity(wot_size); let mut sigma = vec![0.0; wot_size]; let mut d: Vec = vec![-1; wot_size]; let mut q: VecDeque = VecDeque::with_capacity(wot_size); sigma[s.0] = 1.0; d[s.0] = 0; q.push_back(s); while let Some(v) = q.pop_front() { stack.push(v); for w in wot.get_links_source(v).expect("v don't have any source !") { // w found for the first time ? if d[w.0] < 0 { q.push_back(w); d[w.0] = d[v.0] + 1; } // Shortest path to w via v if d[w.0] == d[v.0] + 1 { sigma[w.0] += sigma[v.0]; paths.entry(w).or_insert_with(Vec::new).push(v); } } } let mut delta = vec![0.0; wot_size]; // stack returns vertices in order of non-increasing distance from s while let Some(w) = stack.pop() { if paths.contains_key(&w) { for v in paths.get(&w).expect("Not found w in p !") { if enabled_nodes.contains(&w) { delta[v.0] += sigma[v.0] * (1.0 + (delta[w.0] / sigma[w.0])); } else { // If w not in enabled_nodes, no path can end at w delta[v.0] += sigma[v.0] * (delta[w.0] / sigma[w.0]); } } } if w != s { centralities[w.0] += delta[w.0]; } } } centralities.into_iter().map(|c| c as u64).collect() } fn distance_stress_centralities(&self, wot: &T, step_max: usize) -> Vec { let wot_size = wot.size(); let mut centralities = vec![0.0; wot_size]; let enabled_nodes = wot.get_enabled(); // The source of any path belongs to enabled_nodes for s in enabled_nodes.clone() { let mut stack: Vec = Vec::with_capacity(wot_size); let mut paths: HashMap> = HashMap::with_capacity(wot_size); let mut sigma = vec![0.0; wot_size]; let mut d: Vec = vec![-1; wot_size]; let mut q: VecDeque = VecDeque::with_capacity(wot_size); sigma[s.0] = 1.0; d[s.0] = 0; q.push_back(s); while let Some(v) = q.pop_front() { stack.push(v); if d[v.0] < step_max as isize { for w in wot.get_links_source(v).expect("v don't have any source !") { // w found for the first time ? if d[w.0] < 0 { q.push_back(w); d[w.0] = d[v.0] + 1; } // Shortest path to w via v if d[w.0] == d[v.0] + 1 { sigma[w.0] += sigma[v.0]; paths.entry(w).or_insert_with(Vec::new).push(v); } } } } let mut delta = vec![0.0; wot_size]; // stack returns vertices in order of non-increasing distance from s while let Some(w) = stack.pop() { if paths.contains_key(&w) { for v in paths.get(&w).expect("Not found w in p !") { if enabled_nodes.contains(&w) { delta[v.0] += sigma[v.0] * (1.0 + (delta[w.0] / sigma[w.0])); } else { // If w not in enabled_nodes, no path can end at w delta[v.0] += sigma[v.0] * (delta[w.0] / sigma[w.0]); } } } if w != s { centralities[w.0] += delta[w.0]; } } } centralities.into_iter().map(|c| c as u64).collect() } }