#![allow(clippy::type_complexity)]
use crate::{
agent::Agent,
interaction::{InteractionError, PositionedAgent, SpaceInteraction},
model::Model,
space::Space,
step_context::DeferredAction,
store::AgentStore,
types::{AgentId, Time},
};
use rand::RngCore;
use std::cell::{Ref, RefMut};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use tracing::{debug, trace};
#[derive(Debug, Clone)]
pub struct Event {
pub time: f64,
pub agent_id: AgentId,
pub event_idx: usize,
sequence: u64,
}
impl<S, A, Store, Props, R> EventQueueModel<S, A, Store, Props, R>
where
A: PositionedAgent,
S: SpaceInteraction<A>,
Store: AgentStore<A>,
R: RngCore,
{
pub fn insert_positioned_agent(&mut self, agent: A) -> Result<(), InteractionError<S::Error>> {
let id = agent.id();
if self.agents.contains(id) {
return Err(InteractionError::DuplicateId(id));
}
self.space
.add_agent(&agent)
.map_err(InteractionError::Space)?;
self.agents.insert(agent);
if id > self.max_id {
self.max_id = id;
}
Ok(())
}
pub fn remove_positioned_agent(
&mut self,
id: AgentId,
) -> Result<Option<A>, InteractionError<S::Error>> {
let Some(agent_ref) = self.agents.get(id) else {
return Ok(None);
};
self.space
.remove_agent(&*agent_ref)
.map_err(InteractionError::Space)?;
drop(agent_ref);
Ok(self.agents.remove(id))
}
pub fn move_positioned_agent(
&mut self,
id: AgentId,
new_position: A::Position,
) -> Result<(), InteractionError<S::Error>> {
let mut agent_ref = self
.agents
.get_mut(id)
.ok_or(InteractionError::AgentNotFound(id))?;
let old_position = agent_ref.position().clone();
self.space
.remove_agent(&*agent_ref)
.map_err(InteractionError::Space)?;
agent_ref.set_position(new_position);
if let Err(source) = self.space.add_agent(&*agent_ref) {
agent_ref.set_position(old_position);
if let Err(rollback) = self.space.add_agent(&*agent_ref) {
return Err(InteractionError::RollbackFailed {
operation: "move_positioned_agent",
source,
rollback,
});
}
return Err(InteractionError::Space(source));
}
Ok(())
}
pub fn validate_space_index(&self) -> Result<(), InteractionError<S::Error>> {
for id in self.agents.iter_ids() {
let Some(agent) = self.agents.get(id) else {
continue;
};
let matches = self
.space
.nearby_ids(agent.position(), 0)
.into_iter()
.filter(|candidate| *candidate == id)
.count();
match matches {
0 => return Err(InteractionError::SpaceIndexMissing(id)),
1 => {}
_ => return Err(InteractionError::SpaceIndexDuplicate(id)),
}
}
Ok(())
}
pub fn step_event_spatial(&mut self) -> Result<bool, InteractionError<S::Error>> {
let timed = match self.queue.pop() {
Some(te) => te,
None => {
trace!("step_event_spatial: queue empty");
return Ok(false);
}
};
let event = timed.0;
self.time = event.time;
if !self.agents.contains(event.agent_id) {
trace!(
agent_id = event.agent_id,
time = event.time,
"skipping event for removed agent"
);
return Ok(true);
}
if event.event_idx < self.actions.len() {
let action = self.actions[event.event_idx];
let Some(mut agent_ref) = self.agents.get_mut(event.agent_id) else {
return Ok(true);
};
let mut rng = self.rng.borrow_mut();
let mut deferred: Vec<DeferredAction<A>> = Vec::new();
{
let mut ctx = EventContext {
space: &mut self.space,
properties: &mut self.properties,
rng: &mut *rng,
queue: &mut self.queue,
sequence: &mut self.sequence,
time: self.time,
deferred: &mut deferred,
};
action(&mut *agent_ref, &mut ctx);
}
drop(agent_ref);
drop(rng);
self.apply_deferred_actions_spatial(deferred)?;
}
Ok(true)
}
pub fn step_until_spatial(&mut self, t_end: f64) -> Result<(), InteractionError<S::Error>> {
loop {
match self.queue.peek() {
Some(te) if te.0.time <= t_end => {}
_ => {
self.time = t_end;
debug!(
time = t_end,
queue_len = self.queue.len(),
"step_until_spatial reached boundary"
);
return Ok(());
}
}
self.step_event_spatial()?;
}
}
pub fn run_events_spatial(&mut self, n: usize) -> Result<(), InteractionError<S::Error>> {
for _ in 0..n {
if !self.step_event_spatial()? {
break;
}
}
Ok(())
}
fn apply_deferred_actions_spatial(
&mut self,
deferred: Vec<DeferredAction<A>>,
) -> Result<(), InteractionError<S::Error>> {
for action in deferred {
match action {
DeferredAction::RemoveAgent(id) => {
self.remove_positioned_agent(id)?;
}
DeferredAction::InsertAgent(agent) => {
self.insert_positioned_agent(agent)?;
}
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub(crate) struct TimedEvent(Event);
impl PartialEq for TimedEvent {
fn eq(&self, other: &Self) -> bool {
self.0.time == other.0.time && self.0.sequence == other.0.sequence
}
}
impl Eq for TimedEvent {}
impl PartialOrd for TimedEvent {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TimedEvent {
fn cmp(&self, other: &Self) -> Ordering {
other
.0
.time
.partial_cmp(&self.0.time)
.unwrap_or(Ordering::Equal)
.then_with(|| other.0.sequence.cmp(&self.0.sequence))
}
}
pub struct EventContext<'a, S, A, Props, R>
where
A: Agent,
{
pub(crate) space: &'a mut S,
pub(crate) properties: &'a mut Props,
pub(crate) rng: &'a mut R,
pub(crate) queue: &'a mut BinaryHeap<TimedEvent>,
pub(crate) sequence: &'a mut u64,
pub(crate) time: f64,
pub(crate) deferred: &'a mut Vec<DeferredAction<A>>,
}
impl<'a, S, A, Props, R> EventContext<'a, S, A, Props, R>
where
A: Agent,
{
pub fn space(&self) -> &S {
self.space
}
pub fn space_mut(&mut self) -> &mut S {
self.space
}
pub fn properties(&self) -> &Props {
self.properties
}
pub fn properties_mut(&mut self) -> &mut Props {
self.properties
}
pub fn rng(&mut self) -> &mut R {
self.rng
}
pub fn time(&self) -> f64 {
self.time
}
pub fn add_event(&mut self, agent_id: AgentId, event_idx: usize, dt: f64) {
assert!(
dt.is_finite() && dt >= 0.0,
"event dt must be finite and non-negative, got {dt}"
);
*self.sequence += 1;
let event = Event {
time: self.time + dt,
agent_id,
event_idx,
sequence: *self.sequence,
};
self.queue.push(TimedEvent(event));
}
pub fn defer_remove_agent(&mut self, id: AgentId) {
self.deferred.push(DeferredAction::RemoveAgent(id));
}
pub fn defer_insert_agent(&mut self, agent: A) {
self.deferred.push(DeferredAction::InsertAgent(agent));
}
}
pub struct EventQueueModel<S, A, Store, Props, R>
where
A: Agent,
S: Space,
Store: AgentStore<A>,
R: RngCore,
{
pub(crate) agents: Store,
pub(crate) space: S,
pub(crate) properties: Props,
pub(crate) rng: std::cell::RefCell<R>,
pub(crate) time: f64,
pub(crate) max_id: AgentId,
pub(crate) queue: BinaryHeap<TimedEvent>,
pub(crate) sequence: u64,
pub(crate) actions: Vec<fn(&mut A, &mut EventContext<'_, S, A, Props, R>)>,
pub(crate) _agent: std::marker::PhantomData<A>,
}
impl<S, A, Store, Props, R> EventQueueModel<S, A, Store, Props, R>
where
A: Agent,
S: Space,
Store: AgentStore<A>,
R: RngCore,
{
pub fn new(
agents: Store,
space: S,
properties: Props,
rng: R,
actions: Vec<fn(&mut A, &mut EventContext<'_, S, A, Props, R>)>,
) -> Self {
let max_id = agents.iter_ids().into_iter().max().unwrap_or(0);
Self {
agents,
space,
properties,
rng: std::cell::RefCell::new(rng),
time: 0.0,
max_id,
queue: BinaryHeap::new(),
sequence: 0,
actions,
_agent: std::marker::PhantomData,
}
}
pub fn time_f64(&self) -> f64 {
self.time
}
pub fn rng_mut(&self) -> std::cell::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.get(id).is_some() {
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 add_event(&mut self, agent_id: AgentId, event_idx: usize, dt: f64) {
assert!(
dt.is_finite() && dt >= 0.0,
"event dt must be finite and non-negative, got {dt}"
);
self.sequence += 1;
let event = Event {
time: self.time + dt,
agent_id,
event_idx,
sequence: self.sequence,
};
self.queue.push(TimedEvent(event));
}
pub fn queue_len(&self) -> usize {
self.queue.len()
}
pub fn queue_is_empty(&self) -> bool {
self.queue.is_empty()
}
pub fn peek_time(&self) -> Option<f64> {
self.queue.peek().map(|te| te.0.time)
}
pub fn step_event(&mut self) -> bool {
let timed = match self.queue.pop() {
Some(te) => te,
None => {
trace!("step_event: queue empty");
return false;
}
};
let event = timed.0;
self.time = event.time;
if !self.agents.contains(event.agent_id) {
trace!(
agent_id = event.agent_id,
time = event.time,
"skipping event for removed agent"
);
return true;
}
if event.event_idx < self.actions.len() {
let action = self.actions[event.event_idx];
let Some(mut agent_ref) = self.agents.get_mut(event.agent_id) else {
return true;
};
let mut rng = self.rng.borrow_mut();
let mut deferred: Vec<DeferredAction<A>> = Vec::new();
{
let mut ctx = EventContext {
space: &mut self.space,
properties: &mut self.properties,
rng: &mut *rng,
queue: &mut self.queue,
sequence: &mut self.sequence,
time: self.time,
deferred: &mut deferred,
};
action(&mut *agent_ref, &mut ctx);
}
drop(agent_ref);
drop(rng);
for action in deferred {
match action {
DeferredAction::RemoveAgent(id) => {
self.remove_agent(id);
}
DeferredAction::InsertAgent(agent) => {
let _ = self.insert_agent(agent);
}
}
}
}
true
}
pub fn step_until(&mut self, t_end: f64) {
loop {
match self.queue.peek() {
Some(te) if te.0.time <= t_end => {}
_ => {
self.time = t_end;
debug!(
time = t_end,
queue_len = self.queue.len(),
"step_until reached boundary"
);
return;
}
}
self.step_event();
}
}
pub fn run_events(&mut self, n: usize) {
for _ in 0..n {
if !self.step_event() {
break;
}
}
}
}
impl<S, A, Store, Props, R> Model for EventQueueModel<S, A, Store, Props, R>
where
A: Agent,
S: Space,
Store: AgentStore<A>,
R: 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 {
Time::Continuous(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)
}
}