diff --git a/wotb/Cargo.toml b/wotb/Cargo.toml index d532aef14aaac24abbb6dd1f1855c8996d739b5d..f2c8d97462f9986e4dbc85414f4a6fa4e3940f98 100644 --- a/wotb/Cargo.toml +++ b/wotb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "duniter-wotb" -version = "0.8.0-a0.5" +version = "0.8.0-a0.6" authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@duniter.org>"] description = "Makes Web of Trust computations for the Duniter project." repository = "https://git.duniter.org/nodes/rust/duniter-rs" diff --git a/wotb/lib.rs b/wotb/lib.rs index f30dec63682a72238a885ea9f297500f610aa322..a68352c5b6be9ee1fe94e6bcacead24f6872ae34 100644 --- a/wotb/lib.rs +++ b/wotb/lib.rs @@ -589,37 +589,19 @@ mod tests { 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); - let mut centralities = vec![0; wot_size]; - for i in 0..wot_size { - for j in 0..wot_size { - let paths = path_finder.find_paths(&wot3, NodeId(i), NodeId(j), 5); - let mut intermediate_members: Vec<NodeId> = Vec::new(); - for path in paths { - if path.len() > 2 { - for node_id in &path[1..path.len() - 1] { - if !intermediate_members.contains(node_id) { - intermediate_members.push(*node_id); - } - } - } - } - let centralities_copy = centralities.clone(); - for node_id in intermediate_members { - let centrality = ¢ralities_copy[node_id.0]; - if let Some(tmp) = centralities.get_mut(node_id.0) { - *tmp = *centrality + 1; - } - } - } - } - let mut relative_centralities = Vec::with_capacity(wot_size); - for centrality in centralities { - relative_centralities.push((centrality * 100_000 / oriented_couples_count) as usize); - } - assert_eq!(relative_centralities.len(), 59);*/ + + // Test distance stress centralities computation in g1_genesis wot + let distance_stress_centralities = + centralities_calculator.distance_stress_centralities(&wot3, 5); + assert_eq!(distance_stress_centralities.len(), 59); + assert_eq!( + distance_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, + ] + ); } } diff --git a/wotb/operations/centrality.rs b/wotb/operations/centrality.rs index 97ef9396600231d555b43c73243f519777f3bfa9..c4470a82052334f5ed7c6dcdc0b1468c8e11e984 100644 --- a/wotb/operations/centrality.rs +++ b/wotb/operations/centrality.rs @@ -25,9 +25,11 @@ pub trait CentralitiesCalculator<T: WebOfTrust> { fn betweenness_centralities(&self, wot: &T) -> Vec<u64>; /// Compute stress centrality of all members. fn stress_centralities(&self, wot: &T) -> Vec<u64>; + /// Compute distance stress centrality of all members. + fn distance_stress_centralities(&self, wot: &T, step_max: usize) -> Vec<u64>; } -/// A new "rusty-er" implementation of `WoT` path finding. +/// An implementation based on "Ulrik brandes" algo. #[derive(Debug, Clone, Copy)] pub struct UlrikBrandesCentralityCalculator; @@ -138,4 +140,59 @@ impl<T: WebOfTrust> CentralitiesCalculator<T> for UlrikBrandesCentralityCalculat } centralities.into_iter().map(|c| c as u64).collect() } + fn distance_stress_centralities(&self, wot: &T, step_max: usize) -> 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); + 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 !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() + } }