use crate::{
agent::Agent,
interaction::{InteractionError, PositionedAgent, SpaceInteraction},
model::Model,
scheduler::Scheduler,
space::Space,
store::AgentStore,
types::{AgentId, Time},
};
use rand::RngCore;
use tracing::{debug, trace};
use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData;
pub trait HasAgentIds {
fn agent_ids(&self) -> Vec<AgentId>;
fn agent_ids_into(&self, buf: &mut Vec<AgentId>);
}
impl<S, A, Store, Props, R, Sch> StandardModel<S, A, Store, Props, R, Sch>
where
A: PositionedAgent,
S: SpaceInteraction<A>,
Store: AgentStore<A>,
R: RngCore,
Sch: Scheduler<Self>,
{
pub fn insert_positioned_agent(&mut self, agent: A) -> Result<(), InteractionError<S::Error>> {
crate::interaction::add_agent(self, agent)
}
pub fn remove_positioned_agent(
&mut self,
id: AgentId,
) -> Result<Option<A>, InteractionError<S::Error>> {
crate::interaction::remove_agent(self, id)
}
pub fn move_positioned_agent(
&mut self,
id: AgentId,
new_position: A::Position,
) -> Result<(), InteractionError<S::Error>> {
crate::interaction::move_agent(self, id, new_position)
}
pub fn validate_space_index(&self) -> Result<(), InteractionError<S::Error>> {
crate::interaction::validate_space_index(self)
}
pub fn step_spatial(&mut self) -> Result<(), InteractionError<S::Error>> {
let has_agent_step = self.agent_step_ctx.is_some();
let has_model_step = self.model_step.is_some();
if !(has_agent_step || has_model_step) {
trace!("spatial step skipped: no agent_step or model_step defined");
return Ok(());
}
if self.agents_first {
self.step_agents_spatial()?;
self.step_model();
} else {
self.step_model();
self.step_agents_spatial()?;
}
self.time = match self.time {
Time::Discrete(t) => Time::Discrete(t.saturating_add(1)),
Time::Continuous(t) => Time::Continuous(t + 1.0),
};
debug!(time = %self.time, agents = self.agents.len(), "spatial step completed");
Ok(())
}
pub fn run_spatial(&mut self, steps: usize) -> Result<(), InteractionError<S::Error>> {
for _ in 0..steps {
self.step_spatial()?;
}
Ok(())
}
fn apply_deferred_actions_spatial(
&mut self,
deferred: &mut Vec<crate::step_context::DeferredAction<A>>,
) -> Result<(), InteractionError<S::Error>> {
use crate::step_context::DeferredAction;
if deferred.is_empty() {
return Ok(());
}
for action in deferred.drain(..) {
match action {
DeferredAction::RemoveAgent(id) => {
crate::interaction::remove_agent(self, id)?;
}
DeferredAction::InsertAgent(agent) => {
crate::interaction::add_agent(self, agent)?;
}
}
}
Ok(())
}
fn step_agents_spatial(&mut self) -> Result<(), InteractionError<S::Error>> {
let mut ids = std::mem::take(&mut self.schedule_buf);
ids.clear();
{
let mut sched = self.scheduler.borrow_mut();
sched.schedule_into(&*self, &mut ids);
}
if self.agent_step_ctx.is_none() {
self.schedule_buf = ids;
return Ok(());
}
let mut deferred = std::mem::take(&mut self.deferred_buf);
deferred.clear();
for &id in &ids {
let Some(mut agent_ref) = self.agents.get_mut(id) else {
continue;
};
{
let mut rng = self.rng.borrow_mut();
let mut sched = self.scheduler.borrow_mut();
let mut ctx = crate::step_context::StepContext {
space: &mut self.space,
properties: &mut self.properties,
rng: &mut *rng,
scheduler: &mut *sched,
deferred: &mut deferred,
_agent: PhantomData,
};
if let Some(step_fn) = self.agent_step_ctx.as_mut() {
step_fn(&mut *agent_ref, &mut ctx);
}
}
drop(agent_ref);
self.apply_deferred_actions_spatial(&mut deferred)?;
}
self.deferred_buf = deferred;
self.schedule_buf = ids;
Ok(())
}
}
pub type AgentStepFn<S, A, Props, R, Sch> =
Box<dyn for<'a> FnMut(&mut A, &mut crate::step_context::StepContext<'a, S, A, Props, R, Sch>)>;
pub struct StandardModel<S, A, Store, Props, R, Sch>
where
A: Agent,
S: Space,
Store: AgentStore<A>,
R: RngCore,
{
pub(crate) agents: Store,
pub(crate) space: S,
pub(crate) scheduler: RefCell<Sch>,
pub(crate) properties: Props,
pub(crate) rng: RefCell<R>,
pub(crate) time: Time,
pub(crate) max_id: AgentId,
pub(crate) agents_first: bool,
pub(crate) agent_step_ctx: Option<AgentStepFn<S, A, Props, R, Sch>>,
pub(crate) model_step: Option<fn(&mut Self)>,
pub(crate) deferred_buf: Vec<crate::step_context::DeferredAction<A>>,
pub(crate) schedule_buf: Vec<AgentId>,
pub(crate) agent_marker: PhantomData<A>,
}
impl<S, A, Store, Props, R, Sch> StandardModel<S, A, Store, Props, R, Sch>
where
A: Agent,
S: Space,
Store: AgentStore<A>,
R: RngCore,
Sch: Scheduler<Self>,
{
#[allow(clippy::too_many_arguments)]
pub fn new(
agents: Store,
space: S,
scheduler: Sch,
properties: Props,
rng: R,
agent_step_ctx: Option<AgentStepFn<S, A, Props, R, Sch>>,
model_step: Option<fn(&mut Self)>,
agents_first: bool,
) -> Self {
let max_id = agents.iter_ids().into_iter().max().unwrap_or(0);
Self {
agents,
space,
scheduler: RefCell::new(scheduler),
properties,
rng: RefCell::new(rng),
time: Time::Discrete(0),
max_id,
agents_first,
agent_step_ctx,
model_step,
deferred_buf: Vec::new(),
schedule_buf: Vec::new(),
agent_marker: PhantomData,
}
}
pub fn new_base(agents: Store, space: S, scheduler: Sch, properties: Props, rng: R) -> Self {
Self::new(agents, space, scheduler, properties, rng, None, None, true)
}
pub fn new_with_agent_step(
agents: Store,
space: S,
scheduler: Sch,
properties: Props,
rng: R,
agent_step_ctx: impl for<'a> FnMut(&mut A, &mut crate::step_context::StepContext<'a, S, A, Props, R, Sch>)
+ 'static,
agents_first: bool,
) -> Self {
Self::new(
agents,
space,
scheduler,
properties,
rng,
Some(Box::new(agent_step_ctx)),
None,
agents_first,
)
}
pub fn new_with_model_step(
agents: Store,
space: S,
scheduler: Sch,
properties: Props,
rng: R,
model_step: fn(&mut Self),
agents_first: bool,
) -> Self {
Self::new(
agents,
space,
scheduler,
properties,
rng,
None,
Some(model_step),
agents_first,
)
}
pub fn with_agent_step_ctx(
mut self,
agent_step_ctx: impl for<'a> FnMut(&mut A, &mut crate::step_context::StepContext<'a, S, A, Props, R, Sch>)
+ 'static,
) -> Self {
self.agent_step_ctx = Some(Box::new(agent_step_ctx));
self
}
pub fn with_model_step(mut self, model_step: fn(&mut Self)) -> Self {
self.model_step = Some(model_step);
self
}
pub fn with_agents_first(mut self, agents_first: bool) -> Self {
self.agents_first = agents_first;
self
}
pub fn time(&self) -> Time {
self.time
}
pub fn rng_mut(&self) -> RefMut<'_, R> {
self.rng.borrow_mut()
}
pub fn space(&self) -> &S {
&self.space
}
pub fn space_mut(&mut self) -> &mut S {
&mut self.space
}
pub fn properties(&self) -> &Props {
&self.properties
}
pub fn properties_mut(&mut self) -> &mut Props {
&mut self.properties
}
pub fn agent(&self, id: AgentId) -> Option<Ref<'_, A>> {
self.agents.get(id)
}
pub fn agent_mut(&self, id: AgentId) -> Option<RefMut<'_, A>> {
self.agents.get_mut(id)
}
pub fn insert_agent(&mut self, agent: A) -> Result<(), A> {
let id = agent.id();
if self.agents.contains(id) {
return Err(agent);
}
self.agents.insert(agent);
if id > self.max_id {
self.max_id = id;
}
Ok(())
}
pub fn remove_agent(&mut self, id: AgentId) -> Option<A> {
self.agents.remove(id)
}
pub fn next_id(&mut self) -> AgentId {
self.max_id += 1;
self.max_id
}
pub fn step(&mut self) {
let has_agent_step = self.agent_step_ctx.is_some();
let has_model_step = self.model_step.is_some();
if !(has_agent_step || has_model_step) {
trace!("step skipped: no agent_step or model_step defined");
return;
}
if self.agents_first {
self.step_agents();
self.step_model();
} else {
self.step_model();
self.step_agents();
}
self.time = match self.time {
Time::Discrete(t) => Time::Discrete(t.saturating_add(1)),
Time::Continuous(t) => Time::Continuous(t + 1.0),
};
debug!(time = %self.time, agents = self.agents.len(), "step completed");
}
pub fn step_n(&mut self, n: usize) {
for _ in 0..n {
self.step();
}
}
pub fn run(&mut self, steps: usize) {
self.step_n(steps);
}
fn apply_deferred_actions(
&mut self,
deferred: &mut Vec<crate::step_context::DeferredAction<A>>,
) {
use crate::step_context::DeferredAction;
if deferred.is_empty() {
return;
}
for action in deferred.drain(..) {
match action {
DeferredAction::RemoveAgent(id) => {
self.remove_agent(id);
}
DeferredAction::InsertAgent(agent) => {
let _ = self.insert_agent(agent);
}
}
}
}
fn step_agents(&mut self) {
let mut ids = std::mem::take(&mut self.schedule_buf);
ids.clear();
{
let mut sched = self.scheduler.borrow_mut();
sched.schedule_into(&*self, &mut ids);
}
if self.agent_step_ctx.is_none() {
self.schedule_buf = ids;
return;
}
let mut deferred = std::mem::take(&mut self.deferred_buf);
deferred.clear();
for &id in &ids {
let Some(mut agent_ref) = self.agents.get_mut(id) else {
continue;
};
{
let mut rng = self.rng.borrow_mut();
let mut sched = self.scheduler.borrow_mut();
let mut ctx = crate::step_context::StepContext {
space: &mut self.space,
properties: &mut self.properties,
rng: &mut *rng,
scheduler: &mut *sched,
deferred: &mut deferred,
_agent: PhantomData,
};
if let Some(step_fn) = self.agent_step_ctx.as_mut() {
step_fn(&mut *agent_ref, &mut ctx);
}
}
drop(agent_ref);
self.apply_deferred_actions(&mut deferred);
}
self.deferred_buf = deferred;
self.schedule_buf = ids;
}
fn step_model(&mut self) {
if let Some(step_fn) = self.model_step {
step_fn(self);
}
}
pub fn agents(&self) -> impl Iterator<Item = Ref<'_, A>> {
self.agents
.iter_ids()
.into_iter()
.filter_map(|id| self.agents.get(id))
}
pub fn agents_len(&self) -> usize {
self.agents.len()
}
}
impl<S, A, Store, Props, R, Sch> HasAgentIds for StandardModel<S, A, Store, Props, R, Sch>
where
A: Agent,
S: Space,
Store: AgentStore<A>,
R: RngCore,
{
fn agent_ids(&self) -> Vec<AgentId> {
self.agents.iter_ids()
}
fn agent_ids_into(&self, buf: &mut Vec<AgentId>) {
self.agents.iter_ids_into(buf);
}
}
impl<S, A, Store, Props, R, Sch> Model for StandardModel<S, A, Store, Props, R, Sch>
where
A: Agent,
S: Space,
Store: AgentStore<A>,
R: rand::RngCore,
{
type Agent = A;
type Space = S;
type Properties = Props;
type Rng = R;
type AgentRef<'a>
= Ref<'a, A>
where
Self: 'a;
type AgentRefMut<'a>
= RefMut<'a, A>
where
Self: 'a;
fn time(&self) -> Time {
self.time
}
fn rng_mut(&self) -> impl std::ops::DerefMut<Target = Self::Rng> + '_ {
self.rng.borrow_mut()
}
fn space(&self) -> &Self::Space {
&self.space
}
fn properties(&self) -> &Self::Properties {
&self.properties
}
fn properties_mut(&mut self) -> &mut Self::Properties {
&mut self.properties
}
fn agent(&self, id: AgentId) -> Option<Self::AgentRef<'_>> {
self.agents.get(id)
}
fn agent_mut(&self, id: AgentId) -> Option<Self::AgentRefMut<'_>> {
self.agents.get_mut(id)
}
}