de_sim/examples/jupyter_examples_for_talk/1. DE-Sim tutorial.ipynb
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<!-- :Author: Arthur Goldberg <Arthur.Goldberg@mssm.edu> -->\n",
"<!-- :Date: 2020-07-13 -->\n",
"<!-- :Copyright: 2020, Karr Lab -->\n",
"<!-- :License: MIT -->\n",
"# DE-Sim tutorial\n",
"\n",
"DE-Sim is an open-source, object-oriented, discrete-event simulation (OO DES) tool implemented in Python.\n",
"DE-Sim makes it easy to build and simulate discrete-event models.\n",
"This page introduces the basic concepts of discrete-event modeling and teaches you how to build and simulate discrete-event models with DE-Sim. \n",
"\n",
"## Installation\n",
"Use `pip` to install `de_sim`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: de_sim in /root/.wc/wc_sandbox/packages/de_sim (0.0.7)\n",
"Requirement already satisfied: configobj in /usr/local/lib/python3.7/site-packages (from de_sim) (5.0.6)\n",
"Requirement already satisfied: logging2 in /usr/local/lib/python3.7/site-packages (from de_sim) (0.1.2)\n",
"Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/site-packages (from de_sim) (3.1.2)\n",
"Requirement already satisfied: numpy in /usr/local/lib/python3.7/site-packages (from de_sim) (1.18.1)\n",
"Requirement already satisfied: progressbar2>=3.39 in /usr/local/lib/python3.7/site-packages (from de_sim) (3.47.0)\n",
"Requirement already satisfied: pympler in /usr/local/lib/python3.7/site-packages (from de_sim) (0.8)\n",
"Requirement already satisfied: setuptools in /usr/local/lib/python3.7/site-packages (from de_sim) (44.0.0)\n",
"Requirement already satisfied: wc_utils[git]>=0.0.16 in /root/.wc/wc_sandbox/packages/wc_utils (from de_sim) (0.0.21)\n",
"Requirement already satisfied: six in /usr/local/lib/python3.7/site-packages/six-1.12.0-py3.7.egg (from configobj->de_sim) (1.12.0)\n",
"Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/site-packages (from matplotlib->de_sim) (1.1.0)\n",
"Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/site-packages (from matplotlib->de_sim) (2.8.1)\n",
"Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/site-packages (from matplotlib->de_sim) (2.4.6)\n",
"Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/site-packages (from matplotlib->de_sim) (0.10.0)\n",
"Requirement already satisfied: python-utils>=2.3.0 in /usr/local/lib/python3.7/site-packages (from progressbar2>=3.39->de_sim) (2.3.0)\n",
"Requirement already satisfied: abduct in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (2.0.1)\n",
"Requirement already satisfied: attrdict in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (2.0.1)\n",
"Requirement already satisfied: dataclasses in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (0.6)\n",
"Requirement already satisfied: diskcache in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (4.1.0)\n",
"Requirement already satisfied: humanfriendly in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (4.18)\n",
"Requirement already satisfied: mendeleev in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (0.5.1)\n",
"Requirement already satisfied: natsort in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (6.2.0)\n",
"Requirement already satisfied: objsize in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (0.3.2)\n",
"Requirement already satisfied: openpyxl<=3.0.1,>=2.6.1 in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (3.0.1)\n",
"Requirement already satisfied: pint>=0.10 in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (0.10)\n",
"Requirement already satisfied: pronto>=1 in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (1.1.3)\n",
"Requirement already satisfied: pyexcel>=0.5.9.1 in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (0.5.15)\n",
"Requirement already satisfied: pyexcel_io>=0.5.9.1 in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (0.5.20)\n",
"Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (5.3)\n",
"Requirement already satisfied: qualname in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (0.1.0)\n",
"Requirement already satisfied: requests in /usr/local/lib/python3.7/site-packages/requests-2.22.0-py3.7.egg (from wc_utils[git]>=0.0.16->de_sim) (2.22.0)\n",
"Requirement already satisfied: xlsxwriter in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (1.2.7)\n",
"Requirement already satisfied: gitpython in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (3.0.5)\n",
"Requirement already satisfied: pygithub in /usr/local/lib/python3.7/site-packages (from wc_utils[git]>=0.0.16->de_sim) (1.45)\n",
"Requirement already satisfied: contextlib2 in /usr/local/lib/python3.7/site-packages (from abduct->wc_utils[git]>=0.0.16->de_sim) (0.6.0.post1)\n",
"Requirement already satisfied: sqlalchemy in /usr/local/lib/python3.7/site-packages (from mendeleev->wc_utils[git]>=0.0.16->de_sim) (1.3.12)\n",
"Requirement already satisfied: pandas in /usr/local/lib/python3.7/site-packages (from mendeleev->wc_utils[git]>=0.0.16->de_sim) (0.25.3)\n",
"Requirement already satisfied: pyfiglet in /usr/local/lib/python3.7/site-packages (from mendeleev->wc_utils[git]>=0.0.16->de_sim) (0.8.post1)\n",
"Requirement already satisfied: colorama in /usr/local/lib/python3.7/site-packages (from mendeleev->wc_utils[git]>=0.0.16->de_sim) (0.4.3)\n",
"Requirement already satisfied: et-xmlfile in /usr/local/lib/python3.7/site-packages (from openpyxl<=3.0.1,>=2.6.1->wc_utils[git]>=0.0.16->de_sim) (1.0.1)\n",
"Requirement already satisfied: jdcal in /usr/local/lib/python3.7/site-packages (from openpyxl<=3.0.1,>=2.6.1->wc_utils[git]>=0.0.16->de_sim) (1.4.1)\n",
"Requirement already satisfied: networkx~=2.3 in /usr/local/lib/python3.7/site-packages (from pronto>=1->wc_utils[git]>=0.0.16->de_sim) (2.4)\n",
"Requirement already satisfied: contexter~=0.1.4 in /usr/local/lib/python3.7/site-packages (from pronto>=1->wc_utils[git]>=0.0.16->de_sim) (0.1.4)\n",
"Requirement already satisfied: frozendict~=1.2 in /usr/local/lib/python3.7/site-packages (from pronto>=1->wc_utils[git]>=0.0.16->de_sim) (1.2)\n",
"Requirement already satisfied: fastobo~=0.6.0 in /usr/local/lib/python3.7/site-packages (from pronto>=1->wc_utils[git]>=0.0.16->de_sim) (0.6.1)\n",
"Requirement already satisfied: nanoset~=0.1.2; platform_python_implementation == \"CPython\" in /usr/local/lib/python3.7/site-packages (from pronto>=1->wc_utils[git]>=0.0.16->de_sim) (0.1.3)\n",
"Requirement already satisfied: chardet~=3.0 in /usr/local/lib/python3.7/site-packages/chardet-3.0.4-py3.7.egg (from pronto>=1->wc_utils[git]>=0.0.16->de_sim) (3.0.4)\n",
"Requirement already satisfied: texttable>=0.8.2 in /usr/local/lib/python3.7/site-packages (from pyexcel>=0.5.9.1->wc_utils[git]>=0.0.16->de_sim) (1.6.2)\n",
"Requirement already satisfied: lml>=0.0.4 in /usr/local/lib/python3.7/site-packages (from pyexcel>=0.5.9.1->wc_utils[git]>=0.0.16->de_sim) (0.0.9)\n",
"Requirement already satisfied: idna<2.9,>=2.5 in /usr/local/lib/python3.7/site-packages/idna-2.8-py3.7.egg (from requests->wc_utils[git]>=0.0.16->de_sim) (2.8)\n",
"Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/site-packages (from requests->wc_utils[git]>=0.0.16->de_sim) (1.24.2)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/site-packages/certifi-2019.9.11-py3.7.egg (from requests->wc_utils[git]>=0.0.16->de_sim) (2019.9.11)\n",
"Requirement already satisfied: gitdb2>=2.0.0 in /usr/local/lib/python3.7/site-packages (from gitpython->wc_utils[git]>=0.0.16->de_sim) (2.0.6)\n",
"Requirement already satisfied: deprecated in /usr/local/lib/python3.7/site-packages (from pygithub->wc_utils[git]>=0.0.16->de_sim) (1.2.7)\n",
"Requirement already satisfied: pyjwt in /usr/local/lib/python3.7/site-packages (from pygithub->wc_utils[git]>=0.0.16->de_sim) (1.7.1)\n",
"Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/site-packages (from pandas->mendeleev->wc_utils[git]>=0.0.16->de_sim) (2019.3)\n",
"Requirement already satisfied: decorator>=4.3.0 in /usr/local/lib/python3.7/site-packages (from networkx~=2.3->pronto>=1->wc_utils[git]>=0.0.16->de_sim) (4.4.1)\n",
"Requirement already satisfied: smmap2>=2.0.0 in /usr/local/lib/python3.7/site-packages (from gitdb2>=2.0.0->gitpython->wc_utils[git]>=0.0.16->de_sim) (2.0.5)\n",
"Requirement already satisfied: wrapt<2,>=1.10 in /usr/local/lib/python3.7/site-packages (from deprecated->pygithub->wc_utils[git]>=0.0.16->de_sim) (1.11.2)\n",
"\u001b[31mERROR: Error while checking for conflicts. Please file an issue on pip's issue tracker: https://github.com/pypa/pip/issues/new\n",
"Traceback (most recent call last):\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 3021, in _dep_map\n",
" return self.__dep_map\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 2815, in __getattr__\n",
" raise AttributeError(attr)\n",
"AttributeError: _DistInfoDistribution__dep_map\n",
"\n",
"During handling of the above exception, another exception occurred:\n",
"\n",
"Traceback (most recent call last):\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 3012, in _parsed_pkg_info\n",
" return self._pkg_info\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 2815, in __getattr__\n",
" raise AttributeError(attr)\n",
"AttributeError: _pkg_info\n",
"\n",
"During handling of the above exception, another exception occurred:\n",
"\n",
"Traceback (most recent call last):\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_internal/commands/install.py\", line 535, in _determine_conflicts\n",
" return check_install_conflicts(to_install)\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_internal/operations/check.py\", line 108, in check_install_conflicts\n",
" package_set, _ = create_package_set_from_installed()\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_internal/operations/check.py\", line 50, in create_package_set_from_installed\n",
" package_set[name] = PackageDetails(dist.version, dist.requires())\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 2736, in requires\n",
" dm = self._dep_map\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 3023, in _dep_map\n",
" self.__dep_map = self._compute_dependencies()\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 3032, in _compute_dependencies\n",
" for req in self._parsed_pkg_info.get_all('Requires-Dist') or []:\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 3014, in _parsed_pkg_info\n",
" metadata = self.get_metadata(self.PKG_INFO)\n",
" File \"/usr/local/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py\", line 1895, in get_metadata\n",
" raise KeyError(\"No metadata except PKG-INFO is available\")\n",
"KeyError: 'No metadata except PKG-INFO is available'\u001b[0m\n"
]
}
],
"source": [
"!pip install de_sim"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![gray_line](gray_horiz_line.svg)\n",
"\n",
"## DE-Sim model of a one-dimensional random walk\n",
"\n",
"<font size=\"4\">Three steps: define an event message class; define a simulation object class; and build and run a simulation.</font>\n",
"\n",
"### 1: Create an event message class by subclassing [`EventMessage`](https://docs.karrlab.org/de_sim/master/source/de_sim.html#de_sim.event_message.EventMessage).\n",
"\n",
"<font size=\"4\">Each DE-Sim event contains an event message that provides data to the simulation object which executes the event.\n",
"The random walk model sends event messages that contain the value of a random step.</font>"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import de_sim\n",
"\n",
"class RandomStepMessage(de_sim.EventMessage):\n",
" \"An event message class that stores the value of a random walk step\"\n",
" step_value: float"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![gray_line](gray_horiz_line.svg)\n",
"\n",
"### 2: Subclass `SimulationObject` to define a simulation object class\n",
"\n",
"<font size=\"4\">\n",
"Simulation objects are like threads: a simulation's scheduler decides when to execute them, and their execution is suspended when they have no work to do.\n",
"But a DES scheduler schedules simulation objects to ensure that events occur in simulation time order. Precisely, the fundamental invariant of discrete-event simulation:\n",
"<br>\n",
"<br>\n",
"1. All events in a simulation are executed in non-decreasing time order.\n",
"\n",
"By guaranteeing this behavior, the DE-Sim scheduler ensures that causality relationships between events are respected.\n",
"\n",
"This invariant has two consequences:\n",
"\n",
"1. All synchronization between simulation objects is controlled by the simulation times of events.\n",
"2. Each simulation object executes its events in non-decreasing time order.\n",
"\n",
"The Python classes that generate and handle simulation events are simulation object classes, subclasses of `SimulationObject` which uses a custom class creation method that gives special meaning to certain methods and attributes.\n",
"\n",
"Below, we define a simulation object class that models a random walk which randomly selects the time delay between steps, and illustrates all key features of `SimulationObject`.\n",
"</font>"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"class RandomWalkSimulationObject(de_sim.SimulationObject):\n",
" \" A 1D random walk model, with random delays between steps \"\n",
"\n",
" def __init__(self, name):\n",
" super().__init__(name)\n",
"\n",
" def init_before_run(self):\n",
" \" Initialize before a simulation run; called by the simulator \"\n",
" self.position = 0\n",
" self.history = {'times': [0],\n",
" 'positions': [0]}\n",
" self.schedule_next_step()\n",
"\n",
" def schedule_next_step(self):\n",
" \" Schedule the next event, which is a step \"\n",
" # A step moves -1 or +1 with equal probability\n",
" step_value = random.choice([-1, +1])\n",
" # The time between steps is 1 or 2, with equal probability\n",
" delay = random.choice([1, 2])\n",
" # Schedule an event `delay` in the future for this object\n",
" # The event contains a `RandomStepMessage` with `step_value=step_value`\n",
" self.send_event(delay, self, RandomStepMessage(step_value))\n",
"\n",
" def handle_step_event(self, event):\n",
" \" Handle a step event \"\n",
" # Update the position and history\n",
" self.position += event.message.step_value\n",
" self.history['times'].append(self.time)\n",
" self.history['positions'].append(self.position)\n",
" self.schedule_next_step()\n",
"\n",
" # `event_handlers` contains pairs that map each event message class\n",
" # received by this simulation object to the method that handles\n",
" # the event message class\n",
" event_handlers = [(RandomStepMessage, handle_step_event)]\n",
"\n",
" # messages_sent registers all message types sent by this object\n",
" messages_sent = [RandomStepMessage]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font size=\"4\">\n",
"DE-Sim simulation objects employ special methods and attributes:\n",
"<br>\n",
"\n",
"* Special `SimulationObject` methods:\n",
" 1. **`init_before_run`** (optional): immediately before a simulation run, the simulator calls each simulation object’s `init_before_run` method. In this method simulation objects can send initial events and perform other initializations.\n",
" 2. **`send_event`**: `send_event(delay, receiving_object, event_message)` schedules an event to occur `delay` time units in the future at simulation object `receiving_object`. `event_message` must be an [`EventMessage`](https://docs.karrlab.org/de_sim/master/source/de_sim.html#de_sim.event_message.EventMessage) instance. An event can be scheduled for any simulation object in a simulation.\n",
"The event will be executed at its scheduled simulation time by an event handler in the simulation object `receiving_object`.\n",
"The `event` parameter in the handler will be the scheduled event, which contains `event_message` in its `message` attribute.\n",
" 3. **event handlers**: Event handlers have the signature `event_handler(self, event)`, where `event` is a simulation event. A subclass of `SimulationObject` must define at least one event handler, as illustrated by `handle_step_event` above.\n",
"<br>\n",
"<br>\n",
"* Special `SimulationObject` attributes:\n",
" 1. **`event_handlers`**: a simulation object can receive arbitrarily many types of event messages, and implement arbitrarily many event handlers. The attribute `event_handlers` contains an iterator over pairs that map each event message class received to the event handler which handles the event message class.\n",
" 2. **`time`**: `time` is a read-only attribute that always equals the current simulation time.\n",
"</font>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![gray_line](gray_horiz_line.svg)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3: Execute a simulation by creating and initializing a [`Simulator`](https://docs.karrlab.org/de_sim/master/source/de_sim.html#de_sim.simulator.Simulator), and running the simulation.\n",
"\n",
"<font size=\"4\">\n",
"The `Simulator` class simulates models.\n",
"Its `add_object` method adds a simulation object to the simulator.\n",
"Each object in a simulation must have a unique `name`.\n",
"The `initialize` method, which calls each simulation object’s `init_before_run` method, must be called before a simulation starts.\n",
"\n",
"At least one simulation object in a simulation must schedule an initial event--otherwise the simulation cannot start.\n",
"More generally, a simulation with no events to execute will terminate.\n",
"\n",
"Finally, `run` simulates a model. It takes the maximum time of a simulation run. `run` also takes several optional configuration arguments.\n",
"</font>"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# Create a simulator\n",
"simulator = de_sim.Simulator()\n",
"\n",
"# Create a random walk simulation object and add it to the simulation\n",
"random_walk_sim_obj = RandomWalkSimulationObject('rand_walk')\n",
"simulator.add_object(random_walk_sim_obj)\n",
"\n",
"# Initialize the simulation\n",
"simulator.initialize()\n",
"\n",
"# Run the simulation until time 10\n",
"max_time = 10\n",
"simulator.run(max_time)\n",
"\n",
"# Plot the random walk\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.ticker as plticker\n",
"\n",
"fig, ax = plt.subplots()\n",
"loc = plticker.MultipleLocator(base=1.0)\n",
"ax.yaxis.set_major_locator(loc)\n",
"plt.step(random_walk_sim_obj.history['times'],\n",
" random_walk_sim_obj.history['positions'],\n",
" where='post')\n",
"plt.xlabel('Time')\n",
"plt.ylabel('Position')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font size=\"4\">\n",
"This example runs a simulation for `max_time` time units, and plots the random walk’s trajectory.\n",
"\n",
"This trajectory illustrates two key characteristics of discrete-event models. First, the state changes at discrete times.\n",
"Second, since the state does not change between instantaneous events, the trajectory of any state variable is a step function.\n",
"</font>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![gray_line](gray_horiz_line.svg)\n",
"\n",
"## DE-Sim example with multiple object instances\n",
"\n",
"<font size=\"4\">\n",
"We show an DE-Sim implementation of the parallel hold (PHOLD) model, frequently used to benchmark parallel DES simulators.\n",
"<br>\n",
"<br>\n",
"We illustrate these DE-Sim features:\n",
"\n",
"* Use multiple [`EventMessage`](https://docs.karrlab.org/de_sim/master/source/de_sim.html#de_sim.event_message.EventMessage) types\n",
"* Run multiple instances of a simulation object type\n",
"* Simulation objects scheduling events for each other\n",
"</font>"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"\"\"\" Messages for the PHOLD benchmark for parallel discrete-event simulators \"\"\"\n",
"import random\n",
"\n",
"class MessageSentToSelf(de_sim.EventMessage):\n",
" \"A message that's sent to self\"\n",
"\n",
"class MessageSentToOtherObject(de_sim.EventMessage):\n",
" \"A message that's sent to another PHold simulation object\"\n",
"\n",
"class InitMsg(de_sim.EventMessage):\n",
" \"An initialization message\"\n",
"\n",
"MESSAGE_TYPES = [MessageSentToSelf, MessageSentToOtherObject, InitMsg]"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"class PholdSimulationObject(de_sim.SimulationObject):\n",
" \"\"\" Run a PHOLD simulation \"\"\"\n",
" def __init__(self, name, args):\n",
" self.args = args\n",
" super().__init__(name)\n",
"\n",
" def init_before_run(self):\n",
" self.send_event(random.expovariate(1.0), self, InitMsg())\n",
"\n",
" @staticmethod\n",
" def record_event_header():\n",
" print('\\t'.join(('Sender', 'Send', \"Receivr\",\n",
" 'Event', 'Message type')))\n",
" print('\\t'.join(('', 'time', '', 'time', '')))\n",
" \n",
" def record_event(self, event):\n",
" record_format = '{}\\t{:.2f}\\t{}\\t{:.2f}\\t{}'\n",
" print(record_format.format(event.sending_object.name,\n",
" event.creation_time,\n",
" event.receiving_object.name,\n",
" self.time,\n",
" type(event.message).__name__))\n",
"\n",
" def handle_simulation_event(self, event):\n",
" \"\"\" Handle a simulation event \"\"\"\n",
" # Record this event\n",
" self.record_event(event)\n",
" # Schedule an event\n",
" if random.random() < self.args.frac_self_events or \\\n",
" self.args.num_phold_objects == 1:\n",
" receiver = self\n",
" else:\n",
" # Send the event to another randomly selected object\n",
" obj_index = random.randrange(self.args.num_phold_objects - 1)\n",
" if int(self.name) <= obj_index:\n",
" obj_index += 1\n",
" receiver = self.simulator.simulation_objects[str(obj_index)]\n",
"\n",
" if receiver == self:\n",
" message_type = MessageSentToSelf\n",
" else:\n",
" message_type = MessageSentToOtherObject\n",
" self.send_event(random.expovariate(1.0), receiver, message_type())\n",
"\n",
" event_handlers = [(sim_msg_type, 'handle_simulation_event') \\\n",
" for sim_msg_type in MESSAGE_TYPES]\n",
"\n",
" messages_sent = MESSAGE_TYPES"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font size=\"4\">\n",
"The PHOLD model runs multiple instances of `PholdSimulationObject`.\n",
"\n",
"`create_and_run` creates the objects and adds them to the simulator.\n",
"\n",
"Each `PholdSimulationObject` object is initialized with `args`, an object that defines two attributes used by all objects:\n",
"\n",
"* `args.num_phold_objects`: the number of PHOLD objects running\n",
"* `args.frac_self_events`: the fraction of events sent to self\n",
"\n",
"At time 0, each PHOLD object schedules an `InitMsg` event for itself that occurs after a random exponential time delay with mean = 1.0.\n",
"\n",
"The `handle_simulation_event` method handles all events.\n",
"Each event schedules one more event.\n",
"A random value in [0, 1) is used to decide whether to schedule the event for itself (with probability `args.frac_self_events`) or for another PHOLD object.\n",
"\n",
"If the event is scheduled for another PHOLD object, this gets a reference to the object: \n",
"\n",
" receiver = self.simulator.simulation_objects[str(obj_index)]\n",
"\n",
"The attribute `self.simulator` always references the running simulator, and `self.simulator.simulation_objects` is a dictionary that maps simulation object names to simulation objects.\n",
"</font>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<font size=\"4\">\n",
"Each event is printed by `record_event`.\n",
"It accesses the DE-Sim `Event` object that is passed to all event handlers.\n",
"`de_sim.event.Event` contains five useful fields:\n",
"\n",
"* `sending_object`: the object that created and sent the event\n",
"* `creation_time`: the simulation time when the event was created (a.k.a. its *send time*)\n",
"* `receiving_object`: the object that received the event\n",
"* `event_time`: the simulation time when the event must execute (a.k.a. its *receive time*)\n",
"* `message`: the [`EventMessage`](https://docs.karrlab.org/de_sim/master/source/de_sim.html#de_sim.event_message.EventMessage) carried by the event\n",
"\n",
"However, rather than use the event's `event_time`, `record_event` uses `self.time` to report the simulation time when the event is being executed, as they are always equal.\n",
"</font>\n",
"![gray_line](gray_horiz_line.svg)\n",
"\n",
"### Execute the simulation\n",
"<font size=\"4\">\n",
"Run a short simulation, and print all events:\n",
"</font>\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def create_and_run(args):\n",
"\n",
" # create a simulator\n",
" simulator = de_sim.Simulator()\n",
"\n",
" # create simulation objects, and send each one an initial event message to self\n",
" for obj_id in range(args.num_phold_objects):\n",
" phold_obj = PholdSimulationObject(str(obj_id), args)\n",
" simulator.add_object(phold_obj)\n",
"\n",
" # run the simulation\n",
" simulator.initialize()\n",
" PholdSimulationObject.record_event_header()\n",
" event_num = simulator.simulate(args.max_time).num_events\n",
" print(\"Executed {} events.\\n\".format(event_num))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Sender\tSend\tReceivr\tEvent\tMessage type\n",
"\ttime\t\ttime\t\n",
"5\t0.00\t5\t0.19\tInitMsg\n",
"3\t0.00\t3\t0.37\tInitMsg\n",
"5\t0.19\t3\t0.38\tMessageSentToOtherObject\n",
"1\t0.00\t1\t0.54\tInitMsg\n",
"3\t0.37\t2\t0.59\tMessageSentToOtherObject\n",
"2\t0.00\t2\t0.59\tInitMsg\n",
"2\t0.59\t2\t0.72\tMessageSentToSelf\n",
"2\t0.59\t3\t1.11\tMessageSentToOtherObject\n",
"3\t0.38\t5\t1.16\tMessageSentToOtherObject\n",
"3\t1.11\t3\t1.28\tMessageSentToSelf\n",
"0\t0.00\t0\t1.48\tInitMsg\n",
"2\t0.72\t0\t1.55\tMessageSentToOtherObject\n",
"0\t1.48\t3\t1.80\tMessageSentToOtherObject\n",
"Executed 13 events.\n",
"\n"
]
}
],
"source": [
"from argparse import Namespace\n",
"args = Namespace(max_time=2,\n",
" frac_self_events=0.3,\n",
" num_phold_objects=6)\n",
"create_and_run(args)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": false,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}