use crate::core::id::NodeId;
use crate::experimental::sybil::PropagationModel;
use std::collections::HashMap;
pub struct WildfirePropagation {
pub temperatures: HashMap<NodeId, f32>,
pub base_alpha: f32,
pub temp_multiplier: f32,
}
impl WildfirePropagation {
pub fn new(
temperatures: HashMap<NodeId, f32>,
base_alpha: f32,
temp_multiplier: f32,
) -> Self {
Self {
temperatures,
base_alpha: base_alpha.clamp(0.0, 1.0),
temp_multiplier: temp_multiplier.max(0.0),
}
}
fn get_dynamic_alpha(&self, node_id: NodeId) -> f32 {
let temp = self.temperatures.get(&node_id).copied().unwrap_or(0.0);
let alpha = self.base_alpha + (self.temp_multiplier * temp);
alpha.clamp(0.0, 1.0)
}
}
impl PropagationModel for WildfirePropagation {
fn next_state(
&self,
node_id: NodeId,
current_self: Option<&[f32]>,
neighbors: &[(NodeId, &[f32])],
) -> Option<Vec<f32>> {
if neighbors.is_empty() {
return current_self.map(|v| v.to_vec());
}
let dim = neighbors[0].1.len();
let mut sum = vec![0.0; dim];
let mut count = 0;
for (_, vec) in neighbors {
if vec.len() == dim {
for i in 0..dim {
sum[i] += vec[i];
}
count += 1;
}
}
if count == 0 {
return current_self.map(|v| v.to_vec());
}
for val in sum.iter_mut().take(dim) {
*val /= count as f32;
}
let alpha = self.get_dynamic_alpha(node_id);
match current_self {
Some(self_vec) if self_vec.len() == dim => {
let mut new_vec = vec![0.0; dim];
for i in 0..dim {
new_vec[i] = (1.0 - alpha) * self_vec[i] + alpha * sum[i];
}
Some(new_vec)
}
_ => Some(sum),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::AletheiaDB;
use crate::core::property::PropertyMapBuilder;
use crate::experimental::sybil::Sybil;
#[test]
fn test_wildfire_propagation() {
let db = AletheiaDB::new().unwrap();
let props_a = PropertyMapBuilder::new()
.insert_vector("opinion", &[1.0])
.build();
let node_a = db.create_node("Person", props_a).unwrap();
let props_b = PropertyMapBuilder::new()
.insert_vector("opinion", &[0.0])
.build();
let node_b = db.create_node("Person", props_b).unwrap();
let props_c = PropertyMapBuilder::new()
.insert_vector("opinion", &[0.0])
.build();
let node_c = db.create_node("Person", props_c).unwrap();
db.create_edge(node_a, node_b, "INFLUENCES", Default::default())
.unwrap();
db.create_edge(node_b, node_c, "INFLUENCES", Default::default())
.unwrap();
let mut temps = HashMap::new();
temps.insert(node_b, 10.0);
temps.insert(node_c, 0.0);
let model = WildfirePropagation::new(temps, 0.0, 0.1);
let sybil = Sybil::new(&db);
let state = sybil.simulate("opinion", &model, 2).unwrap();
let vec_b = state.get(&node_b).unwrap();
let vec_c = state.get(&node_c).unwrap();
assert_eq!(vec_b[0], 1.0);
assert_eq!(vec_c[0], 0.0);
}
}