Skip to content
Snippets Groups Projects
Commit 2d04cae6 authored by Éloïs's avatar Éloïs
Browse files

[enh] #62 impl betweenness & stress centralities computation with Ulrik Brandes algo

parent 94ce417a
No related branches found
No related tags found
No related merge requests found
......@@ -27,9 +27,11 @@
//! [js-tests]: https://github.com/duniter/wotb/blob/master/wotcpp/webOfTrust.cpp
#![cfg_attr(feature = "strict", deny(warnings))]
#![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
#![deny(
missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
unused_qualifications)]
unused_qualifications
)]
extern crate bincode;
extern crate byteorder;
......@@ -47,9 +49,10 @@ pub use data::{NodeId, WebOfTrust};
mod tests {
use super::*;
use data::*;
use operations::centrality::*;
use operations::distance::*;
use operations::path::*;
use operations::file::*;
use operations::path::*;
/// Test translated from https://github.com/duniter/wotb/blob/master/tests/test.js
///
......@@ -59,8 +62,9 @@ mod tests {
where
W: WebOfTrust + Sync,
{
let path_finder = RustyPathFinder {};
let centralities_calculator = UlrikBrandesCentralityCalculator {};
let distance_calculator = RustyDistanceCalculator {};
let path_finder = RustyPathFinder {};
let mut wot = W::new(3);
// should have an initial size of 0
......@@ -561,8 +565,31 @@ mod tests {
},)
);
// Test centralities computation in g1_genesis wot
let wot_size = wot3.size();
// Test betweenness centralities computation in g1_genesis wot
let centralities = centralities_calculator.betweenness_centralities(&wot3);
assert_eq!(centralities.len(), 59);
assert_eq!(
centralities,
vec![
148, 30, 184, 11, 60, 51, 40, 115, 24, 140, 47, 69, 16, 34, 94, 126, 151, 0, 34,
133, 20, 103, 38, 144, 73, 523, 124, 23, 47, 17, 9, 64, 77, 281, 6, 105, 54, 0,
111, 21, 6, 2, 0, 1, 47, 59, 28, 236, 0, 0, 0, 0, 60, 6, 0, 1, 8, 33, 169,
]
);
// Test stress centralities computation in g1_genesis wot
let stress_centralities = centralities_calculator.stress_centralities(&wot3);
assert_eq!(stress_centralities.len(), 59);
assert_eq!(
stress_centralities,
vec![
848, 240, 955, 80, 416, 203, 290, 645, 166, 908, 313, 231, 101, 202, 487, 769, 984,
0, 154, 534, 105, 697, 260, 700, 496, 1726, 711, 160, 217, 192, 89, 430, 636, 1276,
41, 420, 310, 0, 357, 125, 50, 15, 0, 12, 275, 170, 215, 1199, 0, 0, 0, 0, 201, 31,
0, 9, 55, 216, 865,
]
);
/*let wot_size = wot3.size();
let members_count = wot3.get_enabled().len() as u64;
assert_eq!(members_count, 59);
let oriented_couples_count: u64 = members_count * (members_count - 1);
......@@ -593,6 +620,6 @@ mod tests {
for centrality in centralities {
relative_centralities.push((centrality * 100_000 / oriented_couples_count) as usize);
}
assert_eq!(relative_centralities.len(), 59);
assert_eq!(relative_centralities.len(), 59);*/
}
}
// Copyright (C) 2017-2018 The Duniter Project Developers.
//
// 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 <https://www.gnu.org/licenses/>.
//! Provide a trait and implementations to find paths between nodes.
use data::NodeId;
use data::WebOfTrust;
use std::collections::{HashMap, VecDeque};
/// Find paths between 2 nodes of a `WebOfTrust`.
pub trait CentralitiesCalculator<T: WebOfTrust> {
/// Compute betweenness centrality of all members.
fn betweenness_centralities(&self, wot: &T) -> Vec<u64>;
/// Compute stress centrality of all members.
fn stress_centralities(&self, wot: &T) -> Vec<u64>;
}
/// A new "rusty-er" implementation of `WoT` path finding.
#[derive(Debug, Clone, Copy)]
pub struct UlrikBrandesCentralityCalculator;
impl<T: WebOfTrust> CentralitiesCalculator<T> for UlrikBrandesCentralityCalculator {
fn betweenness_centralities(&self, wot: &T) -> Vec<u64> {
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<NodeId> = Vec::with_capacity(wot_size);
let mut paths: HashMap<NodeId, Vec<NodeId>> = HashMap::with_capacity(wot_size);
let mut sigma = vec![0.0; wot_size];
let mut d: Vec<isize> = vec![-1; wot_size];
let mut q: VecDeque<NodeId> = VecDeque::with_capacity(wot_size);
sigma[s.0] = 1.0;
d[s.0] = 0;
q.push_back(s);
while !q.is_empty() {
let v = q.pop_front().unwrap();
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(vec![]).push(v);
}
}
}
let mut delta = vec![0.0; wot_size];
// stack returns vertices in order of non-increasing distance from s
while !stack.is_empty() {
let w = stack.pop().unwrap();
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<u64> {
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<NodeId> = Vec::with_capacity(wot_size);
let mut paths: HashMap<NodeId, Vec<NodeId>> = HashMap::with_capacity(wot_size);
let mut sigma = vec![0.0; wot_size];
let mut d: Vec<isize> = vec![-1; wot_size];
let mut q: VecDeque<NodeId> = VecDeque::with_capacity(wot_size);
sigma[s.0] = 1.0;
d[s.0] = 0;
q.push_back(s);
while !q.is_empty() {
let v = q.pop_front().unwrap();
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[w.0] + sigma[v.0];
paths.entry(w).or_insert(vec![]).push(v);
}
}
}
let mut delta = vec![0.0; wot_size];
// stack returns vertices in order of non-increasing distance from s
while !stack.is_empty() {
let w = stack.pop().unwrap();
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()
}
}
......@@ -15,6 +15,7 @@
//! Provide operation traits and implementations on `WebOfTrust` objects.
pub mod path;
pub mod centrality;
pub mod distance;
pub mod file;
pub mod path;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment