rustsim-core 0.0.1

Core ABM engine: agents, models, stores, schedulers, stepping, data collection
Documentation
#![allow(clippy::type_complexity)]

use rand::rngs::StdRng;
use rand::{RngCore, SeedableRng};
use rustsim_core::{
    event_queue::{EventContext, EventQueueModel},
    prelude::*,
};
mod support;
use support::NothingSpace;

#[derive(Debug, Clone)]
struct ReplayAgent {
    id: AgentId,
    value: u64,
}

impl Agent for ReplayAgent {
    fn id(&self) -> AgentId {
        self.id
    }
}

type ReplayModel =
    StandardModel<NothingSpace, ReplayAgent, VecStore<ReplayAgent>, (), StdRng, Randomly>;

fn replay_step(
    agent: &mut ReplayAgent,
    ctx: &mut StepContext<'_, NothingSpace, ReplayAgent, (), StdRng, Randomly>,
) {
    let draw = (ctx.rng().next_u32() % 1000) as u64;
    agent.value = agent.value.wrapping_mul(31).wrapping_add(draw);
}

fn run_standard_replay(seed: u64) -> Vec<(AgentId, u64)> {
    let mut store = VecStore::new();
    for id in 1..=6 {
        store.insert(ReplayAgent { id, value: id * 10 });
    }

    let mut model = ReplayModel::new(
        store,
        NothingSpace,
        Randomly::new(),
        (),
        StdRng::seed_from_u64(seed),
        Some(Box::new(replay_step)),
        None,
        true,
    );

    model.step_n(12);

    (1..=6)
        .map(|id| (id, model.agent(id).unwrap().value))
        .collect()
}

#[test]
fn standard_model_replay_is_deterministic_with_fixed_seed() {
    let run1 = run_standard_replay(42);
    let run2 = run_standard_replay(42);

    assert_eq!(run1, run2);
}

#[derive(Debug, Clone)]
struct ReplayEventAgent {
    id: AgentId,
    remaining: u32,
}

impl Agent for ReplayEventAgent {
    fn id(&self) -> AgentId {
        self.id
    }
}

type EventLog = Vec<(AgentId, u32, u32)>;
type ReplayEventModel = EventQueueModel<
    NothingSpace,
    ReplayEventAgent,
    HashMapStore<ReplayEventAgent>,
    EventLog,
    StdRng,
>;

fn replay_event_action(
    agent: &mut ReplayEventAgent,
    ctx: &mut EventContext<'_, NothingSpace, ReplayEventAgent, EventLog, StdRng>,
) {
    let draw = ctx.rng().next_u32();
    let time = ctx.time() as u32;
    ctx.properties_mut().push((agent.id, time, draw));

    agent.remaining -= 1;
    if agent.remaining > 0 {
        ctx.add_event(agent.id, 0, 1.0);
    }
}

fn run_event_replay(seed: u64) -> EventLog {
    let mut store = HashMapStore::new();
    store.insert(ReplayEventAgent {
        id: 1,
        remaining: 3,
    });
    store.insert(ReplayEventAgent {
        id: 2,
        remaining: 3,
    });

    let actions: Vec<
        fn(
            &mut ReplayEventAgent,
            &mut EventContext<'_, NothingSpace, ReplayEventAgent, EventLog, StdRng>,
        ),
    > = vec![replay_event_action];

    let mut model = ReplayEventModel::new(
        store,
        NothingSpace,
        Vec::new(),
        StdRng::seed_from_u64(seed),
        actions,
    );

    model.add_event(1, 0, 1.0);
    model.add_event(2, 0, 1.0);
    model.run_events(6);

    model.properties().clone()
}

#[test]
fn event_queue_replay_is_deterministic_with_equal_time_events() {
    let run1 = run_event_replay(7);
    let run2 = run_event_replay(7);

    assert_eq!(run1, run2);
}