from mesa.time import BaseScheduler from queue import Empty from heapq import heappush, heappop import math from .utils import logger from mesa import Agent class When: def __init__(self, time): self._time = float(time) def abs(self, time): return self._time class Delta: def __init__(self, delta): self._delta = delta def abs(self, time): return time + self._delta class TimedActivation(BaseScheduler): """A scheduler which activates each agent when the agent requests. In each activation, each agent will update its 'next_time'. """ def __init__(self, *args, **kwargs): super().__init__(self) self._queue = [] self.next_time = 0 def add(self, agent: Agent): if agent.unique_id not in self._agents: heappush(self._queue, (self.time, agent.unique_id)) super().add(agent) def step(self, until: float =float('inf')) -> None: """ Executes agents in order, one at a time. After each step, an agent will signal when it wants to be scheduled next. """ when = None agent_id = None unsched = [] until = until or float('inf') if not self._queue: self.time = until self.next_time = float('inf') return (when, agent_id) = self._queue[0] if until and when > until: self.time = until self.next_time = when return self.time = when next_time = float("inf") while when == self.time: heappop(self._queue) logger.debug(f'Stepping agent {agent_id}') when = (self._agents[agent_id].step() or Delta(1)).abs(self.time) heappush(self._queue, (when, agent_id)) if when < next_time: next_time = when if not self._queue or self._queue[0][0] > self.time: agent_id = None break else: (when, agent_id) = self._queue[0] if when and when < self.time: raise Exception("Invalid scheduling time") self.next_time = next_time self.steps += 1