rustsim-crowd 0.0.1

Microscopic crowd and pedestrian locomotion for rustsim: 2-D and layered 3-D, with Social Force, Collision-Free Speed, Generalized Centrifugal Force, Optimal Steps, and Anticipation Velocity models
Documentation
//! Per-model tolerance harness for the SIMD inner-loop integration
//! (P1-5 follow-up).
//!
//! Gated on the `simd` feature. For every model that ships a
//! `step_scratch_simd` entry point, run one tick of a representative
//! counter-flow fixture through both the scalar `step_scratch` and
//! the SIMD `step_scratch_simd`, then assert per-agent positional
//! and velocity divergence below `1e-9` — matching the unit-test
//! envelope on the `simd::pair_force_x4` helper itself.
//!
//! The harness uses a 32-pedestrian counter-flow with two walls,
//! enough to populate every grid cell with ≥ 4 neighbours so the
//! SIMD path covers both full and partial chunks. The test is
//! intentionally a single tick: lane re-ordering does not compound,
//! and a 1-tick check is sufficient to pin the contract.

#![cfg(feature = "simd")]

use rustsim_crowd::broadphase::Scratch;
use rustsim_crowd::common::{Pedestrian, WallSegment};

fn seed() -> Vec<Pedestrian> {
    (0..32)
        .map(|i| {
            let lane = (i % 4) as f64;
            let col = (i / 4) as f64;
            let dir = if i % 2 == 0 { 1.0 } else { -1.0 };
            Pedestrian::new(
                [col * 0.8, lane * 0.6],
                [dir * 0.5, 0.0],
                0.25,
                1.34,
                [dir * 50.0, lane * 0.6],
            )
        })
        .collect()
}

fn walls() -> Vec<WallSegment> {
    vec![
        WallSegment {
            a: [-20.0, -1.0],
            b: [20.0, -1.0],
        },
        WallSegment {
            a: [-20.0, 3.0],
            b: [20.0, 3.0],
        },
    ]
}

#[test]
fn sfm_step_scratch_simd_matches_scalar_within_tolerance() {
    use rustsim_crowd::social_force::{neighbor_cutoff, step_scratch, step_scratch_simd, Params};

    let mut peds_scalar = seed();
    let mut peds_simd = seed();
    let walls = walls();
    let params = Params::default();
    let dt = 0.05;

    let mut scr_scalar = Scratch::new(neighbor_cutoff(&params));
    let mut scr_simd = Scratch::new(neighbor_cutoff(&params));

    step_scratch(&mut peds_scalar, &walls, &params, dt, &mut scr_scalar);
    step_scratch_simd(&mut peds_simd, &walls, &params, dt, &mut scr_simd);

    for (i, (a, b)) in peds_scalar.iter().zip(peds_simd.iter()).enumerate() {
        let dx = (a.pos[0] - b.pos[0]).abs();
        let dy = (a.pos[1] - b.pos[1]).abs();
        let dvx = (a.vel[0] - b.vel[0]).abs();
        let dvy = (a.vel[1] - b.vel[1]).abs();
        assert!(
            dx < 1.0e-9 && dy < 1.0e-9,
            "SFM agent {i} pos diverged: scalar={:?} simd={:?}",
            a.pos,
            b.pos
        );
        assert!(
            dvx < 1.0e-9 && dvy < 1.0e-9,
            "SFM agent {i} vel diverged: scalar={:?} simd={:?}",
            a.vel,
            b.vel
        );
    }
}

#[test]
fn gcf_step_scratch_simd_matches_scalar_within_tolerance() {
    use rustsim_crowd::generalized_centrifugal_force::{
        neighbor_cutoff, step_scratch, step_scratch_simd, Params,
    };

    let mut peds_scalar = seed();
    let mut peds_simd = seed();
    let walls = walls();
    let params = Params::default();
    let dt = 0.05;

    let mut scr_scalar = Scratch::new(neighbor_cutoff(&params));
    let mut scr_simd = Scratch::new(neighbor_cutoff(&params));

    step_scratch(&mut peds_scalar, &walls, &params, dt, &mut scr_scalar);
    step_scratch_simd(&mut peds_simd, &walls, &params, dt, &mut scr_simd);

    for (i, (a, b)) in peds_scalar.iter().zip(peds_simd.iter()).enumerate() {
        let dx = (a.pos[0] - b.pos[0]).abs();
        let dy = (a.pos[1] - b.pos[1]).abs();
        let dvx = (a.vel[0] - b.vel[0]).abs();
        let dvy = (a.vel[1] - b.vel[1]).abs();
        assert!(
            dx < 1.0e-9 && dy < 1.0e-9,
            "GCF agent {i} pos diverged: scalar={:?} simd={:?}",
            a.pos,
            b.pos
        );
        assert!(
            dvx < 1.0e-9 && dvy < 1.0e-9,
            "GCF agent {i} vel diverged: scalar={:?} simd={:?}",
            a.vel,
            b.vel
        );
    }
}

#[test]
fn cfs_step_scratch_simd_matches_scalar_within_tolerance() {
    use rustsim_crowd::collision_free_speed::{
        neighbor_cutoff, step_scratch, step_scratch_simd, Params,
    };

    let mut peds_scalar = seed();
    let mut peds_simd = seed();
    let walls = walls();
    let params = Params::default();
    let dt = 0.05;

    let mut scr_scalar = Scratch::new(neighbor_cutoff(&params));
    let mut scr_simd = Scratch::new(neighbor_cutoff(&params));

    step_scratch(&mut peds_scalar, &walls, &params, dt, &mut scr_scalar);
    step_scratch_simd(&mut peds_simd, &walls, &params, dt, &mut scr_simd);

    for (i, (a, b)) in peds_scalar.iter().zip(peds_simd.iter()).enumerate() {
        let dx = (a.pos[0] - b.pos[0]).abs();
        let dy = (a.pos[1] - b.pos[1]).abs();
        let dvx = (a.vel[0] - b.vel[0]).abs();
        let dvy = (a.vel[1] - b.vel[1]).abs();
        assert!(
            dx < 1.0e-9 && dy < 1.0e-9,
            "CFS agent {i} pos diverged: scalar={:?} simd={:?}",
            a.pos,
            b.pos
        );
        assert!(
            dvx < 1.0e-9 && dvy < 1.0e-9,
            "CFS agent {i} vel diverged: scalar={:?} simd={:?}",
            a.vel,
            b.vel
        );
    }
}

#[test]
fn avm_step_scratch_simd_matches_scalar_within_tolerance() {
    use rustsim_crowd::anticipation_velocity::{
        neighbor_cutoff, step_scratch, step_scratch_simd, Params,
    };

    let mut peds_scalar = seed();
    let mut peds_simd = seed();
    let walls = walls();
    let params = Params::default();
    let dt = 0.05;

    let mut scr_scalar = Scratch::new(neighbor_cutoff(&params));
    let mut scr_simd = Scratch::new(neighbor_cutoff(&params));

    step_scratch(&mut peds_scalar, &walls, &params, dt, &mut scr_scalar);
    step_scratch_simd(&mut peds_simd, &walls, &params, dt, &mut scr_simd);

    for (i, (a, b)) in peds_scalar.iter().zip(peds_simd.iter()).enumerate() {
        let dx = (a.pos[0] - b.pos[0]).abs();
        let dy = (a.pos[1] - b.pos[1]).abs();
        let dvx = (a.vel[0] - b.vel[0]).abs();
        let dvy = (a.vel[1] - b.vel[1]).abs();
        assert!(
            dx < 1.0e-9 && dy < 1.0e-9,
            "AVM agent {i} pos diverged: scalar={:?} simd={:?}",
            a.pos,
            b.pos
        );
        assert!(
            dvx < 1.0e-9 && dvy < 1.0e-9,
            "AVM agent {i} vel diverged: scalar={:?} simd={:?}",
            a.vel,
            b.vel
        );
    }
}