fa2 0.2.0

Rust implementation of the Force Atlas 2 graph layout algorithm.
Documentation
/// NOTE: I tried to perform attraction with SIMD but I could not observe a
/// significant performance boost, even using AVX2.
use crate::data::NeighborhoodIndex;
use crate::settings::FA2Settings;
use crate::traits::Float;

pub fn apply_attraction<F: Float>(
    settings: &FA2Settings<F>,
    xs: &[F],
    ys: &[F],
    edges: &[(usize, usize, F)],
    out_xs: &mut [F],
    out_ys: &mut [F],
) {
    for (source, target, weight) in edges.iter().copied() {
        let factor = -weight.powf(settings.edge_weight_influence);

        let x_dist = xs[source] - xs[target];
        let y_dist = ys[source] - ys[target];

        let x_displacement = x_dist * factor;
        let y_displacement = y_dist * factor;

        out_xs[source] += x_displacement;
        out_ys[source] += y_displacement;

        out_xs[target] -= x_displacement;
        out_ys[target] -= y_displacement;
    }
}

pub fn apply_nodewise_attraction<F: Float>(
    settings: &FA2Settings<F>,
    index: &NeighborhoodIndex<F>,
    source: usize,
    xs: &[F],
    ys: &[F],
    out_x: &mut F,
    out_y: &mut F,
) {
    let x = xs[source];
    let y = ys[source];

    for (target, weight) in index.stubs(source).iter().copied() {
        let factor = -weight.powf(settings.edge_weight_influence);

        let x_dist = x - xs[target];
        let y_dist = y - ys[target];

        let x_displacement = x_dist * factor;
        let y_displacement = y_dist * factor;

        *out_x += x_displacement;
        *out_y += y_displacement;
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_apply_attraction() {
        let settings = FA2Settings::<f32>::default();

        let xs = [1.0, 2.0];
        let ys = [3.0, -5.0];
        let edges = [(0, 1, 1.0)];
        let mut out_xs = [1.0, 2.0];
        let mut out_ys = [3.0, -5.0];

        apply_attraction(&settings, &xs, &ys, &edges, &mut out_xs, &mut out_ys);

        assert_eq!(out_xs, [2.0, 1.0]);
        assert_eq!(out_ys, [-5.0, 3.0])
    }
}