- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 1.1k
support for discrete event scheduling #2066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Merged
      
      
    
  
     Merged
                    Changes from 13 commits
      Commits
    
    
            Show all changes
          
          
            50 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      92831dd
              
                first commit
              
              
                quaquel b7761a8
              
                Update wolf_sheep.py
              
              
                quaquel 585766b
              
                time unit check and step scheduling in ABMSimulator
              
              
                quaquel 2d9bce8
              
                ongoing work
              
              
                quaquel 08de4ff
              
                various small tweaks
              
              
                quaquel f2155e5
              
                Epstein Civil Violence as hybrid ABM DES
              
              
                quaquel b7457c5
              
                Update epstein_civil_violence.py
              
              
                quaquel b0226cf
              
                Merge remote-tracking branch 'upstream/main' into devs
              
              
                quaquel cf9efc1
              
                docstrings
              
              
                quaquel 790c1e6
              
                correct handling of canceling events
              
              
                quaquel 2fd8069
              
                Merge remote-tracking branch 'upstream/main' into devs
              
              
                quaquel 9aabc48
              
                docstrings, update to benchmark minor cleaning of public api
              
              
                quaquel 76ebfed
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] efd79df
              
                make benchmarks work
              
              
                quaquel 1513095
              
                Merge remote-tracking branch 'upstream/main' into devs
              
              
                quaquel d555858
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] 6e43ab5
              
                fix for benchmarks
              
              
                quaquel ec30623
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] b267bb0
              
                me being stupid
              
              
                quaquel 53e7ae5
              
                schedule events correctly
              
              
                Corvince 155517b
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] 79a30a4
              
                simplified eventlist
              
              
                Corvince 6c843c9
              
                readded imports
              
              
                Corvince 5ba27d8
              
                remove need for model.setup
              
              
                quaquel f298879
              
                adds a bit more docstring info on eventlist implementation motivation
              
              
                quaquel bae814e
              
                cleanup of fully_grown
              
              
                quaquel 911c919
              
                removal of setup from epstein example
              
              
                quaquel 4e54add
              
                set time to starttime upon reset
              
              
                quaquel 1c86aca
              
                move to zero based indexing for _ids
              
              
                quaquel 2341b79
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] d0611e3
              
                move methods to after init
              
              
                quaquel 287f138
              
                cleanup
              
              
                quaquel 6b03f38
              
                unittests for eventlist.py
              
              
                quaquel 75479d3
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] 36e9c94
              
                typo fixes
              
              
                quaquel 1596a6a
              
                simulator unit tests
              
              
                quaquel f80fd5b
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] a714134
              
                added run_until
              
              
                quaquel ca58a70
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] 4fc4957
              
                doc string update and benchmark fix
              
              
                quaquel 25eb453
              
                Merge remote-tracking branch 'upstream/main' into devs
              
              
                quaquel 0261bc2
              
                Merge branch 'main' into devs
              
              
                tpike3 273245d
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] 046c4be
              
                typo fix
              
              
                quaquel 586a3a9
              
                Merge remote-tracking branch 'upstream/main' into devs
              
              
                quaquel fbd68bc
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] 8e48a29
              
                deprecate time.DiscreteEventScheduler
              
              
                quaquel b7237b8
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] 47e3c3f
              
                remove unit test for time.DiscreteEventScheduler
              
              
                quaquel 4f1b88f
              
                [pre-commit.ci] auto fixes from pre-commit.com hooks
              
              
                pre-commit-ci[bot] File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| from .eventlist import Priority, SimulationEvent | ||
| from .simulator import ABMSimulator, DEVSimulator | ||
|  | ||
| __all__ = ["ABMSimulator", "DEVSimulator", "SimulationEvent", "Priority"] | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| import itertools | ||
| from enum import IntEnum | ||
| from heapq import heapify, heappop, heappush | ||
| from types import MethodType | ||
| from typing import Any, Callable | ||
| from weakref import WeakMethod, ref | ||
|  | ||
|  | ||
| class InstanceCounterMeta(type): | ||
| """Metaclass to make instance counter not share count with descendants | ||
|         
                  quaquel marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| TODO:: can also be used for agents.unique_id | ||
| """ | ||
|  | ||
| def __init__(cls, name, bases, attrs): | ||
| super().__init__(name, bases, attrs) | ||
| cls._ids = itertools.count(1) | ||
|  | ||
|  | ||
| class Priority(IntEnum): | ||
| LOW = 10 | ||
| DEFAULT = 5 | ||
| HIGH = 1 | ||
|  | ||
|  | ||
| class SimulationEvent(metaclass=InstanceCounterMeta): | ||
| """A simulation event | ||
| the callable is wrapped using weakrefs, so there is no need to explicitly cancel event if e.g., an agent | ||
| is removed from the simulation. | ||
| Attributes: | ||
| time (float): The simulation time of the event | ||
| function (Callable): The function to execute for this event | ||
| priority (Priority): The priority of the event | ||
| unique_id (int) the unique identifier of the event | ||
| function_args (list[Any]): Argument for the function | ||
| function_kwargs (Dict[str, Any]): Keyword arguments for the function | ||
| """ | ||
|  | ||
| @property | ||
| def CANCELED(self) -> bool: | ||
| return self._canceled | ||
|  | ||
| def __init__( | ||
| self, | ||
| time: int | float, | ||
| function: Callable, | ||
| priority: Priority = Priority.DEFAULT, | ||
| function_args: list[Any] | None = None, | ||
| function_kwargs: dict[str, Any] | None = None, | ||
| ) -> None: | ||
| super().__init__() | ||
| self.time = time | ||
| self.priority = priority.value | ||
| self._canceled = False | ||
|  | ||
| if isinstance(function, MethodType): | ||
| function = WeakMethod(function) | ||
| else: | ||
| function = ref(function) | ||
|  | ||
| self.fn = function | ||
| self.unique_id = next(self._ids) | ||
| self.function_args = function_args if function_args else [] | ||
| self.function_kwargs = function_kwargs if function_kwargs else {} | ||
|  | ||
| if self.fn is None: | ||
| raise Exception() | ||
|  | ||
| def execute(self): | ||
| """execute this event""" | ||
| fn = self.fn() | ||
| if fn is not None: | ||
| fn(*self.function_args, **self.function_kwargs) | ||
|  | ||
| def cancel(self) -> None: | ||
| """cancel this event""" | ||
| self._canceled = True | ||
| self.fn = None | ||
| self.function_args = [] | ||
| self.function_kwargs = {} | ||
|  | ||
| def __cmp__(self, other): | ||
| if self.time < other.time: | ||
| return -1 | ||
| if self.time > other.time: | ||
| return 1 | ||
|  | ||
| if self.priority > other.priority: | ||
| return -1 | ||
| if self.priority < other.priority: | ||
| return 1 | ||
|  | ||
| if self.unique_id < other.unique_id: | ||
| return -1 | ||
| if self.unique_id > other.unique_id: | ||
| return 1 | ||
|  | ||
| # theoretically this should be impossible unless it is the | ||
| # exact same event | ||
| return 0 | ||
|         
                  quaquel marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
|  | ||
| def _to_tuple(self): | ||
| return self.time, self.priority, self.unique_id, self | ||
|  | ||
|  | ||
| class EventList: | ||
|         
                  quaquel marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| """An event list | ||
| This is a heap queue sorted list of events. Events are allways removed from the left. The events are sorted | ||
|         
                  quaquel marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| based on their time stamp, their priority, and their unique_id, guaranteeing a complete ordering. | ||
| """ | ||
|  | ||
| def __init__(self): | ||
| super().__init__() | ||
| self._event_list: list[tuple] = [] | ||
| heapify(self._event_list) | ||
|  | ||
| def add_event(self, event: SimulationEvent): | ||
| """Add the event to the event list | ||
| Args: | ||
| event (SimulationEvent): The event to be added | ||
| """ | ||
|  | ||
| heappush(self._event_list, event._to_tuple()) | ||
|  | ||
| def peek_ahead(self, n: int = 1) -> list[SimulationEvent]: | ||
| """Look at the first n non-canceled event in the event list | ||
| Args: | ||
| n (int): The number of events to look ahead | ||
| Returns: | ||
| list[SimulationEvent] | ||
| Raises: | ||
| IndexError: If the eventlist is empty | ||
| Notes: | ||
| this method can return a list shorted then n if the number of non-canceled events on the event list | ||
| is less than n. | ||
| """ | ||
| # look n events ahead | ||
| if self.is_empty(): | ||
| raise IndexError("event list is empty") | ||
|  | ||
| peek: list[SimulationEvent] = [] | ||
| for entry in self._event_list: | ||
| sim_event: SimulationEvent = entry[3] | ||
| if not sim_event.CANCELED: | ||
| peek.append(sim_event) | ||
| if len(peek) >= n: | ||
| return peek | ||
| return peek | ||
|  | ||
| def pop(self) -> SimulationEvent: | ||
| """pop the first element from the event list""" | ||
|  | ||
| try: | ||
| while True: | ||
| sim_event = heappop(self._event_list)[3] | ||
| if not sim_event.CANCELED: | ||
| return sim_event | ||
| except IndexError as e: | ||
| raise Exception("event list is empty") from e | ||
|  | ||
| def is_empty(self) -> bool: | ||
| return len(self) == 0 | ||
|         
                  EwoutH marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
|  | ||
| def __contains__(self, event: SimulationEvent) -> bool: | ||
| return event._to_tuple() in self._event_list | ||
|  | ||
| def __len__(self) -> int: | ||
| return len(self._event_list) | ||
|  | ||
| def remove(self, event: SimulationEvent) -> None: | ||
| """remove an event from the event list""" | ||
| # we cannot simply remove items from _eventlist because this breaks | ||
| # heap structure invariant. So, we use a form of lazy deletion. | ||
| # SimEvents have a CANCELED flag that we set to True, while poping and peak_ahead | ||
| # silently ignore canceled events | ||
| event.cancel() | ||
|  | ||
| def clear(self): | ||
| self._event_list.clear() | ||
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.