├── .gitignore ├── .idea ├── .gitignore ├── SimPyKit.iml ├── dictionaries │ └── ahbuss.xml ├── inspectionProfiles │ └── Project_Default.xml ├── libraries │ └── R_User_Library.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── LICENSE ├── README.md ├── __init__.py ├── log.txt ├── setup.py └── simkit ├── __init__.py ├── base.py ├── examples ├── __init__.py ├── arrivalprocess.py ├── basic.py ├── entitycreator.py ├── entityserver.py ├── finitecapacityqueue.py ├── ggkqueue.py ├── mover │ ├── __init__.py │ └── simplemover.py ├── run │ ├── RunArrivalProcess.py │ ├── RunConfidenceInterval.py │ ├── RunConfidenceIntervalSteadyState.py │ ├── RunEntityServer.py │ ├── RunEntityServerMultipleReps.py │ ├── RunGGkQueue.py │ ├── RunMultipleServerQueue.py │ ├── RunServerWithReneges.py │ ├── RunSimpleServer.py │ ├── RunTandemQueueWithBlocking.py │ ├── RunTransferLine.py │ ├── RunTwoCranesBerth.py │ └── __init__.py ├── serverwithreneges.py ├── simpleserver.py ├── transferline.py └── twocranesberth.py ├── quantiles.py ├── rand.py ├── simutil.py ├── stats.py └── test ├── TestAdapter.py ├── TestArrivalProcess.py ├── TestBatchArrivals.py ├── TestCancelled.py ├── TestConfidenceInterval.py ├── TestDiscreteVariate.py ├── TestEntityServer.py ├── TestIndexedStateChangeEvent.py ├── TestIndexedStats.py ├── TestNaNObservations.py ├── TestNanData.py ├── TestPersistent.py ├── TestQuantiles.py ├── TestRandomVariate.py ├── TestRenegingServer.py ├── TestSeedByTime.py ├── TestSimkit.py ├── TestSimkit2.py ├── TestSimpleMover.py ├── TestSimpleServer.py ├── TestSimpleStatsRawData.py ├── TestSimpleStatsTally.py ├── TestStopAtTime.py ├── TestStopOnEvent.py ├── TestStoppedArrivals.py ├── TestTransferLine.py ├── TestTruncatingSimpleStatsTimeVarying.py └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /dist/ 3 | /DESpy.egg-info/ 4 | __pycache__ 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/SimPyKit.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/dictionaries/ahbuss.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/R_User_Library.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Arnold Buss 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DESpy 2 | **DESpy** is a Python implementation of Discrete Event Simulation 3 | (DES) methodology based on Schruben's Event Graphs (see [Simulation Modeling with Event Graphs](http://delivery.acm.org/10.1145/360000/358460/p957-schruben.pdf)). 4 | It also suports the LEGO component framework (see [Building Complex Models with LEGOs](https://www.informs-sim.org/wsc02papers/094.pdf) and [Component Based Simulation Modeling with Simkit](https://www.informs-sim.org/wsc02papers/031.pdf)). 5 | 6 | 7 | ## Installation 8 | 9 | * Python 3.7 or greater installed 10 | * `pip install DESpy` 11 | 12 | ## Running Example Scenarios 13 | The sample components are in the ``examples`` package, with the 14 | sample scenarios in the ``examples.run`` package 15 | * SimpleServer 16 | * FiniteCapacityServer 17 | * EntityServer 18 | * ServerWithReneges 19 | * TransferLine 20 | * Confidence Interval Example: terminating case 21 | * Confidence Interval Example: steady-state case 22 | 23 | ## Defining an Event Graph Component 24 | Each Event Graph component corresponds to a specific element in a DESpy model 25 | 26 | |Event Graph|DESpy| 27 | |-----------|-----| 28 | |Component |Subclass of SimEntityBase| 29 | |Parameter |attribute passed in to`` __init()__``| 30 | |State variable| attribute initialized to ``nan`` or ``[]``| 31 | |Run Event| ``reset()`` method and ``run()`` method| 32 | |Other Events|method of same name, first letter lower-case| 33 | |State transition|assignment to state variable followed by ``notify_state_change()``| 34 | |Schedule event|call ``schedule(, , [])``| 35 | |Cancel event|call ``cancel(, []`` | 36 | 37 | ## Executing Model 38 | 39 | |Action|EventList call| 40 | |------|--------------| 41 | |Run verbose mode|``EventList.verbose=True``| 42 | |Run for xxx simtime units|``EventList.stop_at_time(xxx)``| 43 | |Run for n Foo events|``EventList.stop_on_event(n, 'Foo')`` 44 | |Prepare for running model|``EventList.reset()``| 45 | |Run Model|``EventList.start_simulation()``| 46 | 47 | ## Example: SimpleServer 48 | 49 | The `SimpleServer` component is the most basic implementation of a multiple 50 | server queue. Its state representation consists of integers representing the 51 | number of customers in queue (`number_in_queue`) and the number of available 52 | servers (`number_available_servers`). It is not a stand-alone model, but must be 53 | set up to "listen" to another component that periodically schedules an `Arrival` event. 54 | The most basic such component is the `ArrivalProcess`. 55 | 56 | ``` 57 | # Instantiate ArrivalProcess component with interarrival times Exponential(1.7) 58 | interarrival_time_generator = RandomVariate.instance('Exponential', mean=1.7) 59 | arrival_process = ArrivalProcess(interarrival_time_generator) 60 | 61 | # Instantiate SimpleServer component with 2 servers and service times Gamma(1.7, 1.8) 62 | number_servers = 2; 63 | service_time_generator = RandomVariate.instance('Gamma', alpha=1.7, beta=1.8) 64 | simple_server = SimpleServer(number_servers, service_time_generator) 65 | 66 | # Add the SimpleServer instance to the ArrivalProcess instance as a 67 | # SimEventListener 68 | arrival_process.add_sim_event_listener(simple_server) 69 | 70 | # These statistics objects will collect the time-varying number_in_queue 71 | # and number_available_servers of the SimpleServer instance 72 | number_in_queue_stat = SimpleStatsTimeVarying('number_in_queue') 73 | number_available_servers_stat = SimpleStatsTimeVarying('number_available_servers') 74 | 75 | # Add the statistics objects as StateChangeListeners 76 | simple_server.add_state_change_listener(number_in_queue_stat) 77 | simple_server.add_state_change_listener(number_available_servers_stat) 78 | 79 | # Execute the model for 100,000 time units 80 | stopTime = 100000; 81 | EventList.stop_at_time(stopTime) 82 | 83 | # Initialize the EventList and put all Run events on the EventList 84 | EventList.reset() 85 | 86 | # Execute the simulation 87 | EventList.start_simulation() 88 | ``` 89 | 90 | ## Running Multiple Replications 91 | 92 | The most straightforward way to estimate confidence intervals is by running 93 | multiple independent replications. 94 | To run multiple replications, wrap the ``reset()`` and ``start_simulation()`` 95 | calls in a ``for`` loop. Collecting statistics, however, needs to be different 96 | for the "inner" statistics objects and "outer" ones. 97 | 98 | Statistics objects are ``StateChangeListeners`` that implement the ``stateChange()`` method 99 | to update their counters. The two main types are "tally" and "time-varying." 100 | They are typically used in tow different ways: "inner" and "outer." 101 | 102 | An **Inner** statistics object uses state trajectories from a single replication to 103 | produce a value - typically a mean - for that replication. Since simulation data 104 | are tyically auto-correlated, estimates of the variance can be extremely biased. 105 | Thus, the usual expression for a confidence interval cannot be applied. 106 | It is important to `clear()` each inner statistics object 107 | before each replication in order to ensure independence between replications. 108 | 109 | An **Outer** statistics object is typically used to collect data from the inner 110 | statistics objects. After each replication, a value from an inner statistics 111 | object (often the mean) is passed to the outer object. 112 | 113 | In this manner, regardless of the value passed, the outer statistics object 114 | can then (with sufficient quantity of replications) produce a confidence interval for the 115 | value in question (with all the "usual" assumptions about the central limit theorem). 116 | 117 | ### Parameters vs State Variables 118 | 119 | ## Parameters 120 | 121 | Parameters are variables in a component that do not change during a given replication of 122 | the simulation. These are inputs to the simulation and, as such, must be 123 | passed in via the `__init()__` method. Parameters may be scalars, such as the 124 | total number of servers, or RandomVariates which generate different values 125 | on each call, such as the service time generator. In such cases, while the generated values may be different, 126 | the distribution itself remains the same. 127 | 128 | ## State Variables 129 | 130 | State variables _do_ change within a given replication of a model. The full 131 | definition of a state variable must include its initial value, since that 132 | is set in the `reset()` method of each component. Only event methods are 133 | permitted to change the value of a state variable, since events are identified with state transitions. Thus, the value of a given 134 | state at any point in simulated time is completely determinded by its 135 | initial value and the subsequent state transitions. 136 | 137 | Every state transition must be accompanied by a `notify_state_change()` call, which 138 | notifies StateChangeListsners that the given state has changed. This allows 139 | components to be written to the dynamics of the model only and not be concerned with 140 | collecting statistics, since that can be done with the appropriate statistical 141 | objects, which are StateChanegListsners. 142 | 143 | ### Defining Events 144 | 145 | An Event is defined in a subclass of `SimEntityBase` as simply an ordinary method. Within an event method, 146 | there should only be (in order): 147 | 148 | 1. State transitions (followed by state change notifications) 149 | 2. Canceling events (if needed) by a call to `self.cancel()` 150 | 3. Scheduling events (if needed) by a call to `self.sechedule()` 151 | 152 | ### RandomVariate Instantiation 153 | 154 | By convention, a `RandomVariate` class specifies its parameters as named ones in the 155 | constructor. 156 | 157 | There are several ways to instantiate a `RandomVariate`. 158 | 159 | * Direct instantiation, e.g. `Exponential(mean=2.3)` 160 | * Using the `RandomVariate` factory method with keywords: `RandomVariate.instance('Exponential', mean=2.3)` 161 | * Using the RandomVariate factory method with a dictionary (using the `params` keyword): 162 | ``` 163 | params_map={mean:2.3} 164 | RandomVariate.instance('Exponential', params=params_map) 165 | ``` 166 | 167 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahbuss/DESpy/ce149ae5581017fc3df85971da9f73f6156fb300/__init__.py -------------------------------------------------------------------------------- /log.txt: -------------------------------------------------------------------------------- 1 | 0.0.1 2 | - Basic functionality 3 | - Tested on simple "Pinger" class 4 | - No random variate (yet) 5 | - Event arguments not tested 6 | - Priority enum with int values 7 | - Using None as default when scheduling SimEvents 8 | 9 | 0.0.2 10 | - Some standard random variates 11 | - Uses keyword arguments or keyword map for instantiation 12 | - Event arguments implemented 13 | - SimEventListener implemented 14 | - StateChangeListener implemented 15 | - Simple Stats Tally & Time-varying 16 | - Multiple Simple Stats Tally & Time-varying 17 | - A number of standard Event Graph models implemented in simkit.examples 18 | 19 | 0.0.3 20 | - Include simkit.examples in distribution 21 | 22 | 0.1.4 23 | - More examples -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open('README.md') as file: 4 | long_description = file.read() 5 | 6 | setup( 7 | name='DESpy', 8 | version='0.1.15', 9 | packages=['simkit', 'simkit.examples'], 10 | url='https://github.com/ahbuss/DESpy', 11 | license='Apache 2.0 License', 12 | author='Arnold Buss', 13 | author_email='abuss@nps.edu', 14 | description='Support for DES Modeling using Event Graphs', 15 | long_description=long_description, 16 | long_description_content_type='text/markdown', 17 | ) 18 | -------------------------------------------------------------------------------- /simkit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahbuss/DESpy/ce149ae5581017fc3df85971da9f73f6156fb300/simkit/__init__.py -------------------------------------------------------------------------------- /simkit/base.py: -------------------------------------------------------------------------------- 1 | from heapq import heappush 2 | from heapq import heappop 3 | from enum import IntEnum 4 | from enum import unique 5 | from abc import ABC 6 | from abc import abstractmethod 7 | from math import nan 8 | from math import inf 9 | from inspect import signature 10 | 11 | 12 | __author__ = "Arnold Buss" 13 | 14 | @unique 15 | class Priority(IntEnum): 16 | LOWEST = 1E10 17 | LOWER = 1000 18 | LOW = 100 19 | DEFAULT = 0 20 | HIGH = -100 21 | HIGHER = -1000 22 | HIGHEST = -1E10 23 | 24 | class SimEvent: 25 | NEXT_ID = 0 26 | 27 | def __init__(self, source, event_name, scheduled_time, *arguments, **kwds): 28 | """ 29 | 30 | :param source: Object that scheduled this SimEvent 31 | :param event_name: Name of event method 32 | :param scheduled_time: Time this SimEvent is scheduled to occur; should be \u2265 EventList.simtime 33 | :param arguments: optional arguments to be passed to the scheduled event 34 | :param kwds: optional keywords - currently if a Priority other tha DEFAULT is desired use the priority value; 35 | e.g. priority=Priority.HIGH 36 | """ 37 | if kwds.keys().__contains__('priority'): 38 | self.priority = kwds.get('priority') 39 | else: 40 | self.priority = Priority.DEFAULT 41 | self.source = source 42 | self.event_name = event_name 43 | self.scheduled_time = scheduled_time 44 | self.arguments = arguments 45 | self.id = SimEvent.NEXT_ID 46 | self.cancelled = False 47 | SimEvent.NEXT_ID += 1 48 | 49 | def copy(self): 50 | """:return copy of this SimEvent""" 51 | return SimEvent(self.source, self.event_name, self.scheduled_time, *self.arguments, priority=self.priority) 52 | 53 | def __repr__(self): 54 | if self.arguments == (): 55 | argstr = "" 56 | elif self.arguments.__len__() == 1: 57 | argstr = '(' + str(self.arguments[0]) + ')' 58 | else: 59 | argstr = str(self.arguments) 60 | return '{0:,.4f}'.format(self.scheduled_time) + ' ' + self.event_name + ' ' + argstr + \ 61 | ' <' + str(self.source) + '>' 62 | 63 | def __eq__(self, other): 64 | return (self.scheduled_time, self.priority) == (other.scheduled_time, other.priority) 65 | 66 | def __ne__(self, other): 67 | return (self.scheduled_time, self.priority) != (other.scheduled_time, other.priority) 68 | 69 | def __lt__(self, other): 70 | return (self.scheduled_time, self.priority) < (other.scheduled_time, other.priority) 71 | 72 | def __gt__(self, other): 73 | return other.__lt__(self) 74 | 75 | def __hash__(self): 76 | return hash((self.event_name, self.scheduled_time, self.arguments, self.priority)) 77 | 78 | class EventList: 79 | event_list = [] 80 | sim_entities = [] 81 | ignore_on_dump = [] 82 | simtime = 0.0 83 | stop_time = 0.0 84 | verbose = False 85 | running = False 86 | current_event = None 87 | event_counts = {} 88 | stopper = None 89 | is_stop_on_event = False 90 | stop_event_name = None 91 | stop_event_number = inf 92 | stop_event_count = nan 93 | 94 | @staticmethod 95 | def stop_at_time(time): 96 | """:param time: Given time to stop simulation and clear event list """ 97 | EventList.stop_time = time 98 | if EventList.sim_entities.__contains__(EventList.stopper): 99 | EventList.sim_entities.remove(EventList.stopper) 100 | EventList.stopper = Stopper() 101 | 102 | @staticmethod 103 | def stop_on_event(number_events, stop_event_name, *args): 104 | """ 105 | 106 | :param number_events: Stop after this many stop_event_name events have occurred 107 | :param stop_event_name: Given event to stop after 108 | :param args: unused 109 | """ 110 | EventList.is_stop_on_event = True 111 | EventList.stop_event_number = number_events 112 | EventList.stop_event_name = stop_event_name 113 | if EventList.sim_entities.__contains__(EventList.stopper): 114 | EventList.sim_entities.remove(EventList.stopper) 115 | 116 | @staticmethod 117 | def reset(): 118 | """ 119 | Sets simtime to 0.0 120 | Clears event_list 121 | Calls reset() on all (persistent) SimEntityBase's 122 | Schedules run event on all that have them 123 | Clears event counts if is_stop_on_event 124 | """ 125 | EventList.simtime = 0.0 126 | EventList.event_list.clear() 127 | for sim_entity in EventList.sim_entities: 128 | if sim_entity.persistent: 129 | sim_entity.reset() 130 | if hasattr(sim_entity, 'run'): 131 | sim_entity.schedule('run', 0.0, priority=Priority.HIGHEST) 132 | else: 133 | EventList.sim_entities.remove(sim_entity) 134 | if EventList.is_stop_on_event: 135 | EventList.stop_event_count = 0 136 | EventList.event_counts.clear() 137 | EventList.event_counts[EventList.stop_event_name] = 0 138 | 139 | @staticmethod 140 | def schedule(sim_event): 141 | heappush(EventList.event_list, sim_event) 142 | 143 | @staticmethod 144 | def cancel(event_name, *args): 145 | for sim_event in EventList.event_list: 146 | if sim_event.cancelled: 147 | continue 148 | if event_name == sim_event.event_name and args == sim_event.arguments: 149 | sim_event.cancelled = True 150 | break 151 | 152 | @staticmethod 153 | def dump(): 154 | dump_string = "" 155 | dump_string += '*** Event List ***\n' 156 | queue_copy = EventList.event_list.copy() 157 | queue_copy.sort() 158 | if not queue_copy: 159 | dump_string += ' \n' 160 | for event in queue_copy: 161 | if not event.cancelled and not EventList.ignore_on_dump.__contains__(event.event_name): 162 | dump_string += str(event) + '\n' 163 | return dump_string 164 | 165 | @staticmethod 166 | def start_simulation(): 167 | EventList.running = True 168 | if EventList.verbose: 169 | print('Starting Simulation...') 170 | print(EventList.dump()) 171 | while EventList.running and EventList.event_list: 172 | EventList.current_event = heappop(EventList.event_list) 173 | if EventList.current_event.cancelled: 174 | continue 175 | EventList.simtime = EventList.current_event.scheduled_time 176 | EventList.current_event.source.process_sim_event(EventList.current_event) 177 | if not EventList.event_counts.keys().__contains__(EventList.current_event.event_name): 178 | EventList.event_counts[EventList.current_event.event_name] = 1 179 | else: 180 | EventList.event_counts[EventList.current_event.event_name] += 1 181 | if EventList.verbose and not EventList.ignore_on_dump.__contains__(EventList.current_event.event_name): 182 | print('CurrentEvent: ' + str(EventList.current_event) + ' [' + str(EventList.event_counts[EventList.current_event.event_name]) + ']') 183 | print(EventList.dump()) 184 | if EventList.is_stop_on_event: 185 | if EventList.event_counts[EventList.stop_event_name] == EventList.stop_event_number: 186 | EventList.stop_simulation() 187 | 188 | @staticmethod 189 | def stop_simulation(): 190 | EventList.running = False 191 | 192 | @staticmethod 193 | def cold_reset(): 194 | EventList.reset() 195 | EventList.sim_entities.clear() 196 | EventList.event_counts.clear() 197 | SimEntityBase.NEXT_ID = 1 198 | if not EventList.is_stop_on_event and EventList.stop_time > 0.0: 199 | EventList.stop_at_time(EventList.stop_time) 200 | 201 | class Adapter: 202 | 203 | def __init__(self, source_event_name, target_event_name): 204 | self.source_event_name = source_event_name 205 | self.target_event_name = target_event_name 206 | self.source =None 207 | self.target = None 208 | 209 | def connect(self, source, target): 210 | self.source = source 211 | self.target = target 212 | self.source.add_sim_event_listener(self) 213 | 214 | def disconnect(self, source, target): 215 | self.source = None 216 | self.target = None 217 | 218 | def process_sim_event(self, simEvent): 219 | if simEvent.event_name == self.source_event_name: 220 | new_event = simEvent.copy() 221 | new_event.event_name = self.target_event_name 222 | self.target.process_sim_event(new_event) 223 | 224 | 225 | class SimEntityBase: 226 | 227 | NEXT_ID = 1 228 | 229 | def __init__(self, **args): 230 | self.event_listeners = [] 231 | self.state_change_listeners = [] 232 | self.name = type(self).__name__ 233 | self.id = SimEntityBase.NEXT_ID 234 | self.persistent = True 235 | EventList.sim_entities.append(self) 236 | SimEntityBase.NEXT_ID += 1 237 | 238 | def __repr__(self): 239 | return self.name + '.' + str(self.id) 240 | 241 | def reset(self): 242 | pass 243 | 244 | def process_sim_event(self, sim_event): 245 | method_name = sim_event.event_name 246 | if hasattr(self, method_name): 247 | method = getattr(self, method_name) 248 | method_sig = signature(method) 249 | if (sim_event.arguments.__len__() > 0 and sim_event.arguments.__len__() == len(method_sig.parameters)): 250 | method(*sim_event.arguments) 251 | else: 252 | method() 253 | if sim_event.source == self: 254 | self.notify_sim_event_listeners(sim_event) 255 | 256 | def add_sim_event_listener(self, sim_event_listener): 257 | if not sim_event_listener in self.event_listeners: 258 | self.event_listeners.append(sim_event_listener) 259 | 260 | def remove_sim_event_listener(self, sim_event_listener): 261 | self.event_listeners.remove(sim_event_listener) 262 | 263 | def add_state_change_listener(self, state_change_listener): 264 | if hasattr(state_change_listener, 'state_change'): 265 | if not state_change_listener in self.state_change_listeners: 266 | self.state_change_listeners.append(state_change_listener) 267 | 268 | def remove_state_change_listener(self, state_change_listener): 269 | self.state_change_listeners.remove(state_change_listener) 270 | 271 | def notify_state_change(self, state_name, state_value): 272 | state_change_event = StateChangeEvent(self, state_name, state_value) 273 | for state_change_listener in self.state_change_listeners: 274 | if hasattr(state_change_listener, 'state_change'): 275 | state_change_listener.state_change(state_change_event) 276 | 277 | def notify_indexed_state_change(self, index, state_name, state_value): 278 | state_change_event = IndexedStateChangeEvent(index, self, state_name, state_value) 279 | for state_change_listener in self.state_change_listeners: 280 | if hasattr(state_change_listener, 'state_change'): 281 | state_change_listener.state_change(state_change_event) 282 | 283 | def notify_sim_event_listeners(self, sim_event): 284 | if sim_event.event_name != 'run': 285 | for listener in self.event_listeners: 286 | listener.process_sim_event(sim_event) 287 | 288 | def schedule(self, event_name, delay, *args, **kwds): 289 | if delay < 0.0: 290 | raise ValueError('delay must be \u2265 0.0: {delay:.3f}'.format(delay=delay)) 291 | event = SimEvent(self, event_name, EventList.simtime + delay, *args, **kwds) 292 | EventList.schedule(event) 293 | return event; 294 | 295 | def cancel(self, event_name, *arguments): 296 | EventList.cancel(event_name, *arguments) 297 | 298 | def describe(self): 299 | description = self.name 300 | for property in self.__dict__.keys(): 301 | if not ['name', 'state_change_listeners', 'event_listeners'].__contains__(property): 302 | value = self.__dict__.get(property) 303 | description += '\n\t' + property + " = " + str(value) 304 | return description 305 | 306 | class Stopper(SimEntityBase): 307 | def __init__(self): 308 | SimEntityBase.__init__(self) 309 | self.stop_event = None 310 | self.id = 0 311 | SimEntityBase.NEXT_ID -= 1; 312 | 313 | def run(self): 314 | self.stop_event = self.schedule('stop', EventList.stop_time, priority=Priority.LOWEST) 315 | 316 | def stop(self): 317 | EventList.event_list.clear() 318 | 319 | class StateChangeEvent: 320 | def __init__(self, source, state_name, state_value): 321 | self.source = source 322 | self.name = state_name 323 | self.value = state_value 324 | 325 | def __repr__(self): 326 | return str(self.source) + '> ' + str(self.name) + ': ' + str(self.value) 327 | 328 | class IndexedStateChangeEvent(StateChangeEvent): 329 | def __init__(self, index, source, state_name, state_value): 330 | StateChangeEvent.__init__(self, source, state_name, state_value) 331 | self.index = index 332 | 333 | def __repr__(self): 334 | return '{source}> {name}[{index:d}]: {value}'.\ 335 | format(source=self.source, name=self.name, index=self.index, value=str(self.value)) 336 | 337 | class StateChangeListener(ABC): 338 | 339 | @abstractmethod 340 | def state_change(self, state_change_event): 341 | pass 342 | 343 | class Entity: 344 | NEXT_ID = 1 345 | 346 | def __init__(self, name='Entity'): 347 | self.name=name 348 | self.id = Entity.NEXT_ID 349 | self.creation_time = EventList.simtime 350 | self.time_stamp = EventList.simtime 351 | self.stamp_time() 352 | Entity.NEXT_ID += 1 353 | 354 | def stamp_time(self): 355 | self.time_stamp = EventList.simtime 356 | 357 | def elapsed_time(self): 358 | return EventList.simtime - self.time_stamp 359 | 360 | def age(self): 361 | return EventList.simtime - self.creation_time 362 | 363 | def __eq__(self, other): 364 | if other == None: 365 | return False 366 | else: 367 | return self.id == other.id 368 | 369 | def __ne__(self, other): 370 | return not self.__eq__(other) 371 | 372 | def __lt__(self, other): 373 | if self.time_stamp < other.time_stamp: 374 | return True 375 | elif self.time_stamp > other.time_stamp: 376 | return False 377 | else: 378 | return self.id < other.id 379 | 380 | def __gt__(self, other): 381 | return not self.lt(self, other) 382 | 383 | def __hash__(self): 384 | return hash((self.name, self.creation_time, self.id)) 385 | 386 | def __repr__(self): 387 | return self.name + '.' + str(self.id) + ' [' + str(round(self.creation_time, 4)) + ', ' + str(round(self.time_stamp, 4)) + ']' 388 | -------------------------------------------------------------------------------- /simkit/examples/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahbuss/DESpy/ce149ae5581017fc3df85971da9f73f6156fb300/simkit/examples/__init__.py -------------------------------------------------------------------------------- /simkit/examples/arrivalprocess.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase # (1) 2 | from simkit.base import Entity 3 | from math import nan 4 | 5 | class ArrivalProcess(SimEntityBase): # (2) 6 | 7 | def __init__(self, interarrival_time_generator): # (3) 8 | SimEntityBase.__init__(self) 9 | self.interarrival_time_generator=interarrival_time_generator 10 | self.number_arrivals = nan 11 | 12 | def reset(self): # (4) 13 | SimEntityBase.reset(self) 14 | self.number_arrivals = 0 15 | 16 | def run(self): # (5) 17 | self.notify_state_change("number_arrivals", self.number_arrivals) 18 | 19 | self.schedule('arrival', self.interarrival_time_generator.generate()) 20 | 21 | def arrival(self): # (6) 22 | self.number_arrivals += 1 23 | self.notify_state_change("number_arrivals", self.number_arrivals) 24 | 25 | self.schedule('arrival', self.interarrival_time_generator.generate()) 26 | 27 | class EntityCreator(ArrivalProcess): 28 | 29 | def __int__(self, generator): 30 | ArrivalProcess.__init__(self, generator) 31 | 32 | def arrival(self): 33 | ArrivalProcess.arrival(self) 34 | self.schedule('entity_arrival', 0.0, Entity()) 35 | 36 | class BatchArrivalProcess(ArrivalProcess): 37 | 38 | def __init__(self, interarrival_time_generator, batchGenerator): 39 | ArrivalProcess.__init__(self, interarrival_time_generator) 40 | self.batchGenerator = batchGenerator 41 | self.totalIndividualArrivals = nan 42 | self.numberInBatch = nan 43 | 44 | def reset(self): 45 | ArrivalProcess.reset(self) 46 | self.totalIndividualArrivals = 0 47 | self.numberInBatch = nan 48 | 49 | def arrival(self): 50 | ArrivalProcess.arrival(self) 51 | 52 | self.numberInBatch = round(self.batchGenerator.generate()) 53 | self.notify_state_change('number_in_batch', self.numberInBatch) 54 | 55 | for i in range(self.numberInBatch): 56 | self.schedule('arrival1', 0.0) 57 | self.totalIndividualArrivals += self.numberInBatch 58 | self.notify_state_change('total_individual_arrivals', self.totalIndividualArrivals) 59 | 60 | class StoppedArrivalProcess(ArrivalProcess): 61 | 62 | def __init__(self, interarrival_time_generator, stop_time): 63 | ArrivalProcess.__init__(self, interarrival_time_generator) 64 | self.stop_time = stop_time 65 | 66 | def run(self): 67 | ArrivalProcess.run(self) 68 | self.schedule('stop_arrivals', self.stop_time) 69 | 70 | def stop_arrivals(self): 71 | self.cancel('arrival') -------------------------------------------------------------------------------- /simkit/examples/basic.py: -------------------------------------------------------------------------------- 1 | from math import nan 2 | from random import Random 3 | from heapq import heappush 4 | from heapq import heappop 5 | 6 | from simkit.base import SimEntityBase 7 | from simkit.base import EventList 8 | from simkit.base import Priority 9 | from simkit.base import Entity 10 | 11 | 12 | rng = Random(12345) 13 | 14 | class SingleResourceModel(SimEntityBase): 15 | 16 | def __init__(self, interarrival_time_generator, service_time_generator): 17 | SimEntityBase.__init__(self) 18 | self.interarrival_time_generator = interarrival_time_generator 19 | self.service_time_generator = service_time_generator 20 | self.number_in_queue = nan 21 | self.number_available_resources = nan 22 | 23 | def reset(self): 24 | SimEntityBase.reset(self) 25 | self.number_in_queue = 0 26 | self.number_available_resources = 1 27 | 28 | def run(self): 29 | self.notify_state_change('number_in_queue', self.number_in_queue) 30 | self.notify_state_change('number_available_resources', self.number_available_resources) 31 | 32 | self.schedule('enter', 0.0) 33 | 34 | def enter(self): 35 | self.number_in_queue += 1 36 | self.notify_state_change('number_in_queue', self.number_in_queue) 37 | 38 | if (self.number_available_resources > 0): 39 | self.schedule('start', 0.0) 40 | 41 | self.schedule('enter', self.interarrival_time_generator.generate()) 42 | 43 | def start(self): 44 | self.number_in_queue -= 1 45 | self.notify_state_change('number_in_queue', self.number_in_queue) 46 | 47 | self.number_available_resources -= 1 48 | self.notify_state_change('number_available_resources', self.number_available_resources) 49 | 50 | self.schedule('leave', self.service_time_generator.generate()) 51 | 52 | def leave(self): 53 | self.number_available_resources += 1 54 | self.notify_state_change('number_available_resources', self.number_available_resources) 55 | 56 | if self.number_in_queue > 0: 57 | self.schedule('start', 0.0) 58 | 59 | class ClosingTimes(SimEntityBase): 60 | def __init(self,interarrival_time_generator, service_time_generator, closing_time): 61 | SimEntityBase.__init__(self) 62 | self.interarrival_time_generator = interarrival_time_generator 63 | self.service_time_generator = service_time_generator 64 | self.closing_time = closing_time 65 | 66 | def reset(self): 67 | SimEntityBase.reset(self) 68 | self.number_in_queue = 0 69 | self.number_available_resources = 1 70 | 71 | def run(self): 72 | self.notify_state_change('number_in_queue', self.number_in_queue) 73 | self.notify_state_change('number_available_resources', self.number_available_resources) 74 | 75 | self.schedule('arrive', 0.0) 76 | 77 | def arrive(self): 78 | 79 | if EventList.simtime < self.closing_time: 80 | self.schedule('enter', 0.0) 81 | 82 | self.schedule('arrive', self.interarrival_time_generator.generate()) 83 | 84 | def enter(self): 85 | self.number_in_queue += 1 86 | self.notify_state_change('number_in_queue', self.number_in_queue) 87 | 88 | if (self.number_available_resources == 1): 89 | self.schedule('start', 0.0) 90 | 91 | 92 | def start(self): 93 | self.number_in_queue -= 1 94 | self.notify_state_change('number_in_queue', self.number_in_queue) 95 | 96 | self.number_available_resources -= 1 97 | self.notify_state_change('number_available_resources', self.number_available_resources) 98 | 99 | self.schedule('leave', self.service_time_generator.generate()) 100 | 101 | def leave(self): 102 | self.number_available_resources += 1 103 | self.notify_state_change('number_available_resources', self.number_available_resources) 104 | 105 | if self.number_in_queue > 0: 106 | self.schedule('start', 0.0) 107 | 108 | class MultipleResourceModel(SimEntityBase): 109 | 110 | def __init__(self, interarrival_time_generator, service_time_generator, number_resources): 111 | SimEntityBase.__init__(self) 112 | self.interarrival_time_generator = interarrival_time_generator 113 | self.service_time_generator = service_time_generator 114 | self.number_resources = number_resources 115 | self.number_in_queue = nan 116 | self.number_available_resources = nan 117 | 118 | def reset(self): 119 | SimEntityBase.reset(self) 120 | self.number_in_queue = 0 121 | self.number_available_resources = self.number_resources 122 | 123 | def run(self): 124 | self.notify_state_change('number_in_queue', self.number_in_queue) 125 | self.notify_state_change('number_available_resources', self.number_available_resources) 126 | 127 | self.schedule('enter', 0.0) 128 | 129 | def enter(self): 130 | self.number_in_queue += 1 131 | self.notify_state_change('number_in_queue', self.number_in_queue) 132 | 133 | if self.number_available_resources > 0: 134 | self.schedule('start', 0.0) 135 | 136 | self.schedule('enter', self.interarrival_time_generator.generate()) 137 | 138 | def start(self): 139 | self.number_in_queue -= 1 140 | self.notify_state_change('number_in_queue', self.number_in_queue) 141 | 142 | self.number_available_resources -= 1 143 | self.notify_state_change('number_available_resources', self.number_available_resources) 144 | 145 | self.schedule('leave', self.service_time_generator.generate()) 146 | 147 | def leave(self): 148 | self.number_available_resources += 1 149 | self.notify_state_change('number_available_resources', self.number_available_resources) 150 | 151 | if self.number_in_queue > 0: 152 | self.schedule('start', 0.0) 153 | 154 | class BatchedServiceModel(SimEntityBase): 155 | 156 | def __init__(self, interarrival_time_generator, service_time_generator, number_resources, batch_size): 157 | SimEntityBase.__init__(self) 158 | self.interarrival_time_generator = interarrival_time_generator 159 | self.service_time_generator = service_time_generator 160 | self.number_resources = number_resources 161 | self.batch_size = batch_size 162 | self.number_in_queue = nan 163 | self.number_available_resources = nan 164 | 165 | def reset(self): 166 | SimEntityBase.reset(self) 167 | self.number_in_queue = 0 168 | self.number_available_resources = self.number_resources 169 | 170 | def run(self): 171 | self.notify_state_change('number_in_queue', self.number_in_queue) 172 | self.notify_state_change('number_available_resources', self.number_available_resources) 173 | 174 | self.schedule('enter', 0.0) 175 | 176 | def enter(self): 177 | self.number_in_queue += 1 178 | self.notify_state_change('number_in_queue', self.number_in_queue) 179 | 180 | if self.number_available_resources > 0 & self.number_in_queue >= self.batch_size: 181 | self.schedule('start', 0.0) 182 | 183 | self.schedule('enter', self.interarrival_time_generator.generate()) 184 | 185 | def start(self): 186 | self.number_in_queue -= 1 187 | self.notify_state_change('number_in_queue', self.number_in_queue) 188 | 189 | self.number_available_resources -= 1 190 | self.notify_state_change('number_available_resources', self.number_available_resources) 191 | 192 | self.schedule('leave', self.service_time_generator.generate()) 193 | 194 | def leave(self): 195 | self.number_available_resources += 1 196 | self.notify_state_change('number_available_resources', self.number_available_resources) 197 | 198 | if self.number_in_queue > self.batch_size: 199 | self.schedule('start', 0.0) 200 | 201 | class ReworkModel(SimEntityBase): 202 | 203 | def __init__(self, interarrival_time_generator, service_time_generator, prob_needs_rework): 204 | SimEntityBase.__init__(self) 205 | self.interarrival_time_generator = interarrival_time_generator 206 | self.service_time_generator = service_time_generator 207 | self.prob_needs_rework = prob_needs_rework 208 | self.number_in_queue = nan 209 | self.number_available_resources = nan 210 | 211 | def reset(self): 212 | SimEntityBase.reset(self) 213 | self.number_in_queue = 0 214 | self.number_available_resources = 1 215 | 216 | def run(self): 217 | self.notify_state_change('number_in_queue', self.number_in_queue) 218 | self.notify_state_change('number_available_resources', self.number_available_resources) 219 | 220 | self.schedule('enter', 0.0) 221 | 222 | def enter(self): 223 | self.number_in_queue += 1 224 | self.notify_state_change('number_in_queue', self.number_in_queue) 225 | 226 | if self.number_available_resources > 0: 227 | self.schedule('start', 0.0) 228 | 229 | self.schedule('enter', self.interarrival_time_generator.generate()) 230 | 231 | def start(self): 232 | self.number_in_queue -= 1 233 | self.notify_state_change('number_in_queue', self.number_in_queue) 234 | 235 | self.number_available_resources -= 1 236 | self.notify_state_change('number_available_resources', self.number_available_resources) 237 | 238 | self.schedule('leave', self.service_time_generator.generate()) 239 | 240 | def leave(self): 241 | self.number_available_resources += 1 242 | self.notify_state_change('number_available_resources', self.number_available_resources) 243 | 244 | rw = rng.random() 245 | 246 | if self.number_in_queue > 0 & rw > self.prob_needs_rework: 247 | self.schedule('start', 0.0) 248 | 249 | if rw <= self.prob_needs_rework: 250 | self.schedule('rework', 0.0) 251 | 252 | def rework(self): 253 | self.number_in_queue += 1; 254 | self.notify_state_change('number_in_queue', self.number_in_queue) 255 | 256 | if self.number_available_resources > 0: 257 | self.schedule('start', 0.0) 258 | 259 | class TandemQueueWithBlocking(SimEntityBase): 260 | 261 | def __init__(self, interarrival_time_generator, number_server1, number_server2, \ 262 | service_time1_generator, service_time2_generator, buffer_size): 263 | SimEntityBase.__init__(self) 264 | # Parameters 265 | self.interarrival_time_generator = interarrival_time_generator 266 | self.number_server1 = number_server1 267 | self.number_server2 = number_server2 268 | self.service_time1_generator = service_time1_generator 269 | self.service_time2_generator = service_time2_generator 270 | self.buffer_size = buffer_size 271 | # State variables 272 | self.number_in_queue1 = nan 273 | self.number_in_queue2 = nan 274 | self.number_available_server1 = nan 275 | self.number_available_server2 = nan 276 | self.number_blocked = nan 277 | 278 | def reset(self): 279 | SimEntityBase.reset(self) 280 | self.number_in_queue1 = 0 281 | self.number_in_queue2 = 0 282 | self.number_available_server1 = self.number_server1 283 | self.number_available_server2 = self.number_server2 284 | self.number_blocked = 0 285 | 286 | def run(self): 287 | self.notify_state_change('number_in_queue1', self.number_in_queue1) 288 | self.notify_state_change('number_in_queue2', self.number_in_queue2) 289 | self.notify_state_change('number_available_server1', self.number_available_server1) 290 | self.notify_state_change('number_available_server2', self.number_available_server2) 291 | self.notify_state_change('number_blocked', self.number_blocked) 292 | 293 | self.schedule('enter1', 0.0) 294 | 295 | def enter1(self): 296 | self.number_in_queue1 += 1 297 | self.notify_state_change('number_in_queue1', self.number_in_queue1) 298 | 299 | if self.number_available_server1 > 0: 300 | self.schedule('start1', 0.0) 301 | 302 | self.schedule('enter1', self.interarrival_time_generator.generate()) 303 | 304 | def start1(self): 305 | self.number_in_queue1 -= 1 306 | self.notify_state_change('number_in_queue1', self.number_in_queue1) 307 | 308 | self.number_available_server1 -= 1 309 | self.notify_state_change('number_available_server1', self.number_available_server1) 310 | 311 | self.schedule('leave1', self.service_time1_generator.generate()) 312 | 313 | def leave1(self): 314 | self.number_blocked += 1 315 | self.notify_state_change('number_blocked', self.number_blocked) 316 | 317 | if self.number_in_queue2 < self.buffer_size: 318 | self.schedule('enter2', 0.0) 319 | 320 | def enter2(self): 321 | self.number_available_server1 += 1 322 | self.notify_state_change('number_available_server1', self.number_available_server1) 323 | 324 | self.number_blocked -= 1 325 | self.notify_state_change('number_blocked', self.number_blocked) 326 | 327 | self.number_in_queue2 += 1; 328 | self.notify_state_change('number_in_queue2', self.number_in_queue2) 329 | 330 | if self.number_available_server2 > 0: 331 | self.schedule('start2', 0.0) 332 | 333 | if self.number_in_queue1 > 0: 334 | self.schedule('start1', 0.0) 335 | 336 | def start2(self): 337 | self.number_in_queue2 -= 1; 338 | self.notify_state_change('number_in_queue2', self.number_in_queue2) 339 | 340 | self.number_available_server2 -= 1 341 | self.notify_state_change('number_available_server2', self.number_available_server2) 342 | 343 | self.schedule('leave2', self.service_time2_generator.generate()) 344 | 345 | if self.number_blocked > 0: 346 | self.schedule('enter2', 0.0) 347 | 348 | def leave2(self): 349 | self.number_available_server2 += 1 350 | self.notify_state_change('number_available_server2', self.number_available_server2) 351 | 352 | if self.number_in_queue2 > 0: 353 | self.schedule('start2', 0.0) 354 | 355 | class MultipleServerQueue(SimEntityBase): 356 | 357 | def __init__(self, interarrival_time_generator, number_servers, service_time_generator): 358 | SimEntityBase.__init__(self) 359 | self.interarrival_time_generator = interarrival_time_generator 360 | self.number_servers = number_servers 361 | self.service_time_generator = service_time_generator 362 | self.number_arrivals = nan 363 | self.number_available_servers = nan 364 | self.queue = [] 365 | self.number_in_queue = nan 366 | self.delay_in_queue = nan 367 | self.time_in_system = nan 368 | 369 | def reset(self): 370 | SimEntityBase.reset(self) 371 | self.number_arrivals = 0 372 | self.number_available_servers = self.number_servers 373 | self.queue = [] 374 | self.number_in_queue = 0 375 | 376 | def run(self): 377 | self.notify_state_change('number_arrivals', self.number_arrivals) 378 | self.notify_state_change('number_available_servers', self.number_available_servers) 379 | self.notify_state_change('queue', self.queue) 380 | self.notify_state_change('number_in_queue', self.number_in_queue) 381 | 382 | self.schedule('enter', 0.0) 383 | 384 | def enter(self): 385 | customer = Entity() 386 | customer.stamp_time() 387 | heappush(self.queue, customer); 388 | self.notify_state_change('queue', self.queue) 389 | 390 | self.number_in_queue = len(self.queue) 391 | self.notify_state_change('number_in_queue', self.number_in_queue) 392 | 393 | self.number_arrivals += 1; 394 | self.notify_state_change('number_arrivals', self.number_arrivals) 395 | 396 | if self.number_available_servers > 0: 397 | self.schedule('start', 0.0) 398 | 399 | self.schedule('enter', self.interarrival_time_generator.generate()) 400 | 401 | def start(self): 402 | customer = heappop(self.queue) 403 | self.notify_state_change('queue', self.queue) 404 | 405 | self.number_in_queue = len(self.queue) 406 | self.notify_state_change('number_in_queue', self.number_in_queue) 407 | 408 | self.delay_in_queue = customer.elapsed_time() 409 | self.notify_state_change('delay_in_queue', self.delay_in_queue) 410 | 411 | self.number_available_servers -= 1 412 | self.notify_state_change('number_available_servers', self.number_available_servers) 413 | 414 | self.schedule('leave', self.service_time_generator.generate(), customer) 415 | 416 | def leave(self, customer): 417 | self.time_in_system = customer.elapsed_time() 418 | self.notify_state_change('time_in_system', self.time_in_system) 419 | 420 | self.number_available_servers += 1 421 | self.notify_state_change('number_available_servers', self.number_available_servers) 422 | 423 | if len(self.queue) > 0: 424 | self.schedule('start', 0.0) -------------------------------------------------------------------------------- /simkit/examples/entitycreator.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import Entity 3 | from simkit.base import Priority 4 | 5 | class EntityCreator(SimEntityBase): 6 | 7 | def __init__(self, interarrival_time_generator): 8 | SimEntityBase.__init__(self) 9 | self.interarrival_time_generator = interarrival_time_generator 10 | 11 | def run(self): 12 | self.schedule('generate', self.interarrival_time_generator.generate()) 13 | 14 | def generate(self): 15 | self.schedule('generate', self.interarrival_time_generator.generate()) 16 | self.schedule('arrival', 0.0, Entity()) 17 | 18 | def doArrival(self, entity): 19 | pass 20 | -------------------------------------------------------------------------------- /simkit/examples/entityserver.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import Priority 3 | from math import nan 4 | from heapq import heappush 5 | from heapq import heappop 6 | 7 | class EntityServer(SimEntityBase): 8 | 9 | def __init__(self, number_servers, service_time_generator): 10 | SimEntityBase.__init__(self) 11 | self.number_servers = number_servers 12 | self.service_time_generator = service_time_generator 13 | self.number_available_servers = nan 14 | self.queue = [] 15 | self.delay_in_queue = nan 16 | self.time_in_system = nan 17 | 18 | @property 19 | def number_servers(self): 20 | return self.__number_servers 21 | 22 | @number_servers.setter 23 | def number_servers(self, numberServers): 24 | if numberServers <= 0: 25 | raise ValueError('number_servers must be > 0: ' + str(numberServers)) 26 | self.__number_servers = numberServers 27 | 28 | def reset(self): 29 | self.number_available_servers = self.number_servers 30 | self.queue.clear() 31 | self.delay_in_queue = nan 32 | self.time_in_system = nan 33 | 34 | def run(self): 35 | self.notify_state_change('number_available_servers', self.number_available_servers) 36 | self.notify_state_change('queue', self.queue) 37 | 38 | def arrival(self, entity): 39 | entity.stamp_time() 40 | heappush(self.queue, entity) 41 | self.notify_state_change('queue', self.queue) 42 | 43 | if (self.number_available_servers > 0): 44 | self.schedule('start_service', 0.0, priority=Priority.HIGH) 45 | 46 | def start_service(self): 47 | entity = heappop(self.queue) 48 | self.notify_state_change('queue', self.queue) 49 | 50 | self.delay_in_queue = entity.elapsed_time() 51 | self.notify_state_change('delay_in_queue', self.delay_in_queue) 52 | 53 | self.number_available_servers -= 1 54 | self.notify_state_change('number_available_servers', self.number_available_servers) 55 | 56 | self.schedule('end_service', self.service_time_generator.generate(), entity) 57 | 58 | def end_service(self, entity): 59 | self.number_available_servers += 1 60 | self.notify_state_change('number_available_servers', self.number_available_servers) 61 | 62 | self.time_in_system = entity.elapsed_time() 63 | self.notify_state_change('time_in_system', self.time_in_system) 64 | 65 | if self.queue.__len__() > 0: 66 | self.schedule('start_service', 0.0, priority=Priority.HIGH) -------------------------------------------------------------------------------- /simkit/examples/finitecapacityqueue.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import Priority 3 | from math import nan 4 | 5 | class FiniteCapacityQueue(SimEntityBase): 6 | 7 | def __init__(self, total_number_servers, service_time_generator, queue_capacity): 8 | SimEntityBase.__init__(self) 9 | self.total_number_servers = total_number_servers 10 | self.service_time_generator = service_time_generator 11 | self.queue_capacity = queue_capacity 12 | self.number_in_queue = nan 13 | self.number_available_servers = nan 14 | self.number_balks = nan 15 | self.number_potential_customers = nan 16 | 17 | if (self.queue_capacity < 0): 18 | raise ValueError('queue_capacity must be \u2265 0: {cap:%d}'.format(cap=self.queue_capacity)) 19 | 20 | def reset(self): 21 | SimEntityBase.reset(self) 22 | self.number_in_queue = 0 23 | self.number_available_servers = self.total_number_servers 24 | self.number_balks = 0 25 | self.number_potential_customers = 0 26 | 27 | def run(self): 28 | self.notify_state_change('number_in_queue', self.number_in_queue) 29 | self.notify_state_change('number_available_servers', self.number_available_servers) 30 | self.notify_state_change('number_balks', self.number_balks) 31 | self.notify_state_change('number_potential_customers', self.number_potential_customers) 32 | 33 | def arrival(self): 34 | self.number_potential_customers += 1 35 | self.notify_state_change('number_potential_customers', self.number_potential_customers) 36 | 37 | if self.number_in_queue < self.queue_capacity or self.number_available_servers > 0: 38 | self.schedule('join_queue', 0.0) 39 | 40 | if self.number_in_queue == self.queue_capacity and self.number_available_servers == 0: 41 | self.schedule('balk', 0.0) 42 | 43 | def balk(self): 44 | self.number_balks += 1 45 | self.notify_state_change('number_balks', self.number_balks) 46 | 47 | def join_queue(self): 48 | self.number_in_queue += 1 49 | 50 | if self.number_available_servers > 0: 51 | self.schedule('start_service', 0.0, priority=Priority.HIGH) 52 | 53 | def start_service(self): 54 | self.number_in_queue -= 1 55 | self.notify_state_change('number_in_queue', self.number_in_queue) 56 | 57 | self.number_available_servers -= 1 58 | self.notify_state_change('number_available_servers', self.number_available_servers) 59 | 60 | self.schedule('end_service', self.service_time_generator.generate()) 61 | 62 | def end_service(self): 63 | self.number_available_servers += 1 64 | self.notify_state_change('number_available_servers', self.number_available_servers) 65 | 66 | if self.number_in_queue > 0: 67 | self.schedule('start_service', 0.0, priority=Priority.HIGH) 68 | -------------------------------------------------------------------------------- /simkit/examples/ggkqueue.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import Priority 3 | from math import nan 4 | 5 | class GGkQueue (SimEntityBase): 6 | def __init__(self, interarrival_time_generator, number_servers, service_time_generator): 7 | SimEntityBase.__init__(self) 8 | self.interarrival_time_generator = interarrival_time_generator 9 | self.number_servers = number_servers 10 | self.service_time_generator = service_time_generator 11 | self.number_in_queue = nan 12 | self.number_available_servers = nan 13 | 14 | def reset(self): 15 | SimEntityBase.reset(self) 16 | self.number_available_servers = self.number_servers 17 | self.number_in_queue = 0 18 | 19 | def run(self): 20 | self.notify_state_change('number_in_queue', self.number_in_queue) 21 | self.notify_state_change('number_available_servers', self.number_available_servers) 22 | 23 | self.schedule('arrival', self.service_time_generator.generate()) 24 | 25 | def arrival(self): 26 | self.number_in_queue += 1 27 | self.notify_state_change('number_in_queue', self.number_in_queue) 28 | 29 | self.schedule('arrival', self.interarrival_time_generator.generate()) 30 | 31 | if self.number_available_servers > 0: 32 | self.schedule('start_service', 0.0, priority=Priority.HIGH ) 33 | 34 | def start_service(self): 35 | self.number_in_queue -= 1 36 | self.notify_state_change('number_in_queue', self.number_in_queue) 37 | 38 | self.number_available_servers -= 1 39 | self.notify_state_change('number_available_servers', self.number_available_servers) 40 | 41 | self.schedule('end_service', self.service_time_generator.generate()) 42 | 43 | def end_service(self): 44 | self.number_available_servers += 1 45 | self.notify_state_change('number_available_servers', self.number_available_servers) 46 | 47 | if self.number_in_queue > 0: 48 | self.schedule('start_service', 0.0, priority=Priority.HIGH) 49 | -------------------------------------------------------------------------------- /simkit/examples/mover/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahbuss/DESpy/ce149ae5581017fc3df85971da9f73f6156fb300/simkit/examples/mover/__init__.py -------------------------------------------------------------------------------- /simkit/examples/mover/simplemover.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | from simkit.base import SimEntityBase 4 | from simkit.base import EventList 5 | from math import sqrt 6 | from math import pow 7 | from math import nan 8 | 9 | class Point: 10 | def __init__(self, x=0.0, y=0.0): 11 | self.x = x 12 | self.y = y 13 | 14 | def distance(self, other): 15 | delta_x = other.x - self.x 16 | delta_y = other.y - self.y 17 | return sqrt(delta_x * delta_x + delta_y * delta_y) 18 | 19 | def set_coord(self, x, y): 20 | self.x = x 21 | self.y = y 22 | 23 | def add(self, other): 24 | return Point(self.x + other.x, self.y + other.y) 25 | 26 | def subtract(self, other): 27 | return Point(self.x - other.x, self.y - other.y) 28 | 29 | def norm_sq(self): 30 | return self.x * self.x + self.y * self.y 31 | 32 | def norm(self): 33 | return sqrt(self.norm_sq()) 34 | 35 | def inner_product(self, other): 36 | return self.x * other.x + self.y + other.y 37 | 38 | def scalar_mult(self, a): 39 | self.x *= a 40 | self.y *= a 41 | 42 | def __repr__(self): 43 | return 'p({x:.3f}, {y:.3f})'.format(x=self.x, y= self.y) 44 | 45 | 46 | class SimpleMover(SimEntityBase): 47 | def __init__(self, initial_loc, max_speed): 48 | SimEntityBase.__init__(self) 49 | self.initial_loc = initial_loc 50 | self.max_speed = max_speed 51 | self.last_stop_loc = None 52 | self.velocity = None 53 | self.start_move_time = nan 54 | self.destination = None 55 | 56 | def reset(self): 57 | self.start_move_time = EventList.simtime 58 | self.last_stop_loc = self.initial_loc 59 | self.velocity = Point(0.0, 0.0) 60 | self.destination = Point(nan, nan) 61 | 62 | def run(self): 63 | self.notify_state_change('start_move_time', self.start_move_time) 64 | self.notify_state_change('last_stop_loc', self.last_stop_loc) 65 | self.notify_state_change('velocity', self.velocity) 66 | self.notify_state_change('destination', self.destination) 67 | 68 | def move_to(self, destination): 69 | self.destination = destination 70 | self.notify_state_change('destination', self.destination) 71 | 72 | self.schedule('start_move', 0.0, self) 73 | 74 | def start_move(self, me): 75 | diff = self.destination.subtract(self.last_stop_loc) 76 | distance = diff.norm() 77 | self.velocity.set_coord(self.max_speed * diff.x / distance, self.max_speed * diff.y / distance) 78 | self.notify_state_change('velocity', self.velocity) 79 | 80 | self.start_move_time = EventList.simtime 81 | self.notify_state_change('start_move_time', self.start_move_time) 82 | 83 | time_to_destination = distance / self.max_speed 84 | self.schedule('end_move', time_to_destination, me) 85 | 86 | def end_move(self, me): 87 | self.last_stop_loc.set_coord(self.destination.x, self.destination.y) 88 | self.notify_state_change('last_stop_loc', self.last_stop_loc) 89 | 90 | self.velocity.set_coord(0.0, 0.0) 91 | self.notify_state_change('velocity', self.velocity) 92 | 93 | self.destination.set_coord(nan, nan) 94 | 95 | def order_stop(self): 96 | self.schedule('stop', 0.0, self) 97 | 98 | def stop(self, me): 99 | self.last_stop_loc = self.current_loc() 100 | self.notify_state_change('last_stop_loc', self.last_stop_loc) 101 | 102 | self.start_move_time = EventList.simtime 103 | self.notify_state_change('start_move_time', self.start_move_time) 104 | 105 | self.velocity.set_coord(0.0, 0.0) 106 | self.notify_state_change('velocity', self.velocity) 107 | 108 | def current_loc(self): 109 | current_loc = self.last_stop_loc 110 | if not self.velocity is None: 111 | if self.velocity.norm() > 0: 112 | current_loc = Point(self.last_stop_loc.x + self.velocity.x * (EventList.simtime - self.start_move_time), \ 113 | self.last_stop_loc.y + self.velocity.y * (EventList.simtime - self.start_move_time)) 114 | return current_loc 115 | 116 | def __repr__(self): 117 | return '{name} {loc} {velocity}'.format(name=self.name, loc=self.current_loc(), velocity=self.velocity) 118 | 119 | class SimplePathMoverManager(SimEntityBase): 120 | def __init__(self, mover, path, start_on_run): 121 | SimEntityBase.__init__(self) 122 | self.mover = mover 123 | self.path = path 124 | self.start_on_run = start_on_run 125 | 126 | self.next = nan 127 | 128 | self.add_sim_event_listener(self.mover) 129 | self.mover.add_sim_event_listener(self) 130 | 131 | def reset(self): 132 | SimEntityBase.reset(self) 133 | self.next = 0 134 | 135 | def run(self): 136 | if self.start_on_run and len(self.path) > 0: 137 | self.schedule('move_to', 0.0, self.path[self.next]) 138 | 139 | def move_to(self, destination): 140 | pass 141 | 142 | def end_move(self, mover): 143 | if mover == self.mover: 144 | self.next += 1; 145 | if self.next < (len(self.path) - 1): 146 | self.schedule('move_to', 0.0, self.path[self.next]) 147 | 148 | else: 149 | self.schedule('order_stop', 0.0) 150 | 151 | 152 | -------------------------------------------------------------------------------- /simkit/examples/run/RunArrivalProcess.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.arrivalprocess import ArrivalProcess 2 | from simkit.base import EventList 3 | from simkit.simutil import SimpleStateChangeDumper 4 | from simkit.rand import RandomVariate 5 | 6 | interarrival_time_generator = RandomVariate.instance('Exponential', mean=1.7) 7 | arrival_process = ArrivalProcess(interarrival_time_generator) 8 | print(arrival_process.describe()) 9 | print() 10 | 11 | arrival_process.add_state_change_listener(SimpleStateChangeDumper()) 12 | 13 | EventList.verbose = True 14 | EventList.stop_at_time(15.0) 15 | 16 | EventList.reset() 17 | EventList.start_simulation() -------------------------------------------------------------------------------- /simkit/examples/run/RunConfidenceInterval.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.entitycreator import EntityCreator 2 | from simkit.examples.entityserver import EntityServer 3 | from simkit.stats import SimpleStatsTally 4 | from simkit.stats import CollectionSizeTimeVarying 5 | from simkit.rand import RandomVariate 6 | from simkit.base import EventList 7 | from time import time 8 | 9 | interarrival_time_generator = RandomVariate.instance('Uniform', min=0.9, max=2.2) 10 | entity_creator = EntityCreator(interarrival_time_generator) 11 | print(entity_creator.describe()) 12 | 13 | total_number_servers=2 14 | service_time_generator = RandomVariate.instance('Gamma', alpha=1.7, beta=1.8) 15 | entity_server = EntityServer(total_number_servers, service_time_generator) 16 | print(entity_server.describe()) 17 | 18 | entity_creator.add_sim_event_listener(entity_server) 19 | 20 | inner_delay_in_queue_stat = SimpleStatsTally("delay_in_queue") 21 | entity_server.add_state_change_listener(inner_delay_in_queue_stat) 22 | 23 | inner_number_in_queue_stat = CollectionSizeTimeVarying('queue') 24 | entity_server.add_state_change_listener(inner_number_in_queue_stat) 25 | 26 | outer_number_in_queue_stat = SimpleStatsTally('outer_number_in_queue') 27 | 28 | outer_delay_in_queue_stat = SimpleStatsTally('mean_delay_in_queue') 29 | 30 | # print(getattr(entity_server, 'number_servers')) 31 | 32 | runtime = 800.0 33 | p = 0.975 34 | numberReps = 100 35 | 36 | EventList.stop_at_time(runtime) 37 | 38 | start = time() 39 | for rep in range(numberReps): 40 | EventList.reset() 41 | inner_delay_in_queue_stat.reset() 42 | inner_number_in_queue_stat.reset() 43 | EventList.start_simulation() 44 | outer_delay_in_queue_stat.new_observation(inner_delay_in_queue_stat.mean) 45 | outer_number_in_queue_stat.new_observation(inner_number_in_queue_stat.mean) 46 | end = time() 47 | elapsed = end-start 48 | print('\n{reps:d} replications of length {runtime:,.1f} took {time:,.4f} sec'.format(reps=numberReps,runtime=runtime, time=elapsed)) 49 | print('95% CI for number in queue: {mean:,.4f} \u00B1 {halfwidth:,.4f}'.format(mean=outer_number_in_queue_stat.mean, halfwidth=outer_number_in_queue_stat.halfwidth(p))) 50 | print('95% CI for delay in queue: {mean:,.4f} \u00B1 {halfwidth:,.4f}'.format(mean=outer_delay_in_queue_stat.mean, halfwidth=outer_delay_in_queue_stat.halfwidth(p))) 51 | 52 | 53 | -------------------------------------------------------------------------------- /simkit/examples/run/RunConfidenceIntervalSteadyState.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.entitycreator import EntityCreator 2 | from simkit.examples.entityserver import EntityServer 3 | from simkit.stats import SimpleStatsTally, TruncatingSimpleStatsTally 4 | from simkit.stats import CollectionSizeTimeVarying 5 | from simkit.rand import RandomVariate 6 | from simkit.base import EventList 7 | from time import time 8 | 9 | interarrival_time_generator = RandomVariate.instance('Uniform', min=1.1, max=3.2) 10 | entity_creator = EntityCreator(interarrival_time_generator) 11 | print(entity_creator.describe()) 12 | 13 | total_number_servers=2 14 | service_time_generator = RandomVariate.instance('Gamma', alpha=1.7, beta=1.8) 15 | entity_server = EntityServer(total_number_servers, service_time_generator) 16 | print(entity_server.describe()) 17 | 18 | entity_creator.add_sim_event_listener(entity_server) 19 | 20 | truncation_point = 100000 21 | steady_state_observations = 10000 22 | 23 | inner_delay_in_queue_stat = TruncatingSimpleStatsTally('delay_in_queue', truncation_point) 24 | entity_server.add_state_change_listener(inner_delay_in_queue_stat) 25 | 26 | inner_number_in_queue_stat = CollectionSizeTimeVarying('queue') 27 | entity_server.add_state_change_listener(inner_number_in_queue_stat) 28 | 29 | outer_number_in_queue_stat = SimpleStatsTally('outer_number_in_queue') 30 | 31 | outer_delay_in_queue_stat = SimpleStatsTally('mean_delay_in_queue') 32 | 33 | p = 0.975 34 | numberReps = 50 35 | 36 | EventList.stop_on_event(truncation_point + steady_state_observations, 'start_service') 37 | 38 | print('\nRunning {reps:d} replications with truncation at {tp:,d} observations and {ss:,d} observations in steady-state '.\ 39 | format(reps=numberReps,tp=truncation_point, ss=steady_state_observations)) 40 | start = time() 41 | for rep in range(1,numberReps+1): 42 | if rep % 10 == 0: 43 | print('rep {rep:d} halfwidth {hw:,.4f}'.format(rep=rep, hw=outer_delay_in_queue_stat.halfwidth(p))) 44 | EventList.reset() 45 | inner_delay_in_queue_stat.reset() 46 | inner_number_in_queue_stat.reset() 47 | EventList.start_simulation() 48 | outer_delay_in_queue_stat.new_observation(inner_delay_in_queue_stat.mean) 49 | outer_number_in_queue_stat.new_observation(inner_number_in_queue_stat.mean) 50 | end = time() 51 | elapsed = end-start 52 | 53 | print('\nSimulation took {elapsed:,.4f} sec'.format(elapsed=elapsed)) 54 | print('95% CI for number in queue: {mean:,.4f} \u00B1 {halfwidth:,.4f}'.format(mean=outer_number_in_queue_stat.mean, halfwidth=outer_number_in_queue_stat.halfwidth(p))) 55 | print('95% CI for delay in queue: {mean:,.4f} \u00B1 {halfwidth:,.4f}'.format(mean=outer_delay_in_queue_stat.mean, halfwidth=outer_delay_in_queue_stat.halfwidth(p))) 56 | -------------------------------------------------------------------------------- /simkit/examples/run/RunEntityServer.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.entityserver import EntityServer 2 | from simkit.rand import RandomVariate 3 | from simkit.examples.entitycreator import EntityCreator 4 | from simkit.base import EventList 5 | from simkit.simutil import SimpleStateChangeDumper 6 | from simkit.stats import SimpleStatsTally 7 | from simkit.stats import CollectionSizeTimeVarying 8 | from time import time 9 | 10 | interarrivalMean = 1.7 11 | interarrival = RandomVariate.instance('Exponential', mean=interarrivalMean) 12 | entity_creator = EntityCreator(interarrival) 13 | print (entity_creator.describe()) 14 | 15 | alpha = 1.7 16 | beta = 1.8 17 | number_servers = 2 18 | generator = RandomVariate.instance('Gamma', alpha=alpha, beta=beta) 19 | entity_server = EntityServer(number_servers, generator) 20 | 21 | print(entity_server.describe()) 22 | 23 | entity_creator.add_sim_event_listener(entity_server) 24 | 25 | dumper = SimpleStateChangeDumper() 26 | 27 | delay_in_queue_stat = SimpleStatsTally("delay_in_queue") 28 | entity_server.add_state_change_listener(delay_in_queue_stat) 29 | 30 | time_in_system_stat = SimpleStatsTally('time_in_system') 31 | entity_server.add_state_change_listener(time_in_system_stat) 32 | 33 | number_in_queue_stat = CollectionSizeTimeVarying("queue") 34 | entity_server.add_state_change_listener(number_in_queue_stat) 35 | service_mean = alpha * beta 36 | 37 | EventList.stop_at_time(100000.0) 38 | EventList.verbose = False 39 | 40 | start = time() 41 | EventList.reset() 42 | EventList.start_simulation() 43 | end = time() 44 | 45 | print('\nSimulation took {time:,.3f} sec'.format(time=(end-start))) 46 | print('Simulation ended at simtime {time:,.0f}\n'.format(time=EventList.simtime)) 47 | 48 | print('Avg time in system = {avg:,.4f}'.format(avg=time_in_system_stat.mean)) 49 | print('Avg delay in queue = {avg:,.4f}'.format(avg=delay_in_queue_stat.mean)) 50 | print('Avg number in queue = {avg:,.4f}'.format(avg=number_in_queue_stat.time_varying_mean())) -------------------------------------------------------------------------------- /simkit/examples/run/RunEntityServerMultipleReps.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.entityserver import EntityServer 2 | from simkit.rand import RandomVariate 3 | from simkit.examples.entitycreator import EntityCreator 4 | from simkit.base import EventList 5 | from simkit.simutil import SimpleStateChangeDumper 6 | from simkit.stats import SimpleStatsTally 7 | from simkit.stats import CollectionSizeTimeVarying 8 | from simkit.quantiles import student_t 9 | from math import sqrt 10 | 11 | interarrivalMean = 1.7 12 | interarrival = RandomVariate.instance('Exponential', mean=interarrivalMean) 13 | entity_creator = EntityCreator(interarrival) 14 | print (entity_creator.describe()) 15 | 16 | alpha = 1.7 17 | beta = 1.8 18 | number_servers = 2 19 | generator = RandomVariate.instance('Gamma', alpha=alpha, beta=beta) 20 | entity_server = EntityServer(number_servers, generator) 21 | 22 | print(entity_server.describe()) 23 | 24 | entity_creator.add_sim_event_listener(entity_server) 25 | 26 | dumper = SimpleStateChangeDumper() 27 | 28 | delay_in_queue_stat = SimpleStatsTally("delay_in_queue") 29 | entity_server.add_state_change_listener(delay_in_queue_stat) 30 | 31 | time_in_system_stat = SimpleStatsTally('time_in_system') 32 | entity_server.add_state_change_listener(time_in_system_stat) 33 | 34 | number_in_queue_stat = CollectionSizeTimeVarying("queue") 35 | entity_server.add_state_change_listener(number_in_queue_stat) 36 | service_mean = alpha * beta 37 | 38 | outer_number_in_queue_stat = SimpleStatsTally("outer_number_in_queue") 39 | outer_delay_in_queue_stat = SimpleStatsTally("outer_delay_in_queue") 40 | 41 | stop_time = 100000.0 42 | 43 | EventList.stop_at_time(stop_time) 44 | EventList.verbose = False 45 | 46 | number_replications = 100 47 | 48 | print('There will be {reps:,d} replications for {time:,.1f} time units each'. 49 | format(reps=number_replications, time=stop_time)) 50 | 51 | for replication in [1, number_replications + 1]: 52 | EventList.reset() 53 | delay_in_queue_stat.reset() 54 | number_in_queue_stat.reset() 55 | EventList.start_simulation() 56 | outer_delay_in_queue_stat.new_observation(delay_in_queue_stat.mean) 57 | outer_number_in_queue_stat.new_observation(number_in_queue_stat.time_varying_mean()) 58 | 59 | alpha = 0.05; 60 | t_value = student_t(1-alpha/2, number_replications - 1) 61 | 62 | delay_halfwidth = outer_delay_in_queue_stat.stdev * t_value / sqrt(number_replications) 63 | number_in_queue_halfwidth = outer_number_in_queue_stat.stdev * t_value / sqrt(number_replications) 64 | 65 | print('{conf:.0f}% CI for delay in queue: {avg:,.4f} ± {half:,.4f}'.format(\ 66 | conf=100*(1-alpha),avg=outer_delay_in_queue_stat.mean, half = delay_halfwidth)) 67 | print('{conf:.0f}% CI for number in queue = {avg:,.4f} ± {half:,.4f}'.format(\ 68 | conf=100*(1-alpha),avg=outer_number_in_queue_stat.mean, half=number_in_queue_halfwidth)) -------------------------------------------------------------------------------- /simkit/examples/run/RunGGkQueue.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.ggkqueue import GGkQueue 2 | from simkit.rand import Uniform, Gamma 3 | from simkit.base import EventList 4 | from simkit.simutil import SimpleStateChangeDumper 5 | from simkit.stats import SimpleStatsTimeVarying 6 | from time import time 7 | 8 | interarrival_time_generator = Uniform( min=0.5, max=1.5) 9 | number_servers = 7 10 | service_time_generator = Gamma(alpha=3.0, beta=2.0) 11 | 12 | ggk_queue = GGkQueue(interarrival_time_generator, number_servers, service_time_generator) 13 | simple_state_change_dumper = SimpleStateChangeDumper() 14 | # ggk_queue.add_state_change_listener(simple_state_change_dumper) 15 | 16 | print(ggk_queue.describe()) 17 | 18 | 19 | 20 | stop_time = 100000.0 21 | EventList.stop_at_time(stop_time) 22 | # EventList.verbose = True 23 | 24 | number_in_queue_stat = SimpleStatsTimeVarying('number_in_queue') 25 | number_available_servers_stat = SimpleStatsTimeVarying('number_available_servers') 26 | 27 | ggk_queue.add_state_change_listener(number_in_queue_stat) 28 | ggk_queue.add_state_change_listener(number_available_servers_stat) 29 | 30 | start = time() 31 | EventList.reset() 32 | EventList.start_simulation() 33 | end = time() 34 | 35 | elapsed = end - start 36 | 37 | 38 | print('avg # in queue: {avg:,.4f}'.format(avg=number_in_queue_stat.time_varying_mean())) 39 | print('avg # avail servers: {avg:,.4f}'.format(avg=number_available_servers_stat.time_varying_mean())) 40 | 41 | utilization = 1.0 - number_available_servers_stat.mean / ggk_queue.number_servers 42 | print('avg utilization: {avg:,.4f}'.format(avg=utilization)) 43 | -------------------------------------------------------------------------------- /simkit/examples/run/RunMultipleServerQueue.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.basic import MultipleServerQueue 2 | from simkit.rand import RandomVariate 3 | from simkit.base import EventList 4 | from simkit.simutil import SimpleStateChangeDumper 5 | from simkit.stats import * 6 | 7 | interarrival_time_generator = RandomVariate.instance('Uniform', min=0.9, max=2.3) 8 | number_servers = 2 9 | service_time_generator = RandomVariate.instance('Gamma', alpha=1.7, beta=1.8) 10 | 11 | multiple_server_queue = MultipleServerQueue(interarrival_time_generator, number_servers, service_time_generator) 12 | print(multiple_server_queue.describe()) 13 | 14 | simple_state_change_dumper = SimpleStateChangeDumper() 15 | # multiple_server_queue.add_state_change_listener(simple_state_change_dumper) 16 | 17 | number_in_queue_stat = SimpleStatsTimeVarying('number_in_queue') 18 | multiple_server_queue.add_state_change_listener(number_in_queue_stat) 19 | 20 | delay_in_queue_stat = SimpleStatsTally('delay_in_queue') 21 | multiple_server_queue.add_state_change_listener(delay_in_queue_stat) 22 | 23 | time_in_system_stat = SimpleStatsTally('time_in_system') 24 | multiple_server_queue.add_state_change_listener(time_in_system_stat) 25 | 26 | collection_stat = CollectionSizeTimeVarying('queue') 27 | multiple_server_queue.add_state_change_listener(collection_stat) 28 | 29 | number_available_servers_stat = SimpleStatsTimeVarying('number_available_servers') 30 | multiple_server_queue.add_state_change_listener(number_available_servers_stat) 31 | 32 | EventList.verbose = False 33 | # EventList.stop_on_event(5, 'arrival') 34 | EventList.stop_at_time(10000) 35 | 36 | EventList.reset() 37 | EventList.start_simulation() 38 | 39 | print('Simulation ended at time {time:,.2f}'.format(time=EventList.simtime)) 40 | print('There have been {arrivals:,d} arrivals'.format(arrivals=multiple_server_queue.number_arrivals)) 41 | print('Avg # in queue: {avg:.4f}'.format(avg=number_in_queue_stat.time_varying_mean())) 42 | print('Avg delay in queue: {time:.4f}'.format(time=delay_in_queue_stat.mean)) 43 | print('Avg time in system: {time:.4f}'.format(time=time_in_system_stat.mean)) 44 | 45 | avg_number_in_queue = number_in_queue_stat.time_varying_mean() 46 | avg_arrival_rate = multiple_server_queue.number_arrivals / EventList.simtime 47 | 48 | little_delay_in_queue = avg_number_in_queue / avg_arrival_rate 49 | print("Avg delay in queue via Little's formula: {avg:.4f}".format(avg=little_delay_in_queue)) 50 | 51 | avg_number_in_system = number_in_queue_stat.time_varying_mean() + multiple_server_queue.number_servers - number_available_servers_stat.time_varying_mean() 52 | little_time_in_system = avg_number_in_system / avg_arrival_rate 53 | 54 | print("Avg time in system via Little's formula: {avg:.4f}".format(avg=little_time_in_system)) 55 | -------------------------------------------------------------------------------- /simkit/examples/run/RunServerWithReneges.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.serverwithreneges import CustomerCreator 2 | from simkit.examples.serverwithreneges import ServerWithReneges 3 | from simkit.rand import RandomVariate 4 | from simkit.base import EventList 5 | from simkit.stats import SimpleStatsTally 6 | from simkit.stats import CollectionSizeTimeVarying 7 | from time import time 8 | 9 | interarrival_generator = RandomVariate.instance('Exponential', mean=1.5) 10 | renege_generator = RandomVariate.instance('Uniform', min=2.0, max=6.0) 11 | creator = CustomerCreator(interarrival_generator, renege_generator) 12 | print(creator.describe()) 13 | 14 | total_number_servers = 2 15 | service_time_generator = RandomVariate.instance('Gamma', alpha=2.5, beta=1.2) 16 | server = ServerWithReneges(total_number_servers, service_time_generator) 17 | print (server.describe()) 18 | 19 | creator.add_sim_event_listener(server) 20 | 21 | delay_in_queue_stat = SimpleStatsTally('delay_in_queue') 22 | server.add_state_change_listener(delay_in_queue_stat) 23 | 24 | number_in_queue_stat = CollectionSizeTimeVarying('queue') 25 | server.add_state_change_listener(number_in_queue_stat) 26 | 27 | for rep in range(1,5): 28 | start = time() 29 | EventList.stop_at_time(100000.0) 30 | EventList.reset() 31 | number_in_queue_stat.reset() 32 | EventList.start_simulation() 33 | end = time() 34 | 35 | print('\nReplication {rep:,d} took {time:.4f} sec'.format(time = end - start, rep=rep)) 36 | print('Simulation ended at simtime {time:,.0f}\n'.format(time=EventList.simtime)) 37 | print('Avg delay in queue = {avg:,.4f}'.format(avg=(delay_in_queue_stat.mean))) 38 | print('Avg number in queue = {avg:.4f}'.format(avg=number_in_queue_stat.time_varying_mean())) 39 | print('There have been {num:,d} reneges'.format(num=server.number_reneges)) 40 | print('There have been {num:,d} served'.format(num=delay_in_queue_stat.count)) 41 | print('% of potential customers reneging = {percent:.2f}% '. \ 42 | format(percent=(100 * server.number_reneges / (server.number_reneges + delay_in_queue_stat.count)))) -------------------------------------------------------------------------------- /simkit/examples/run/RunSimpleServer.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example executes the Simple Server component for one replication, estimating the expected average number in 3 | queue and utilization of servers. Arrivals are using the ArrivalProcess component 4 | 5 | Parameters Used: 6 | ArrivalProcess: Uniform(0.9, 2.2) interarrival times 7 | SimpleServer: 2 servers, Gamma(1.7, 1.8) service times 8 | Run length: 100,000 time units 9 | """ 10 | from time import time 11 | 12 | from simkit.rand import RandomVariate 13 | from simkit.base import EventList 14 | from simkit.stats import SimpleStatsTimeVarying 15 | from simkit.examples.arrivalprocess import ArrivalProcess 16 | from simkit.examples.simpleserver import SimpleServer 17 | 18 | interarrival_time_generator = RandomVariate.instance('Uniform', min=0.9, max=2.2) 19 | arrival_process = ArrivalProcess(interarrival_time_generator) 20 | 21 | number_servers = 2 22 | service_time_generator = RandomVariate.instance('Gamma', alpha=1.7, beta=1.8) 23 | simple_server = SimpleServer(number_servers, service_time_generator) 24 | 25 | arrival_process.add_sim_event_listener(simple_server) 26 | 27 | number_in_queue_stat = SimpleStatsTimeVarying('number_in_queue') 28 | number_available_servers_stat = SimpleStatsTimeVarying('number_available_servers') 29 | 30 | simple_server.add_state_change_listener(number_in_queue_stat) 31 | simple_server.add_state_change_listener(number_available_servers_stat) 32 | 33 | print(arrival_process.describe()) 34 | print(simple_server.describe()) 35 | print() 36 | 37 | 38 | stopTime = 10000 39 | EventList.stop_at_time(stopTime) 40 | 41 | start = time() 42 | EventList.reset() 43 | EventList.start_simulation() 44 | end = time() 45 | 46 | elapsed = end - start 47 | print('Simulation took {time:.3f} sec'.format(time=elapsed)) 48 | print('Simulation ended at simtime {time:,.0f}'.format(time=EventList.simtime)) 49 | utilization = 1.0 - number_available_servers_stat.time_varying_mean() / simple_server.total_number_servers 50 | print('Avg # in queue = \t{avg:.4f}'.format(avg=number_in_queue_stat.time_varying_mean())) 51 | print('Avg # utilization = {avg:.4f}'.format(avg=utilization)) -------------------------------------------------------------------------------- /simkit/examples/run/RunTandemQueueWithBlocking.py: -------------------------------------------------------------------------------- 1 | from simkit.rand import RandomVariate 2 | from simkit.base import EventList 3 | from simkit.simutil import SimpleStateChangeDumper 4 | from simkit.examples.basic import TandemQueueWithBlocking 5 | from simkit.stats import SimpleStatsTimeVarying 6 | from simkit.rand import RandomVariate 7 | from random import Random 8 | 9 | # Uncomment to seed with computer clock time 10 | # RandomVariate.baseRNG = Random() 11 | interarrval_time_generator = RandomVariate.instance('Exponential', mean=1.8) 12 | service_time_generator = [RandomVariate.instance('Gamma', alpha=2.5, beta=1.6), RandomVariate.instance('Uniform', min=2.2, max=4.4)] 13 | number_servers = [3, 2] 14 | buffer_size = 1 15 | 16 | print(interarrval_time_generator) 17 | print(service_time_generator) 18 | 19 | tandem_queue_with_blocking = TandemQueueWithBlocking(interarrval_time_generator, number_servers[0], number_servers[1], 20 | service_time_generator[0], 21 | service_time_generator[1], 22 | buffer_size) 23 | print(tandem_queue_with_blocking.describe()) 24 | 25 | simple_state_change_dumper = SimpleStateChangeDumper() 26 | # tandem_queue_with_blocking.add_state_change_listener(simple_state_change_dumper) 27 | 28 | number_in_queue1_stat = SimpleStatsTimeVarying('number_in_queue1') 29 | number_in_queue2_stat = SimpleStatsTimeVarying('number_in_queue2') 30 | number_available_server1_stat = SimpleStatsTimeVarying('number_available_server1') 31 | number_available_server2_stat = SimpleStatsTimeVarying('number_available_server2') 32 | tandem_queue_with_blocking.add_state_change_listener(number_in_queue1_stat) 33 | tandem_queue_with_blocking.add_state_change_listener(number_in_queue2_stat) 34 | tandem_queue_with_blocking.add_state_change_listener(number_available_server1_stat) 35 | tandem_queue_with_blocking.add_state_change_listener(number_available_server2_stat) 36 | 37 | stop_time = 100000.0 38 | # stop_time = 1000 39 | # EventList.verbose = True 40 | EventList.stop_at_time(stop_time) 41 | upper_range = 20 42 | 43 | # number_leaves = 100000 44 | # EventList.stop_on_event(number_leaves, 'leave2') 45 | print('Simulation will run for {time:,.2f} time units'.format(time=EventList.stop_time)) 46 | print('Buffer sizes will be from 1 to {upper:,d}'.format(upper=upper_range)) 47 | # print('Simulation will run for {num:,d} leave2 events'.format(num=number_leaves)) 48 | 49 | for buffer_size in range(1, upper_range + 1): 50 | tandem_queue_with_blocking.buffer_size = buffer_size 51 | 52 | EventList.reset() 53 | 54 | number_in_queue1_stat.reset() 55 | number_in_queue2_stat.reset() 56 | number_available_server1_stat.reset() 57 | number_available_server2_stat.reset() 58 | 59 | EventList.start_simulation() 60 | 61 | print('buffer size: {buffer:d} avg # in queue1: {avg:,.4f}'.format(buffer=buffer_size, avg=number_in_queue1_stat.time_varying_mean())) 62 | # print(number_in_queue1_stat) 63 | # print(number_in_queue2_stat) 64 | # print(number_available_server1_stat) 65 | # print(number_available_server2_stat) -------------------------------------------------------------------------------- /simkit/examples/run/RunTransferLine.py: -------------------------------------------------------------------------------- 1 | from simkit.base import EventList 2 | from simkit.rand import RandomVariate 3 | from simkit.base import Adapter 4 | from simkit.stats import IndexedSimpleStatsTimeVarying 5 | from simkit.stats import IndexedSimpleStatsTally 6 | from simkit.stats import SimpleStatsTally 7 | from simkit.stats import IndexedCollectionSizeTimeVaryingStat 8 | from simkit.examples.transferline import TransferLine 9 | from simkit.examples.transferline import JobCreator 10 | from time import time 11 | 12 | job_creator = JobCreator(RandomVariate.instance('Exponential', mean=1.7)) 13 | print(job_creator.describe()) 14 | 15 | number_stations = 3 16 | number_machines = [5, 4, 2] 17 | service_times = [RandomVariate.instance('Gamma', alpha=3.2, beta=2.3), \ 18 | RandomVariate.instance('Uniform', min=4.5, max=6.7), 19 | RandomVariate.instance('Exponential', mean=3.0)] 20 | transfer_line = TransferLine(number_stations, number_machines, service_times) 21 | print(transfer_line.describe()) 22 | 23 | adapter = Adapter('job_arrival', 'arrival') 24 | adapter.connect(job_creator, transfer_line) 25 | 26 | number_available_machines_stat = IndexedSimpleStatsTimeVarying('number_available_machines') 27 | transfer_line.add_state_change_listener(number_available_machines_stat) 28 | 29 | delay_in_queue_stat = IndexedSimpleStatsTally('delay_in_queue') 30 | transfer_line.add_state_change_listener(delay_in_queue_stat) 31 | 32 | time_at_station_stat = IndexedSimpleStatsTally('time_at_station') 33 | transfer_line.add_state_change_listener(time_at_station_stat) 34 | 35 | time_in_system_stat = SimpleStatsTally('time_in_system') 36 | transfer_line.add_state_change_listener(time_in_system_stat) 37 | 38 | number_in_queue_stat = IndexedCollectionSizeTimeVaryingStat('queue') 39 | transfer_line.add_state_change_listener(number_in_queue_stat) 40 | 41 | total_delay_in_queue_stat = SimpleStatsTally('total_delay_in_queue') 42 | transfer_line.add_state_change_listener(total_delay_in_queue_stat) 43 | 44 | time_in_system_stat = SimpleStatsTally('time_in_system') 45 | transfer_line.add_state_change_listener(time_in_system_stat) 46 | 47 | EventList.stop_at_time(100000.0) 48 | 49 | start = time() 50 | EventList.verbose = False 51 | EventList.reset() 52 | EventList.start_simulation() 53 | end = time() 54 | 55 | print('\nSimulation run took {sec:.4f} sec'.format(sec=(end-start))) 56 | print('Simulation ended at simtime {time:,.0f}'.format(time=EventList.simtime)) 57 | 58 | print('Number of Arrivals:\t{num:,d}'.format(num=job_creator.number_arrivals)) 59 | print('Number completed: \t{num:,d}'.format(num=time_in_system_stat.count)) 60 | 61 | print('\nUsing Direct Estimation:') 62 | print('Station\tAvg Util\tAvg # in Q\tAvg Delay in Q\tAvg Time at Station') 63 | for station in range(transfer_line.number_stations): 64 | utilization = 1.0 - number_available_machines_stat.time_varying_mean(station) / transfer_line.number_machines[station] 65 | print(' {station:d}\t{util:.3f}\t\t {numinq:.3f}\t\t {delay:.3f}\t\t\t{timeas:.3f}'.\ 66 | format(station=station, numinq=number_in_queue_stat.time_varying_mean(station), util=utilization, delay=delay_in_queue_stat.mean(station),\ 67 | timeas=time_at_station_stat.mean(station))) 68 | 69 | arrival_rate = job_creator.number_arrivals / EventList.simtime 70 | 71 | print('\nUsing Little\'s Formula:') 72 | print('Station\tAvg Delay in Q\tAvg Time at Station') 73 | for station in range(transfer_line.number_stations): 74 | number_arrivals_to_station = delay_in_queue_stat.count(station) + len(transfer_line.queue[station]) + \ 75 | transfer_line.number_machines[station] - transfer_line.number_available_machines[station] 76 | arrival_rate_to_station = number_arrivals_to_station / EventList.simtime 77 | print(' {station:d}\t {delay:,.3f}\t {time:,.3f}'.format(station=station,\ 78 | delay=(number_in_queue_stat.mean(station)/arrival_rate),\ 79 | time = ((number_in_queue_stat.mean(station) + transfer_line.number_machines[station] - \ 80 | number_available_machines_stat.time_varying_mean(station))/arrival_rate))) 81 | 82 | print('\nAvg total delay in queue\t{delay:,.3f}'.format(delay=total_delay_in_queue_stat.mean)) 83 | print('Avg total time in system\t{time:,.3f}'.format(time=time_in_system_stat.mean)) -------------------------------------------------------------------------------- /simkit/examples/run/RunTwoCranesBerth.py: -------------------------------------------------------------------------------- 1 | from pydoc import describe 2 | 3 | from simkit.examples import twocranesberth 4 | from simkit.rand import RandomVariate 5 | from simkit.base import EventList 6 | from simkit.simutil import SimpleStateChangeDumper 7 | from simkit.examples.twocranesberth import ShipArrivalProcess 8 | from simkit.examples.twocranesberth import TwoCranesBerth 9 | from simkit.stats import SimpleStatsTally 10 | 11 | interarrival_genarator = RandomVariate.instance('Exponential', mean=0.7) 12 | unload_time_generator = RandomVariate.instance('Uniform', min=0.5, max=1.5) 13 | ship_arrival_process = ShipArrivalProcess(interarrival_genarator, unload_time_generator) 14 | 15 | two_cranes_berth = TwoCranesBerth(); 16 | ship_arrival_process.add_sim_event_listener(two_cranes_berth) 17 | 18 | simple_state_dumper = SimpleStateChangeDumper() 19 | ship_arrival_process.add_state_change_listener(simple_state_dumper) 20 | two_cranes_berth.add_state_change_listener(simple_state_dumper) 21 | 22 | print (ship_arrival_process) 23 | print(two_cranes_berth) 24 | 25 | delay_in_queue_stat = SimpleStatsTally('delay_in_queue') 26 | time_in_system_stat = SimpleStatsTally('time_in_system') 27 | 28 | two_cranes_berth.add_state_change_listener(delay_in_queue_stat) 29 | two_cranes_berth.add_state_change_listener(time_in_system_stat) 30 | 31 | EventList.verbose = True 32 | 33 | stop_time = 3650.0 34 | EventList.stop_at_time(stop_time) 35 | 36 | EventList.reset() 37 | EventList.start_simulation() 38 | 39 | print('Simulation ended at time {time:,.1f}'.format(time=EventList.simtime)) 40 | print('# arriving ships: {arrive:,d}'.format(arrive=ship_arrival_process.number_arrivals)) 41 | 42 | -------------------------------------------------------------------------------- /simkit/examples/run/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahbuss/DESpy/ce149ae5581017fc3df85971da9f73f6156fb300/simkit/examples/run/__init__.py -------------------------------------------------------------------------------- /simkit/examples/serverwithreneges.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import Entity 3 | from simkit.base import Priority 4 | from math import nan 5 | from heapq import heappush 6 | from heapq import heappop 7 | 8 | class RenegingCustomer(Entity): 9 | 10 | def __init__(self, renegeTime): 11 | Entity.__init__(self, 'RenegingCustomer') 12 | self.renegeTime = renegeTime 13 | 14 | def __repr__(self): 15 | return Entity.__repr__(self) + ' (' + str(round(self.renegeTime,4)) + ')' 16 | 17 | class CustomerCreator(SimEntityBase): 18 | 19 | def __init__(self, interarrival_time_generator, renege_time_generator): 20 | SimEntityBase.__init__(self) 21 | self.interarrival_time_generator = interarrival_time_generator 22 | self.renege_time_generator = renege_time_generator 23 | self.number_arrivals = nan 24 | 25 | def reset(self): 26 | SimEntityBase.reset(self) 27 | self.number_arrivals = 0 28 | 29 | def run(self): 30 | self.schedule('create', self.interarrival_time_generator.generate()) 31 | 32 | def create(self): 33 | self.number_arrivals += 1 34 | self.notify_state_change('number_arrivals', self.number_arrivals) 35 | customer = RenegingCustomer(self.renege_time_generator.generate()) 36 | self.schedule('arrival', 0.0, customer) 37 | self.schedule('create', self.interarrival_time_generator.generate()) 38 | 39 | def __repr__(self): 40 | return SimEntityBase.__repr__(self) + ' (' + str(self.interarrival_time_generator) + ', ' + str(self.renege_time_generator) + ')' 41 | 42 | class ServerWithReneges(SimEntityBase): 43 | 44 | def __init__(self, total_number_servers, service_time_generator): 45 | SimEntityBase.__init__(self) 46 | self.total_number_servers = total_number_servers 47 | self.service_time_generator = service_time_generator 48 | self.queue = [] 49 | self.number_available_servers = nan 50 | self.number_reneges = nan 51 | self.number_served = nan 52 | 53 | def reset(self): 54 | self.queue.clear() 55 | self.number_available_servers = self.total_number_servers 56 | self.number_reneges = 0 57 | self.number_served = 0 58 | 59 | def run(self): 60 | self.notify_state_change('number_available_servers', self.number_available_servers) 61 | self.notify_state_change('queue', self.queue) 62 | self.notify_state_change('number_reneges', self.number_reneges) 63 | self.notify_state_change('number_served', self.number_served) 64 | 65 | def arrival(self, customer): 66 | customer.stamp_time() 67 | heappush(self.queue, customer) 68 | self.notify_state_change('queue', self.queue) 69 | 70 | if self.number_available_servers > 0: 71 | self.schedule('start_service', 0.0, priority=Priority.HIGH) 72 | 73 | self.schedule('renege', customer.renegeTime, customer) 74 | 75 | def start_service(self): 76 | 77 | customer = heappop(self.queue) 78 | self.cancel('renege', customer) 79 | 80 | self.notify_state_change('delay_in_queue', customer.elapsed_time()) 81 | self.notify_state_change('queue', self.queue) 82 | 83 | self.number_available_servers -= 1 84 | self.notify_state_change('number_available_servers', self.number_available_servers) 85 | 86 | self.schedule('end_service', self.service_time_generator.generate(), customer) 87 | 88 | def renege(self, customer): 89 | self.number_reneges += 1; 90 | self.notify_state_change('number_reneges', self.number_reneges) 91 | 92 | self.notify_state_change('delay_in_queue_reneged', customer.elapsed_time()) 93 | 94 | self.queue.remove(customer) 95 | self.notify_state_change('queue', self.queue) 96 | 97 | def end_service(self, customer): 98 | self.notify_state_change('time_in_system', customer.elapsed_time()) 99 | 100 | self.number_available_servers += 1 101 | self.notify_state_change('number_available_servers', self.number_available_servers) 102 | 103 | if self.queue.__len__() > 0: 104 | self.schedule('start_service', 0.0, priority=Priority.HIGH) 105 | 106 | @property 107 | def total_number_servers(self): 108 | return self.__totalNumberServers 109 | 110 | @total_number_servers.setter 111 | def total_number_servers(self, totalNumberServers): 112 | if totalNumberServers <= 0: 113 | raise ValueError('total_number_servers must be > 0: ' + str(totalNumberServers)) 114 | self.__totalNumberServers = totalNumberServers 115 | 116 | 117 | -------------------------------------------------------------------------------- /simkit/examples/simpleserver.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import Priority 3 | from math import nan 4 | 5 | class SimpleServer(SimEntityBase): 6 | 7 | def __init__(self, total_number_servers, service_time_generator): 8 | SimEntityBase.__init__(self) 9 | self.total_number_servers = total_number_servers 10 | self.service_time_generator = service_time_generator 11 | self.number_in_queue = nan 12 | self.number_available_servers = nan 13 | self.number_served = nan 14 | 15 | def reset(self): 16 | self.number_available_servers = self.total_number_servers 17 | self.number_in_queue = 0 18 | self.number_served = 0 19 | 20 | def run(self): 21 | self.notify_state_change('number_available_servers', self.number_available_servers) 22 | self.notify_state_change('number_in_queue', self.number_in_queue) 23 | self.notify_state_change('number_served', self.number_served) 24 | 25 | def arrival(self): 26 | self.number_in_queue += 1 27 | self.notify_state_change('number_in_queue', self.number_in_queue) 28 | 29 | if (self.number_available_servers > 0): 30 | self.schedule('start_service', 0.0, priority=Priority.HIGH) 31 | 32 | def start_service(self): 33 | self.number_in_queue -= 1 34 | self.notify_state_change('number_in_queue', self.number_in_queue) 35 | 36 | self.number_available_servers -= 1 37 | self.notify_state_change('number_available_servers', self.number_available_servers) 38 | 39 | self.schedule('end_service', self.service_time_generator.generate()) 40 | 41 | def end_service(self): 42 | self.number_available_servers += 1 43 | self.notify_state_change('number_available_servers', self.number_available_servers) 44 | 45 | self.number_served += 1 46 | self.notify_state_change('number_served', self.number_served) 47 | 48 | if self.number_in_queue > 0: 49 | self.schedule('start_service', 0.0, priority=Priority.HIGH) 50 | -------------------------------------------------------------------------------- /simkit/examples/transferline.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import Priority 3 | from simkit.base import Entity 4 | from simkit.examples.arrivalprocess import ArrivalProcess 5 | from heapq import heappush 6 | from heapq import heappop 7 | 8 | class Job(Entity): 9 | 10 | def __init__(self): 11 | Entity.__init__(self, 'Job') 12 | self.total_delay_in_queue = 0.0 13 | self.time_in_system = 0.0 14 | 15 | def updateDelayInQueue(self): 16 | self.total_delay_in_queue += self.elapsed_time() 17 | 18 | class JobCreator(ArrivalProcess): 19 | def __init__(self, interarrival_time_generator): 20 | ArrivalProcess.__init__(self, interarrival_time_generator) 21 | 22 | def arrival(self): 23 | ArrivalProcess.arrival(self) 24 | self.schedule('job_arrival', 0.0, Job(), 0) 25 | 26 | class TransferLine(SimEntityBase): 27 | def __init__(self, number_stations, number_machines, service_time_generators): 28 | SimEntityBase.__init__(self) 29 | self.number_stations = number_stations 30 | self.service_time_generators = service_time_generators 31 | self.number_machines = number_machines 32 | self.queue = [] 33 | self.number_available_machines = [] 34 | self.validate() 35 | 36 | def validate(self): 37 | service_times_ok = len(self.service_time_generators) == self.number_stations 38 | number_machines_ok = len(self.number_machines) == self.number_stations 39 | if not service_times_ok or not number_machines_ok: 40 | raise ValueError('{ns:d} stations specified but {st:d} service times and {nm:d} machines'. \ 41 | format(ns=self.number_stations, st=len(self.service_time_generators), nm=len(self.number_machines))) 42 | 43 | def reset(self): 44 | self.queue.clear() 45 | self.number_available_machines.clear() 46 | 47 | def run(self): 48 | if self.number_stations > 0: 49 | self.schedule('init', 0.0, 0, priority=Priority.HIGHER) 50 | 51 | def init(self, station): 52 | self.queue.append([]) 53 | self.notify_indexed_state_change(station, 'queue', self.queue[station]) 54 | 55 | self.number_available_machines.append(self.number_machines[station]) 56 | self.notify_indexed_state_change(station, 'number_available_machines', self.number_available_machines[station]) 57 | 58 | if station < self.number_stations - 1: 59 | self.schedule('init', 0.0, station + 1, priority=Priority.HIGHER) 60 | 61 | def arrival(self, job, station): 62 | job.stamp_time() 63 | heappush(self.queue[station], job) 64 | self.notify_indexed_state_change(station, 'queue', self.queue[station]) 65 | 66 | if self.number_available_machines[station] > 0: 67 | self.schedule('start_processing', 0.0, station, priority=Priority.HIGH) 68 | 69 | def start_processing(self, station): 70 | job = heappop(self.queue[station]) 71 | self.notify_indexed_state_change(station, 'delay_in_queue', job.elapsed_time()) 72 | self.notify_indexed_state_change(station, 'queue', self.queue[station]) 73 | job.updateDelayInQueue() 74 | 75 | self.number_available_machines[station] -= 1 76 | self.notify_indexed_state_change(station, 'number_available_machines', self.number_available_machines[station]) 77 | 78 | self.schedule('end_processing', self.service_time_generators[station].generate(), job, station) 79 | 80 | def end_processing(self, job, station): 81 | self.number_available_machines[station] += 1 82 | self.notify_indexed_state_change(station, 'number_available_machines', self.number_available_machines[station]) 83 | 84 | self.notify_indexed_state_change(station, 'time_at_station', job.elapsed_time()) 85 | 86 | if len(self.queue[station]) > 0: 87 | self.schedule('start_processing', 0.0, station, priority=Priority.HIGH) 88 | 89 | if station < self.number_stations - 1: 90 | self.schedule('arrival', 0.0, job, station + 1) 91 | 92 | if (station == self.number_stations - 1): 93 | self.schedule('job_complete', 0.0, job) 94 | 95 | def job_complete(self, job): 96 | self.notify_state_change('total_delay_in_queue', job.total_delay_in_queue) 97 | self.notify_state_change('time_in_system', job.age()) -------------------------------------------------------------------------------- /simkit/examples/twocranesberth.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import Entity 3 | from simkit.base import Priority 4 | from simkit.examples.arrivalprocess import ArrivalProcess 5 | from math import nan 6 | from heapq import heappush 7 | from heapq import heappop 8 | 9 | class Ship (Entity): 10 | def __init__(self, unloading_time_generator): 11 | Entity.__init__(self) 12 | self.remaining_unloading_time = unloading_time_generator 13 | self.name = 'Ship' 14 | 15 | def credit_work(self, rate): 16 | """ 17 | Decrement remaining_unloading_time by the elapsed time * the rate 18 | :param rate: Rate at which credit_work was performed; must be > 0.0. 19 | """ 20 | if rate <= 0.0: 21 | raise ValueError('rate must be > 0.0: {rate}'.format(rate=rate)) 22 | self.remaining_unloading_time -= self.elapsed_time() * rate; 23 | 24 | def __repr__(self): 25 | return '{super} {remain:.4f}'.format(super=Entity.__repr__(self), remain=self.remaining_unloading_time) 26 | 27 | class ShipArrivalProcess(ArrivalProcess): 28 | def __init__(self, interarrival_time_generator, unloading_time_generator): 29 | ArrivalProcess.__init__(self, interarrival_time_generator) 30 | self.unloading_time_generator = unloading_time_generator 31 | 32 | def arrival(self): 33 | ArrivalProcess.arrival(self) 34 | ship = Ship(self.unloading_time_generator.generate()) 35 | self.schedule('ship_arrival', 0.0, ship) 36 | 37 | class TwoCranesBerth(SimEntityBase): 38 | def __init__(self): 39 | SimEntityBase.__init__(self) 40 | self.queue = [] 41 | self.berths = [] 42 | self.delay_in_queue = nan 43 | self.time_in_System = nan 44 | 45 | def reset(self): 46 | SimEntityBase.reset(self) 47 | self.queue.clear() 48 | self.berths.clear() 49 | self.delay_in_queue = nan 50 | self.time_in_System = nan 51 | 52 | def run(self): 53 | self.notify_state_change('queue', self.queue) 54 | self.notify_state_change('berths', self.berths) 55 | 56 | def ship_arrival(self, ship): 57 | ship.stamp_time() 58 | heappush(self.queue, ship) 59 | self.notify_state_change('queue', self.queue) 60 | 61 | if len(self.berths)== 0: 62 | self.schedule('start_unloading_two_cranes', 0.0, priority=Priority.HIGH) 63 | 64 | if len(self.berths)== 1: 65 | self.schedule('switch_to_one_crane', 0.0) 66 | 67 | def start_unloading_two_cranes(self): 68 | ship = heappop(self.queue) 69 | self.notify_state_change('queue', self.queue) 70 | 71 | self.delay_in_queue = ship.elapsed_time() 72 | self.notify_state_change('delay_in_queue', self.delay_in_queue) 73 | 74 | ship.stamp_time() 75 | heappush(self.berths, ship) 76 | self.notify_state_change('berths', self.berths) 77 | 78 | self.schedule('end_unloading_two_cranes', ship.remaining_unloading_time / 2) 79 | 80 | def end_unloading_two_cranes(self): 81 | ship = heappop(self.berths) 82 | # ship = self.berths.pop() 83 | self.notify_state_change('berths', self.berths) 84 | 85 | self.time_in_System = ship.age() 86 | self.notify_state_change('time_in_system', self.time_in_System) 87 | 88 | def switch_to_one_crane(self): 89 | ship = heappop(self.berths) 90 | # ship = self.berths[0] 91 | ship.credit_work(2) 92 | ship.stamp_time() 93 | heappush(self.berths, ship) 94 | 95 | self.cancel('end_unloading_two_cranes') 96 | 97 | self.schedule('start_unloading_one_crane', 0.0, priority=Priority.HIGH) 98 | 99 | self.schedule('end_unloading_one_crane', ship.remaining_unloading_time, ship) 100 | 101 | def start_unloading_one_crane(self): 102 | ship = heappop(self.queue) 103 | self.notify_state_change('queue', self.queue) 104 | 105 | self.delay_in_queue = ship.elapsed_time() 106 | self.notify_state_change('delay_in_queue', self.delay_in_queue) 107 | 108 | ship.stamp_time() 109 | 110 | heappush(self.berths, ship) 111 | # self.berths.append(ship) 112 | self.notify_state_change('berths', self.berths) 113 | 114 | self.schedule('end_unloading_one_crane', ship.remaining_unloading_time, ship) 115 | 116 | def end_unloading_one_crane(self, ship): 117 | self.berths.remove(ship) 118 | self.notify_state_change('berths', self.berths) 119 | 120 | self.time_in_System = ship.age() 121 | self.notify_state_change('time_in_system', self.time_in_System) 122 | 123 | if len(self.queue) == 0: 124 | self.schedule('switch_to_two_cranes', 0.0) 125 | 126 | if len(self.queue) > 0: 127 | self.schedule('start_unloading_one_crane', 0.0, priority=Priority.HIGH) 128 | 129 | def switch_to_two_cranes(self): 130 | ship = heappop(self.berths) 131 | ship.credit_work(1) 132 | ship.stamp_time() 133 | heappush(self.berths, ship) 134 | 135 | self.notify_state_change('in_berth', ship) 136 | 137 | self.cancel('end_unloading_one_crane', ship) 138 | 139 | self.schedule('end_unloading_two_cranes', ship.remaining_unloading_time / 2, priority=Priority.HIGH) 140 | 141 | -------------------------------------------------------------------------------- /simkit/quantiles.py: -------------------------------------------------------------------------------- 1 | from math import pi 2 | from math import sqrt 3 | from math import fabs 4 | from math import inf 5 | from math import log 6 | from math import tan 7 | from math import pow 8 | from math import exp 9 | from math import nan 10 | 11 | # Rather than have a dependence on bigger packages, for simple computations of confidence intervals and 12 | # hypothesis tests for means, the Normal and Student T quantiles only are implemented here. 13 | # For more extensive statistical actions use an appropriate package (like scipy) or connect to R 14 | from unicodedata import name 15 | 16 | A0 = 2.50662823884 17 | A1 = -18.61500062529 18 | A2 = 41.39119773534 19 | A3 = -25.44106049637 20 | B1 = -8.47351093090 21 | B2 = 23.08336743743 22 | B3 = -21.06224101826 23 | B4 = 3.13082909833 24 | C0 = -2.78718931138 25 | C1 = -2.29796479134 26 | C2 = 4.85014127135 27 | C3 = 2.32121276858 28 | D1 = 3.54388924762 29 | D2 = 1.63706781897 30 | SPLIT = 0.42 31 | 32 | # Based on Beasley & Springer, "Algorithm AS 111: The Percentage Points of the Normal Distribution", 33 | # Journal of the Royal Statistical Society. Series C(Applied Statistics) Vol. 26, No 1. 34 | def normal(p): 35 | if p < 0.0 or p > 1.0: 36 | raise ValueError('p must be in [0.0, 1.0]: ' + str(p)) 37 | if p == 0.0: 38 | quantile = -inf 39 | elif p == 1.0: 40 | quantile = inf 41 | elif p == 0.5: 42 | quantile = 0.0 43 | else: 44 | q = p - 0.5 45 | if (fabs(q) < SPLIT): 46 | r = q * q 47 | quantile = q * (((A3 * r + A2) * r + A1) * r + A0) \ 48 | / ((((B4 * r + B3) * r + B2) * r + B1) * r + 1.0) 49 | else: 50 | if q <= 0.0: 51 | r = p 52 | else: 53 | r = 1.0 - p 54 | r = sqrt(-log(r)) 55 | quantile = (((C3 * r + C2) * r + C1) * r + C0) \ 56 | / ((D2 * r + D1) * r + 1.0); 57 | if q < 0.0: 58 | quantile = -quantile 59 | return quantile 60 | 61 | 62 | # Based on Hill, G.W., "Algorithm 396 Student T Quantiles," Communications of the ACM, 63 | # Vol 13, No. 10, October 1970. 64 | # Returns nan if df = 0 65 | def student_t(p, df): 66 | if df < 0: 67 | raise ValueError('df must be > 0: ' + str(df)) 68 | if p < 0.0 or p > 1.0: 69 | raise ValueError('p must be >= 0: ' + str(p)) 70 | if df == 0: 71 | return nan 72 | p = 2.0 * p 73 | if p == 0.0: 74 | quantile = -inf 75 | elif p == 2.0: 76 | quantile = inf 77 | elif p == 1.0: 78 | quantile = 0.5 79 | elif df == 1: 80 | quantile = -1.0 / tan(p * 0.5 * pi) 81 | elif df == 2: 82 | quantile = sqrt(2.0 / (p * (2.0 - p)) - 2.0) 83 | else: 84 | a = 1.0 / (df - 0.5) 85 | b = 48.0 / (a * a) 86 | c = ((20700.0 * a / b - 98.0) * a - 16.0) * a + 96.3 87 | d = ((94.5 / (b + c) - 3.0) / b + 1.0) * sqrt(a * 0.5 * pi) * df 88 | x = d * p 89 | y = pow(x, 2.0 / df) 90 | 91 | if y > 0.05 + a: 92 | x = normal(0.5 * p) 93 | y = x * x 94 | if df < 5: 95 | c = c + 0.3 * (df - 4.5) * (x + 0.6) 96 | c = (((0.05 * d * x - 5.0) * x - 7.0) * x - 2.0) * x + b + c 97 | y = (((((0.4 * y + 6.3) * y + 36.0) * y + 94.5) / c - y - 3.0) / b + 1.0) * x 98 | y = a * y * y 99 | # This is the update from Hill, 1981 100 | if (y > 0.1): 101 | y = exp(y) - 1.0 102 | else: 103 | y = ((y + 4.0) * y + 112.0) * y * y / 24.0 + y 104 | else: 105 | y = ((1.0 / (((df + 6.0) / (df * y) - 0.089 * d - 8.222) * (df + 2.0) * 3.0) + 0.5 / \ 106 | (df + 4.0)) * y - 1.0) * (df + 1.0) / (df + 2.0) + 1.0 / y; 107 | quantile = sqrt(df * y) 108 | if (p < 0.5 and df != 1): 109 | quantile = -quantile 110 | return quantile -------------------------------------------------------------------------------- /simkit/rand.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from abc import abstractmethod 3 | from random import Random 4 | from math import log 5 | from math import nan 6 | from math import exp 7 | from math import sqrt 8 | from sys import modules 9 | 10 | class RandomVariate(ABC): 11 | baseRNG = Random(12345) 12 | 13 | @staticmethod 14 | def instance(name, module='simkit.rand', **kwds): 15 | """ 16 | 17 | :param name: The name of the random variate ('Exponential', 'Gamma', etc) 18 | :param module: The module it is in (default is 'simkit.rand' 19 | :param kwds: named parameters for the random variate class; specifics depend on the class 20 | or a dictionary with the RandomVariate parameters as key=value pairs 21 | :return: The desired instance of the random variate class 22 | """ 23 | clazz = getattr(modules[module],name) 24 | instance = clazz() 25 | if kwds.keys().__contains__('params'): 26 | params = kwds.get('params') 27 | else: 28 | params = kwds 29 | for keyword in params.keys(): 30 | if hasattr(instance, keyword): 31 | setattr(instance, keyword, params[keyword]) 32 | if hasattr(instance, 'normalize'): 33 | getattr(instance, 'normalize')() 34 | return instance 35 | 36 | def __init__(self): 37 | self.rng(RandomVariate.baseRNG) 38 | self.originalState = self.rng.getstate() 39 | 40 | @abstractmethod 41 | def generate(self): 42 | pass 43 | 44 | def rng(self, rng): 45 | self.rng = rng 46 | 47 | def seed(self, seed): 48 | self.rng.seed(seed) 49 | 50 | def reset(self): 51 | self.rng.setstate(self.originalState) 52 | 53 | class Exponential(RandomVariate): 54 | 55 | def __init__(self, mean=nan): 56 | """ 57 | 58 | :param mean: Mean of the Exponential random variates generated 59 | """ 60 | RandomVariate.__init__(self) 61 | self.__mean = mean 62 | 63 | @property 64 | def mean(self): 65 | return self.__mean 66 | 67 | @mean.setter 68 | def mean(self, mean): 69 | if mean <= 0: 70 | raise ValueError('mean must be > 0:') 71 | 72 | self.__mean = mean 73 | 74 | 75 | def generate(self): 76 | return -self.mean * log(self.rng.random()) 77 | 78 | def __repr__(self): 79 | return 'Exponential ({mean:.3f})'.format(mean=self.mean) 80 | 81 | class Gamma(RandomVariate): 82 | def __init__(self, alpha=nan, beta=nan): 83 | """ 84 | 85 | :param alpha: \u03B1 of the Gamma distribution (shape parameter) 86 | :param beta: \u0B32 of the Gamma distribution (scale parameter) 87 | """ 88 | RandomVariate.__init__(self) 89 | self.alpha = alpha 90 | self.beta = beta 91 | 92 | def generate(self): 93 | return self.baseRNG.gammavariate(self.alpha, self.beta) 94 | 95 | def __repr__(self): 96 | return 'Gamma ({alpha:.3f}, {beta:.3f})'.format(alpha=self.alpha, beta=self.beta) 97 | 98 | class Beta(RandomVariate): 99 | def __init__(self,alpha=nan, beta=nan, shift=0.0, scale=1.0): 100 | """ 101 | 102 | :param alpha: \u03B1 of the Beta distribution (shape parameter) 103 | :param beta: \u0B32 of the Beta distribution (shape parameter) 104 | """ 105 | RandomVariate.__init__(self) 106 | self.alpha = alpha 107 | self.beta = beta 108 | self.scale = scale 109 | self.shift = shift 110 | 111 | def generate(self): 112 | return self. shift + self.scale * self.rng.betavariate(self.alpha, self.beta) 113 | 114 | def __repr__(self): 115 | if self.scale == 1.0 and self.shift == 0.0: 116 | return 'Beta ({alpha:.3f}, {beta:.3f})'.format(alpha=self.alpha, beta=self.beta) 117 | else: 118 | return 'Beta ({alpha:.3f}, {beta:.3f}, {shift:.3f}, {scale:.3f})'.\ 119 | format(alpha=self.alpha, beta=self.beta, shift=self.shift, scale=self.scale) 120 | 121 | class Uniform(RandomVariate): 122 | def __init__(self, min=nan, max=nan): 123 | """ 124 | 125 | :param min: Minimum value 126 | :param max: Maximum value 127 | """ 128 | RandomVariate.__init__(self) 129 | self.min = min 130 | self.max = max 131 | 132 | def generate(self): 133 | return self.min + (self.max - self.min) * self.rng.random() 134 | 135 | def __repr__(self): 136 | return 'Uniform ({min:.3f}, {max:.3f})'.format(min=self.min, max=self.max) 137 | 138 | class Constant(RandomVariate): 139 | 140 | def __init__(self, value=nan): 141 | """ 142 | 143 | :param value: The one value that will always be generated 144 | """ 145 | self.value = value 146 | 147 | def __repr__(self): 148 | return 'Constant ({value:.3f})'.format(value=self.value) 149 | 150 | def generate(self): 151 | return self.value 152 | 153 | class Triangular(RandomVariate): 154 | 155 | def __init__(self, min=nan, mode=nan, max=nan): 156 | """ 157 | 158 | :param min: Minimum value of triangle 159 | :param mode: Mode (point) of triangle 160 | :param max: Maximum value of triangle 161 | """ 162 | RandomVariate.__init__(self) 163 | self.min = min 164 | self.mode = mode 165 | self.max = max 166 | 167 | def generate(self): 168 | return self.rng.triangular(self.min, self.max, self.mode) 169 | 170 | def __repr__(self): 171 | return 'Triangular ({min:.3f}, {max:.3f}, {mode:.3f})'.format(min=self.min, max=self.max, mode=self.mode) 172 | 173 | class Normal(RandomVariate): 174 | def __init__(self, mean=0.0, stdev=1.0): 175 | """ 176 | 177 | :param mean: \u03BC of Normal distribution (defaults to 0.0) 178 | :param stdev: \u03C3 of Normal distribution (defaults to 1.0) 179 | """ 180 | RandomVariate.__init__(self) 181 | self.mean = mean 182 | self.stdev = stdev 183 | 184 | def generate(self): 185 | return self.rng.gauss(self.mean, self.stdev) 186 | 187 | def __repr__(self): 188 | return 'Normal ({mean:.3f}, {stdev:.3f})'.format(mean=self.mean, stdev=self.stdev) 189 | 190 | class TruncatedNormal(Normal): 191 | def __init__(self, mean=0.0, stdev=1.0, trunc=0.0): 192 | Normal.__init__(self, mean, stdev) 193 | self.trunc = trunc 194 | 195 | def generate(self): 196 | x = Normal.generate(self) 197 | return max(x, self.trunc) 198 | 199 | def __repr__(self): 200 | return 'Truncated Normal ({mean:.3f}, {stdev:.3f} {trunc:.3f})'.format(mean=self.mean, stdev=self.stdev, trunc=self.trunc) 201 | 202 | class ResampledNormal(Normal): 203 | def __init__(self, mean=0.0, stdev=1.0, trunc=0.0): 204 | Normal.__init__(self, mean, stdev) 205 | self.trunc = trunc 206 | 207 | def generate(self): 208 | x = Normal.generate(self) 209 | while x < self.trunc: 210 | x = Normal.generate(self) 211 | return x 212 | 213 | def __repr__(self): 214 | return 'Resampled Normal ({mean:.3f}, {stdev:.3f} {trunc:.3f})'.format(mean=self.mean, stdev=self.stdev, trunc=self.trunc) 215 | 216 | 217 | class Weibull(RandomVariate): 218 | def __init__(self, shape=1, scale=1): 219 | RandomVariate.__init__(self) 220 | self.shape=shape 221 | self.scale = scale 222 | 223 | def generate(self): 224 | return self.scale * (-log(self.rng.random()))**(1.0 / self.shape) 225 | 226 | def __repr__(self): 227 | return "Weibull ({shape:.3f}, {scale:.3f})".format(shape=self.shape, scale=self.scale) 228 | 229 | class DiscreteUniform(RandomVariate): 230 | 231 | def __init__(self, min=nan, max=nan): 232 | """ 233 | 234 | :param min: Min value (should be int) 235 | :param max: Max value (should be int) 236 | """ 237 | RandomVariate.__init__(self) 238 | self.min = min 239 | self.max = max 240 | 241 | def __repr__(self): 242 | return 'Discrete Uniform ({min:d}, {max:d})'.format(min=self.min, max=self.max) 243 | 244 | def generate(self): 245 | return self.rng.randint(self.min, self.max) 246 | 247 | class Poisson(RandomVariate): 248 | def __init__(self, mean=nan): 249 | """ 250 | 251 | :param mean: Mean of the Poisson distribution 252 | """ 253 | RandomVariate.__init__(self) 254 | self.mean = mean 255 | 256 | def normalize(self): 257 | self.a = exp(-self.mean) 258 | self.sqrtmean = sqrt(self.mean) 259 | 260 | def generate(self): 261 | if self.mean < 100.0: 262 | x = 0 263 | y = self.rng.random() 264 | while y >= self.a: 265 | x += 1 266 | y *= self.rng.random() 267 | else: 268 | x = self.rng.normalvariate(self.mean, self.sqrtmean) + 0.5 269 | return x 270 | 271 | def __repr__(self): 272 | return 'Poisson ({mean:.3f})'.format(mean=self.mean) 273 | 274 | class Binomial(RandomVariate): 275 | def __init__(self, n=nan, p=nan): 276 | """ 277 | 278 | :param n: Maximum possible value of Binomial 279 | :param p: Probability of 'success' 280 | """ 281 | RandomVariate.__init__(self) 282 | self.n = n 283 | self.p = p 284 | 285 | def generate(self): 286 | x = 0 287 | for i in range(self.n): 288 | if self.rng.random() < self.p: 289 | x += 1 290 | return x 291 | 292 | def __repr__(self): 293 | return 'Binomial ({n:d}, {p:.3f})'.format(n=self.n,p=self.p) 294 | 295 | class Geometric(RandomVariate): 296 | 297 | def __init__(self, p=nan): 298 | """ 299 | 300 | :param p: Probability if 'success' for Geometric distribution 301 | """ 302 | RandomVariate.__init__(self) 303 | self.p = p 304 | 305 | def normalize(self): 306 | self.multiplier = 1.0 / log(1.0 - self.p) 307 | 308 | def generate(self): 309 | return round(log(self.rng.random()) * self.multiplier) 310 | 311 | def __repr__(self): 312 | return 'Geormetric: {p:f}'.format(p=self.p) 313 | 314 | class Discrete(RandomVariate): 315 | 316 | def __init__(self, values=[], frequencies=[]): 317 | """ 318 | 319 | :param values: Possible values to be generated 320 | :param frequencies: Frequencies of respective values; must be non-negative, but need not sum to 1.0 321 | """ 322 | RandomVariate.__init__(self) 323 | self.values = values 324 | self.frequencies = frequencies 325 | if len(values) != len(frequencies): 326 | raise ValueError('values and frequencies must have the same length: {v:d} {f:d}'.\ 327 | format(v=len(values), f=len(frequencies))) 328 | 329 | def normalize(self): 330 | """ 331 | Computes the probabilities and cumulative probabilities based on the passed-in frequencies 332 | """ 333 | cumulative = 0.0 334 | for freq in self.frequencies: 335 | cumulative += freq 336 | self.probabilities = [] 337 | for freq in self.frequencies: 338 | self.probabilities.append(freq / cumulative) 339 | self.cdf = [] 340 | self.cdf.append(self.probabilities[0]) 341 | for i in range(1,len(self.probabilities)): 342 | self.cdf.append(self.cdf[i - 1] + self.probabilities[i]) 343 | 344 | def generate(self): 345 | u = self.rng.random() 346 | index = 0 347 | while u > self.cdf[index] and index < len(self.probabilities) - 1: 348 | index += 1 349 | return self.values[index] 350 | 351 | def __repr__(self): 352 | return 'Discrete (' + str(self.values) + '=>' +str(self.probabilities) + ')' 353 | 354 | 355 | 356 | -------------------------------------------------------------------------------- /simkit/simutil.py: -------------------------------------------------------------------------------- 1 | from simkit.base import StateChangeListener 2 | 3 | class SimpleStateChangeDumper(StateChangeListener): 4 | 5 | def state_change(self, state_change_event): 6 | print(str(state_change_event)) -------------------------------------------------------------------------------- /simkit/stats.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod 2 | from math import inf 3 | from math import nan 4 | from math import sqrt 5 | from math import isnan 6 | from math import isnan 7 | 8 | from simkit.base import StateChangeListener, SimEntityBase 9 | from simkit.base import EventList 10 | from simkit.base import IndexedStateChangeEvent 11 | from simkit.quantiles import student_t 12 | 13 | class SimpleStatsBase(StateChangeListener): 14 | 15 | def __init__(self, name="default"): 16 | self.min = inf 17 | self.max = -inf 18 | self.count = 0 19 | self.mean = 0.0 20 | self.diff = 0.0 21 | self.variance = 0.0 22 | self.stdev = 0.0 23 | self.name = name 24 | 25 | def reset(self): 26 | self.min = inf 27 | self.max = -inf 28 | self.count = 0 29 | self.mean = 0.0 30 | self.diff = 0.0 31 | self.variance = 0.0 32 | self.stdev = 0.0 33 | 34 | @abstractmethod 35 | def new_observation(self, x): 36 | self.count += 1 37 | if x < self.min: 38 | self.min = x 39 | if x > self.max: 40 | self.max = x 41 | 42 | def __repr__(self): 43 | return '{name}: {count:,d} {min:,.4f} {max:,.4f} {mean:,.4f} {var:,.4f} {stdev:,.4f}'.\ 44 | format(name=self.name, count=self.count, min=self.min, max=self.max, mean=self.mean,var=self.variance, stdev=self.stdev) 45 | 46 | def state_change(self, state_change_event): 47 | if state_change_event.name == self.name: 48 | self.new_observation(state_change_event.value) 49 | 50 | def halfwidth(self, p): 51 | if self.count > 1: 52 | quantile = student_t(p, self.count - 1) 53 | else: 54 | quantile = inf 55 | return self.stdev / self.count * quantile 56 | 57 | class SimpleStatsTally(SimpleStatsBase): 58 | 59 | def __init__(self, name='default'): 60 | SimpleStatsBase.__init__(self, name) 61 | self.reset() 62 | 63 | def new_observation(self, x): 64 | if isnan(x): 65 | return 66 | SimpleStatsBase.new_observation(self, x) 67 | self.diff = x - self.mean 68 | self.mean += self.diff / self.count 69 | if self.count == 1: 70 | self.variance += 0.0 71 | else: 72 | self.variance += self.diff * self.diff / self.count - 1.0 / (self.count - 1) * self.variance 73 | self.stdev = sqrt(self.variance) 74 | 75 | def reset(self): 76 | SimpleStatsBase.reset(self) 77 | self.diff = 0.0 78 | 79 | class SimpleStatsTimeVarying(SimpleStatsBase): 80 | 81 | def __init__(self, name='default'): 82 | SimpleStatsBase.__init__(self, name) 83 | self.startTime = EventList.simtime 84 | self.reset() 85 | self.last_value = nan 86 | 87 | def new_observation(self, x): 88 | if isnan(x): 89 | return; 90 | SimpleStatsBase.new_observation(self, x) 91 | if self.count == 1: 92 | self.mean = self.diff 93 | self.variance = 0.0 94 | elif EventList.simtime > self.last_time: 95 | factor = 1.0 - (self.last_time - self.startTime) / (EventList.simtime - self.startTime) 96 | self.mean += self.diff * factor 97 | self.variance += factor * ((1.0 - factor) * self.diff * self.diff -self.variance) 98 | self.diff = x - self.mean 99 | self.last_time = EventList.simtime 100 | self.stdev = sqrt(self.variance) 101 | 102 | def reset(self): 103 | SimpleStatsBase.reset(self) 104 | self.diff = 0.0 105 | self.last_time = 0.0 106 | self.last_value = nan 107 | 108 | def time_varying_mean(self): 109 | self.new_observation(self.last_value) 110 | return self.mean 111 | 112 | def time_varying_variance(self): 113 | self.new_observation(self.last_value) 114 | return self.variance 115 | 116 | def time_varying_stdev(self): 117 | return sqrt(self.time_varying_variance()) 118 | 119 | def __repr__(self): 120 | self.new_observation(self.last_value) 121 | return '{name}: {count:,d} {min:,.4f} {max:,.4f} {mean:,.4f} {var:,.4f} {stdev:,.4f}'.\ 122 | format(name=self.name, count=self.count, min=self.min, max=self.max, mean=self.mean,var=self.variance, stdev=self.stdev) 123 | 124 | class CollectionSizeTimeVarying(SimpleStatsTimeVarying): 125 | 126 | def __init__(self, name='default'): 127 | SimpleStatsTimeVarying.__init__(self, name) 128 | self.last_value = [] 129 | 130 | def reset(self): 131 | SimpleStatsTimeVarying.reset(self) 132 | self.last_value = [] 133 | 134 | def new_observation(self, q): 135 | SimpleStatsTimeVarying.new_observation(self, q.__len__()) 136 | 137 | class IndexedSimpleStats(): 138 | 139 | def __init__(self, name='default'): 140 | self.stats = {} 141 | self.name = name 142 | 143 | def reset(self): 144 | self.stats = {} 145 | 146 | def state_change(self, stateChangeEvent): 147 | if isinstance(stateChangeEvent, IndexedStateChangeEvent) and stateChangeEvent.name == self.name: 148 | self.new_observation(stateChangeEvent.index, stateChangeEvent.value) 149 | 150 | 151 | @abstractmethod 152 | def new_observation(self, index, x): 153 | pass 154 | 155 | def mean(self, index): 156 | if index in self.stats: 157 | return self.stats[index].mean 158 | else: 159 | return nan 160 | 161 | def variance(self, index): 162 | if index in self.stats: 163 | return self.stats[index].variance 164 | else: 165 | return nan 166 | 167 | def stdev(self, index): 168 | if index in self.stats: 169 | return self.stats[index].stdev 170 | else: 171 | return nan 172 | 173 | def count(self, index): 174 | if index in self.stats: 175 | return self.stats[index].count 176 | else: 177 | return nan 178 | 179 | def min(self, index): 180 | if index in self.stats: 181 | return self.stats[index].min 182 | else: 183 | return nan 184 | 185 | def max(self, index): 186 | if index in self.stats: 187 | return self.stats[index].max 188 | else: 189 | return nan 190 | 191 | def __repr__(self): 192 | ret = self.name 193 | keys = sorted(self.stats.keys()) 194 | for index in keys: 195 | ret += '\n{stat}'.format(stat=self.stats[index]) 196 | return ret 197 | 198 | class IndexedSimpleStatsTally(IndexedSimpleStats): 199 | 200 | def __init__(self, name='default'): 201 | IndexedSimpleStats.__init__(self, name) 202 | 203 | def new_observation(self, index, x): 204 | if isnan(x): 205 | return 206 | if not index in self.stats: 207 | self.stats[index] = SimpleStatsTally('{name}[{index:d}]'.format(name=self.name, index=index)) 208 | self.stats[index].new_observation(x) 209 | 210 | class IndexedSimpleStatsTimeVarying(IndexedSimpleStats): 211 | 212 | def __init__(self, name='default'): 213 | IndexedSimpleStats.__init__(self, name) 214 | 215 | def new_observation(self, index, x): 216 | if isnan(x): 217 | return 218 | if not index in self.stats: 219 | self.stats[index] = SimpleStatsTimeVarying('{name}[{index:d}]'.format(name=self.name, index=index)) 220 | self.stats[index].new_observation(x) 221 | 222 | def time_varying_mean(self, index): 223 | if index in self.stats: 224 | return self.stats[index].time_varying_mean() 225 | else: 226 | return nan 227 | 228 | class IndexedCollectionSizeTimeVaryingStat(IndexedSimpleStats): 229 | 230 | def __int__(self,name='default'): 231 | IndexedSimpleStatsTally.__init__(self) 232 | 233 | def new_observation(self, index, x): 234 | if not index in self.stats: 235 | self.stats[index] = CollectionSizeTimeVarying('{name}[{index:d}]'.format(name=self.name, index=index)) 236 | self.stats[index].new_observation(x) 237 | 238 | def time_varying_mean(self, index): 239 | if index in self.stats: 240 | return self.stats[index].time_varying_mean() 241 | else: 242 | return nan 243 | 244 | class TruncatingSimpleStatsTally(SimpleStatsTally): 245 | 246 | def __init__(self, name='default', truncation_point=0): 247 | SimpleStatsTally.__init__(self, name) 248 | self.truncated = None 249 | if truncation_point < 0: 250 | raise ValueError('truncation_point must be \u2265 0: {tp:d}'.format(tp=truncation_point)) 251 | self.truncation_point = truncation_point 252 | 253 | def reset(self): 254 | SimpleStatsTally.reset(self) 255 | self.truncated = False 256 | 257 | def new_observation(self, x): 258 | if isnan(x): 259 | return 260 | SimpleStatsTally.new_observation(self, x) 261 | if not self.truncated and self.count >= self.truncation_point: 262 | self.reset() 263 | self.truncated = True 264 | 265 | def __repr__(self): 266 | return SimpleStatsTally.__repr__(self) + " [{tp:,d}]".format(tp=self.truncation_point) 267 | 268 | class TruncatingSimpleStatsTimeVarying(SimpleStatsTimeVarying): 269 | 270 | def __init__(self, truncation_point, name='default'): 271 | SimpleStatsTimeVarying.__init__(self, name) 272 | self.truncation_point = truncation_point 273 | if truncation_point < 0.0: 274 | raise ValueError('truncation_point must be \u2265 0: {tp:,.f}'.format(tp=truncation_point)) 275 | self.truncated = None 276 | self.reset() 277 | self.truncate = TruncatingSimpleStatsTimeVarying.Truncate() 278 | self.truncate.truncation_point = self.truncation_point 279 | self.truncate.outer = self 280 | 281 | def truncation_reset(self): 282 | last_value = self.last_value 283 | SimpleStatsTimeVarying.reset(self) 284 | self.lastTime = EventList.simtime 285 | self.truncated = False 286 | self.last_value = last_value 287 | 288 | def new_observation(self, x): 289 | if isnan(x): 290 | return 291 | SimpleStatsTimeVarying.new_observation(self, x) 292 | 293 | class Truncate(SimEntityBase): 294 | 295 | def __int__(self): 296 | SimEntityBase.__init__(self) 297 | self.outer = None 298 | self.truncation_point = nan 299 | 300 | def run(self): 301 | self.schedule('truncate', self.outer.truncation_point) 302 | 303 | def truncate(self): 304 | self.outer.truncation_reset() 305 | 306 | class SimpleStatsTallyRawData (SimpleStatsTally): 307 | def __init__(self, name='default'): 308 | SimpleStatsTally.__init__(self, name) 309 | self.raw_data = [] 310 | 311 | def reset(self): 312 | SimpleStatsTally.reset(self) 313 | # self.raw_data.clear() 314 | 315 | def new_observation(self, x): 316 | SimpleStatsTally.new_observation(self, x) 317 | self.raw_data.append(x) 318 | 319 | 320 | 321 | 322 | -------------------------------------------------------------------------------- /simkit/test/TestAdapter.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.arrivalprocess import EntityCreator 2 | from simkit.examples.entityserver import EntityServer 3 | from simkit.base import EventList 4 | from simkit.rand import RandomVariate 5 | from simkit.base import Adapter 6 | 7 | entity_creator = EntityCreator(RandomVariate.instance('Constant', value=2.3)) 8 | entity_server = EntityServer(1, RandomVariate.instance('Constant', value=2.2)) 9 | adapter = Adapter("entity_arrival", "arrival") 10 | adapter.connect(entity_creator, entity_server) 11 | 12 | 13 | # simEvent = entity_creator.waitDelay('Foo', 1.2, Priority.HIGH, Entity()) 14 | # print(simEvent) 15 | # print(simEvent.id) 16 | # copy = simEvent.copy() 17 | # print(copy) 18 | # print(copy.id) 19 | 20 | EventList.stop_on_event(5, 'start_service') 21 | EventList.verbose = True 22 | 23 | EventList.reset() 24 | EventList.start_simulation() -------------------------------------------------------------------------------- /simkit/test/TestArrivalProcess.py: -------------------------------------------------------------------------------- 1 | from simkit.rand import Exponential 2 | from simkit.examples.arrivalprocess import ArrivalProcess 3 | from simkit.examples.arrivalprocess import EntityCreator 4 | from simkit.base import EventList 5 | from simkit.simutil import SimpleStateChangeDumper 6 | 7 | if __name__=="__main__": 8 | generator = Exponential(1.7) 9 | arrival_process = ArrivalProcess(generator) 10 | print(arrival_process) 11 | 12 | print(type(arrival_process)) 13 | 14 | dumper = SimpleStateChangeDumper() 15 | arrival_process.add_state_change_listener(dumper) 16 | 17 | EventList.stop_at_time(100.0) 18 | 19 | EventList.verbose = True 20 | 21 | EventList.reset() 22 | EventList.start_simulation() 23 | 24 | EventList.cold_reset() 25 | EventList.stop_on_event(10, 'entity_arrival') 26 | 27 | entityCreator = EntityCreator(generator) 28 | 29 | print("With EntityCreator") 30 | EventList.reset() 31 | EventList.start_simulation() 32 | 33 | # This should throw a ValueError 34 | # arrival_process.waitDelay('Foo', -.001) -------------------------------------------------------------------------------- /simkit/test/TestBatchArrivals.py: -------------------------------------------------------------------------------- 1 | from simkit.rand import RandomVariate 2 | from simkit.base import EventList 3 | from simkit.base import Adapter 4 | from simkit.simutil import SimpleStateChangeDumper 5 | from simkit.examples.arrivalprocess import BatchArrivalProcess 6 | from simkit.examples.simpleserver import SimpleServer 7 | 8 | interarrival_time_generator = RandomVariate.instance('Exponential', mean=2.5) 9 | batch_generator = RandomVariate.instance('Discrete', values=[1, 2, 3, 4, 5], \ 10 | frequencies=[20, 30, 40, 50, 60]) 11 | batch_arrival_process = BatchArrivalProcess(interarrival_time_generator, batch_generator) 12 | print(batch_arrival_process.describe()) 13 | 14 | number_servers = 1 15 | service_time_generator = RandomVariate.instance('Gamma', alpha=1.2, beta = 2.3) 16 | simple_server = SimpleServer(number_servers, service_time_generator) 17 | print(simple_server.describe()) 18 | 19 | adapter = Adapter('arrival1', 'arrival') 20 | adapter.connect(batch_arrival_process, simple_server) 21 | 22 | batch_arrival_process.add_state_change_listener(SimpleStateChangeDumper()) 23 | simple_server.add_state_change_listener(SimpleStateChangeDumper()) 24 | 25 | stopTime = 20.0 26 | 27 | EventList.verbose = True 28 | EventList.stop_at_time(stopTime) 29 | EventList.stop_on_event(10, 'arrival') 30 | 31 | EventList.reset() 32 | EventList.start_simulation() -------------------------------------------------------------------------------- /simkit/test/TestCancelled.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import EventList 3 | from simkit.base import Priority 4 | 5 | class TestCancelled(SimEntityBase): 6 | 7 | def run(self): 8 | self.schedule('q', 1.0) 9 | self.schedule('b', 0.5) 10 | self.schedule('c', 2.0, 3) 11 | self.schedule('c', 1.5, 2) 12 | self.schedule('d', 3.4, 1, 2, 3) 13 | self.schedule('d', 3.4, 1, 2, 4) 14 | 15 | def b(self): 16 | print('In b()!') 17 | self.cancel('a') 18 | self.cancel('c', 3) 19 | 20 | def a(self): 21 | print('In a()!') 22 | 23 | def c(self, i): 24 | print('In c(): i = ' + str(i)) 25 | self.cancel('d', 1, 2, 3) 26 | 27 | def d(self, x, y, z): 28 | print('In D: args = (' + str(x) + ',' + str(y) + ',' + str(z) + ')') 29 | 30 | if __name__=='__main__': 31 | test = TestCancelled() 32 | 33 | EventList.verbose = True 34 | 35 | EventList.reset() 36 | EventList.start_simulation() 37 | 38 | -------------------------------------------------------------------------------- /simkit/test/TestConfidenceInterval.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.entitycreator import EntityCreator 2 | from simkit.examples.entityserver import EntityServer 3 | from simkit.stats import SimpleStatsTally 4 | from simkit.stats import CollectionSizeTimeVarying 5 | from simkit.rand import RandomVariate 6 | from simkit.base import EventList 7 | from time import time 8 | 9 | interarrival_time_generator = RandomVariate.instance('Uniform', min=0.9, max=2.2) 10 | entity_creator = EntityCreator(interarrival_time_generator) 11 | print(entity_creator.describe()) 12 | 13 | total_number_servers=2 14 | service_time_generator = RandomVariate.instance('Gamma', alpha=1.7, beta=1.8) 15 | entity_server = EntityServer(total_number_servers, service_time_generator) 16 | print(entity_server.describe()) 17 | 18 | entity_creator.add_sim_event_listener(entity_server) 19 | 20 | inner_delay_in_queue_stat = SimpleStatsTally("delay_in_queue") 21 | entity_server.add_state_change_listener(inner_delay_in_queue_stat) 22 | 23 | inner_number_in_queue_stat = CollectionSizeTimeVarying('queue') 24 | entity_server.add_state_change_listener(inner_number_in_queue_stat) 25 | 26 | outer_number_in_queue_stat = SimpleStatsTally('outer_number_in_queue') 27 | 28 | outer_delay_in_queue_stat = SimpleStatsTally('mean_delay_in_queue') 29 | 30 | # print(getattr(entity_server, 'number_servers')) 31 | 32 | runtime = 800.0 33 | p = 0.975 34 | numberReps = 100 35 | 36 | EventList.stop_at_time(runtime) 37 | 38 | start = time() 39 | for rep in range(numberReps): 40 | EventList.reset() 41 | inner_delay_in_queue_stat.reset() 42 | inner_number_in_queue_stat.reset() 43 | EventList.start_simulation() 44 | outer_delay_in_queue_stat.new_observation(inner_delay_in_queue_stat.mean) 45 | outer_number_in_queue_stat.new_observation(inner_number_in_queue_stat.mean) 46 | end = time() 47 | elapsed = end-start 48 | print('\n{reps:d} replications of length {runtime:,.1f} took {time:,.4f} sec'.format(reps=numberReps,runtime=runtime, time=elapsed)) 49 | print('95% CI for number in queue: {mean:,.4f} \u00B1 {halfwidth:,.4f}'.format(mean=outer_number_in_queue_stat.mean, halfwidth=outer_number_in_queue_stat.halfwidth(p))) 50 | print('95% CI for delay in queue: {mean:,.4f} \u00B1 {halfwidth:,.4f}'.format(mean=outer_delay_in_queue_stat.mean, halfwidth=outer_delay_in_queue_stat.halfwidth(p))) 51 | 52 | 53 | -------------------------------------------------------------------------------- /simkit/test/TestDiscreteVariate.py: -------------------------------------------------------------------------------- 1 | from simkit.rand import RandomVariate 2 | from simkit.rand import Discrete 3 | 4 | rv = RandomVariate.instance('Discrete', values=[1, 2, 3, 4, 5], frequencies=[20, 30, 40, 50, 60]) 5 | 6 | print(rv) 7 | 8 | counts={} 9 | 10 | number = 1000000 11 | for i in range(number): 12 | x = rv.generate 13 | if counts.keys().__contains__(x): 14 | counts[x] = counts[x] + 1 15 | else: 16 | counts[x] = 1 17 | 18 | print(counts) 19 | 20 | for x in sorted(counts.keys()): 21 | counts[x] = counts[x] / number 22 | print('{x:d} = {p:.4f}'.format(x=x, p=counts[x])) 23 | 24 | counts.clear() 25 | rv = RandomVariate.instance('Discrete', values=['one', 'two', 'three', 'four', 'fiver'], frequencies=[20, 30, 40, 50, 60]) 26 | for i in range(number): 27 | x = rv.generate 28 | if counts.keys().__contains__(x): 29 | counts[x] = counts[x] + 1 30 | else: 31 | counts[x] = 1 32 | 33 | print(counts) 34 | 35 | for x in sorted(counts.keys()): 36 | counts[x] = counts[x] / number 37 | print('{x:s} = {p:.4f}'.format(x=x, p=counts[x])) 38 | 39 | counts.clear() 40 | rv = RandomVariate.instance('DiscreteUniform', min=-3, max=5) 41 | print(rv) 42 | for i in range(number): 43 | x = rv.generate 44 | if counts.keys().__contains__(x): 45 | counts[x] = counts[x] + 1 46 | else: 47 | counts[x] = 1 48 | 49 | print(counts) 50 | 51 | for x in sorted(counts.keys()): 52 | counts[x] = counts[x] / number 53 | print('{x:d} = {p:.4f}'.format(x=x, p=counts[x])) 54 | -------------------------------------------------------------------------------- /simkit/test/TestEntityServer.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.entityserver import EntityServer 2 | from simkit.rand import Exponential 3 | from simkit.examples.entitycreator import EntityCreator 4 | from simkit.base import EventList 5 | from simkit.simutil import SimpleStateChangeDumper 6 | from simkit.stats import SimpleStatsTally 7 | from simkit.stats import SimpleStatsTimeVarying 8 | 9 | serviceMean = 1.3 10 | numberServers = 1 11 | generator = Exponential(serviceMean) 12 | entityServer = EntityServer(numberServers, generator) 13 | 14 | print(entityServer) 15 | 16 | interarrivalMean = 2.0 17 | interarrival = Exponential(interarrivalMean) 18 | entityCreator = EntityCreator(interarrival) 19 | print (entityCreator) 20 | print (entityCreator.interarrival_time_generator) 21 | 22 | entityCreator.add_sim_event_listener(entityServer) 23 | 24 | dumper = SimpleStateChangeDumper() 25 | # entity_server.add_state_change_listener(dumper) 26 | 27 | delayInQueueStat = SimpleStatsTally("delayInQueue") 28 | entityServer.add_state_change_listener(delayInQueueStat) 29 | 30 | timeInSystemStat = SimpleStatsTally('time_in_system') 31 | entityServer.add_state_change_listener(timeInSystemStat) 32 | 33 | numberInQueueStat = SimpleStatsTimeVarying("number_in_queue") 34 | entityServer.add_state_change_listener(numberInQueueStat) 35 | # queue = [] 36 | # heappush(queue, Entity() ) 37 | # heappush(queue, Entity() ) 38 | # print(queue) 39 | 40 | expected = (serviceMean * interarrivalMean) / (interarrivalMean -serviceMean) 41 | print('expected avg time_in_system: {avg:.4f}'.format(avg=expected)) 42 | print('expected avg delayInQueue: {avg:.4f}'.format( avg=(expected * serviceMean / interarrivalMean))) 43 | 44 | EventList.stop_at_time(100000.0) 45 | EventList.verbose = False 46 | 47 | EventList.reset() 48 | EventList.start_simulation() 49 | 50 | print('Simulation ended at time ' + str(EventList.simtime)) 51 | 52 | print(timeInSystemStat) 53 | print(delayInQueueStat) 54 | print(numberInQueueStat) -------------------------------------------------------------------------------- /simkit/test/TestIndexedStateChangeEvent.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import EventList 3 | from simkit.base import Priority 4 | from simkit.simutil import SimpleStateChangeDumper 5 | 6 | class TestIndexedStateChange(SimEntityBase): 7 | def __init__(self, number): 8 | SimEntityBase.__init__(self) 9 | self.number = number 10 | 11 | def run(self): 12 | self.schedule('init', 0.0, 0) 13 | 14 | def init(self, i): 15 | self.notify_indexed_state_change(i, 'foo', i) 16 | if i < self.number - 1: 17 | self.schedule('init', 0.0, i+1) 18 | 19 | if __name__=="__main__": 20 | test = TestIndexedStateChange(4) 21 | test.add_state_change_listener(SimpleStateChangeDumper()) 22 | 23 | EventList.verbose = True 24 | 25 | EventList.reset() 26 | EventList.start_simulation() 27 | 28 | -------------------------------------------------------------------------------- /simkit/test/TestIndexedStats.py: -------------------------------------------------------------------------------- 1 | from simkit.stats import IndexedSimpleStatsTally 2 | from simkit.base import IndexedStateChangeEvent 3 | from simkit.base import SimEntityBase 4 | from simkit.rand import RandomVariate 5 | 6 | source = SimEntityBase() 7 | print(source.describe()) 8 | 9 | name = 'foo' 10 | rv = RandomVariate.instance('Exponential', mean=2.3) 11 | 12 | stats = IndexedSimpleStatsTally(name) 13 | for i in range(1000): 14 | for j in range(4): 15 | event = IndexedStateChangeEvent(j, source, name, rv.generate()) 16 | stats.state_change(event) 17 | 18 | print(stats) 19 | -------------------------------------------------------------------------------- /simkit/test/TestNaNObservations.py: -------------------------------------------------------------------------------- 1 | from simkit.stats import SimpleStatsTally 2 | from math import nan 3 | 4 | sst = SimpleStatsTally("test"); 5 | sst.new_observation(nan) 6 | 7 | print(sst) 8 | 9 | for x in range(1,11): 10 | sst.new_observation(x) 11 | sst.new_observation(nan) 12 | 13 | print(sst) -------------------------------------------------------------------------------- /simkit/test/TestNanData.py: -------------------------------------------------------------------------------- 1 | from simkit.stats import SimpleStatsTally 2 | from math import nan 3 | 4 | simple_stats_tally = SimpleStatsTally() 5 | simple_stats_tally.new_observation(nan) 6 | simple_stats_tally.new_observation(1) 7 | simple_stats_tally.new_observation(2) 8 | print(simple_stats_tally) 9 | -------------------------------------------------------------------------------- /simkit/test/TestPersistent.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.arrivalprocess import ArrivalProcess 2 | from simkit.rand import RandomVariate 3 | from simkit.base import EventList 4 | from inspect import getmembers 5 | arrivalProcess = ArrivalProcess(RandomVariate.instance('Constant', value=3.4)) 6 | print(arrivalProcess.describe()) 7 | 8 | print(EventList.sim_entities) 9 | arrivalProcess.persistent = False 10 | EventList.reset() 11 | print(EventList.sim_entities) 12 | 13 | print(getmembers(arrivalProcess)) -------------------------------------------------------------------------------- /simkit/test/TestQuantiles.py: -------------------------------------------------------------------------------- 1 | from simkit.quantiles import normal 2 | from simkit.quantiles import student_t 3 | 4 | p = 0.0 5 | # print('Normal quantiles:') 6 | # while p < 1.0: 7 | # print('{p:,.3f}\t{q:,.8f}'.format(p=p, q=NormalQuantile.quantile(p))) 8 | # p += 0.005 9 | # print(NormalQuantile.quantile(-.2)) 10 | # print(NormalQuantile.quantile(1.1)) 11 | print('StudentT quantiles:') 12 | 13 | p = 0.005 14 | p = 1-p 15 | print('p={p:.3f}'.format(p=p)) 16 | for df in range(100): 17 | print(str(df) + '\t' + str(student_t(p, df))) 18 | -------------------------------------------------------------------------------- /simkit/test/TestRandomVariate.py: -------------------------------------------------------------------------------- 1 | from simkit.rand import RandomVariate 2 | from simkit.stats import SimpleStatsTally 3 | 4 | if __name__ == '__main__': 5 | rv = RandomVariate.instance('Exponential', mean=1) 6 | print(rv) 7 | 8 | total = 0.0 9 | n = 1000000 10 | for i in range(n): 11 | total += rv.generate() 12 | # print(rv.generate()) 13 | print(total / n) 14 | 15 | print() 16 | rv.seed(2468) 17 | for i in range(1, 5): 18 | print(rv.generate) 19 | 20 | rv2 = RandomVariate.instance('Exponential', 'simkit.rand', mean=1.0 + i / 10) 21 | rv2.mean = 1.0 + i / 10 22 | print(rv2) 23 | 24 | # for module in modules: 25 | # print(module) 26 | rv3 = RandomVariate.instance('Constant', 'simkit.rand', value=5.4); 27 | print(rv3) 28 | for i in range(4): 29 | print(rv3.generate) 30 | 31 | rv4 = RandomVariate.instance('Beta', alpha=2, beta=3) 32 | print(rv4) 33 | 34 | rv4 = RandomVariate.instance('Beta', alpha=3, beta=4, scale=5) 35 | print(rv4) 36 | 37 | rv4 = RandomVariate.instance('Beta', alpha=3, beta=4, scale=5, shift=-3) 38 | print(rv4) 39 | 40 | rv4 = RandomVariate.instance('Beta', params={'alpha':1.2, 'beta':3.44}) 41 | print(rv4) 42 | 43 | rv5 = RandomVariate.instance('Discrete', values=[1, 2, 3, 4], frequencies=[10, 20, 30, 40]) 44 | print(rv5) 45 | 46 | rv5 = RandomVariate.instance('Discrete', values=[4.0, 3.0, 2.0, 1.0], frequencies=[100, 200, 300, 400]) 47 | print(rv5) 48 | 49 | rv6 = RandomVariate.instance('Poisson', mean=2.3) 50 | print(rv6) 51 | number = 1000000 52 | sst = SimpleStatsTally(str(rv6)) 53 | for i in range(number): 54 | sst.new_observation(rv6.generate()) 55 | print(sst) 56 | 57 | rv7 = RandomVariate.instance('Binomial', n=20, p=0.3) 58 | sst = SimpleStatsTally(str(rv7)) 59 | for i in range(number): 60 | sst.new_observation(rv7.generate()) 61 | print(sst) 62 | 63 | rv8 = RandomVariate.instance('Uniform', min=0.9, max=2.2) 64 | sst=SimpleStatsTally(str(rv8)) 65 | for i in range(number): 66 | sst.new_observation(rv8.generate()) 67 | print(sst) 68 | 69 | rv4.reset() 70 | first = [] 71 | for i in range(10): 72 | first.append(rv4.generate) 73 | 74 | second=[] 75 | rv4.reset() 76 | for i in range(10): 77 | second.append(rv4.generate()) 78 | 79 | print(first == second) 80 | 81 | rv9 = RandomVariate.instance('Normal', mean=-2, stdev=4.5) 82 | sst9 = SimpleStatsTally(str(rv9)) 83 | 84 | for i in range(10000): 85 | x = rv9.generate() 86 | sst9.new_observation(x) 87 | print(sst9) 88 | 89 | 90 | rv10 = RandomVariate.instance("Weibull", shape=4, scale=2) 91 | sst10 = SimpleStatsTally(str(rv10)) 92 | 93 | for i in range(10000): 94 | sst10.new_observation(rv10.generate()) 95 | print(sst10) 96 | 97 | rv11 = RandomVariate.instance("TruncatedNormal", mean=1.2, stdev=0.75, trunc = 1.2) 98 | sst11 = SimpleStatsTally(str(rv11)) 99 | for i in range(10000): 100 | sst11.new_observation(rv11.generate()) 101 | print(sst11) 102 | 103 | rv12 = RandomVariate.instance("ResampledNormal", mean=1.2, stdev=0.75, trunc = 1.2) 104 | sst12 = SimpleStatsTally(str(rv12)) 105 | for i in range(10000): 106 | sst12.new_observation(rv12.generate()) 107 | print(sst12) 108 | -------------------------------------------------------------------------------- /simkit/test/TestRenegingServer.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.serverwithreneges import CustomerCreator 2 | from simkit.examples.serverwithreneges import ServerWithReneges 3 | from simkit.rand import RandomVariate 4 | from simkit.base import EventList 5 | from simkit.stats import SimpleStatsTally 6 | from simkit.stats import CollectionSizeTimeVarying 7 | 8 | interarrival_generator = RandomVariate.instance('Exponential', mean=1.5) 9 | renege_generator = RandomVariate.instance('Uniform', min=2.0, max=6.0) 10 | creator = CustomerCreator(interarrival_generator, renege_generator) 11 | print(creator.describe()) 12 | 13 | total_number_servers = 2 14 | service_time_generator = RandomVariate.instance('Gamma', alpha=2.5, beta=1.2) 15 | server = ServerWithReneges(total_number_servers, service_time_generator) 16 | print (server.describe()) 17 | 18 | creator.add_sim_event_listener(server) 19 | 20 | # server.add_state_change_listener(SimpleStateChangeDumper()) 21 | 22 | delay_in_queue_stat = SimpleStatsTally('delay_in_queue') 23 | server.add_state_change_listener(delay_in_queue_stat) 24 | 25 | number_in_queue_stat = CollectionSizeTimeVarying('queue') 26 | server.add_state_change_listener(number_in_queue_stat) 27 | 28 | EventList.verbose = False 29 | EventList.stop_at_time(100000.0) 30 | # EventList.stopOnEvent(10000, 'Renege') 31 | EventList.reset() 32 | EventList.start_simulation() 33 | 34 | print('\nSimulation ended at simtime {time:,.3f}'.format(time=EventList.simtime)) 35 | print('Avg delay in queue = {avg:,.4f}'.format(avg=(delay_in_queue_stat.mean))) 36 | print('Avg number in queue = {avg:.4f}'.format(avg=number_in_queue_stat.mean)) 37 | print('There have been {num:,d} reneges'.format(num=server.number_reneges)) 38 | print('There have been {num:,d} served'.format(num=delay_in_queue_stat.count)) 39 | print('{percent:.2f}% reneged'.format(percent=(100 * server.number_reneges / (server.number_reneges + delay_in_queue_stat.count)))) 40 | -------------------------------------------------------------------------------- /simkit/test/TestSeedByTime.py: -------------------------------------------------------------------------------- 1 | from simkit.rand import Exponential 2 | from datetime import datetime 3 | 4 | rv = Exponential(mean=1.5) 5 | print(rv) 6 | 7 | number = 5 8 | 9 | print('original:') 10 | for i in range(number): 11 | print(rv.generate()) 12 | 13 | print('after reset() (should be identical:') 14 | rv.reset() 15 | for i in range(number): 16 | print(rv.generate()) 17 | 18 | print('after seed by clock (should be different each time:') 19 | time = datetime.now().time() 20 | print(time) 21 | rv.seed(time) 22 | for i in range(number): 23 | print(rv.generate()) -------------------------------------------------------------------------------- /simkit/test/TestSimkit.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEvent, SimEntityBase, EventList 2 | from heapq import heappush 3 | from simkit.base import Priority 4 | 5 | class TestSimEntity(SimEntityBase): 6 | 7 | def __init__(self, name='TestSimEntity'): 8 | SimEntityBase.__init__(self) 9 | self.count = 0 10 | 11 | def reset(self): 12 | self.count = 0 13 | 14 | def run(self): 15 | self.notify_state_change("count", self.count) 16 | self.waitDelay('foo', 0.0) 17 | 18 | def foo(self): 19 | self.count += 1 20 | self.notify_state_change('count', self.count) 21 | 22 | if __name__=='__main__': 23 | source = TestSimEntity() 24 | name='aSimEvent' 25 | scheduled=42.0 26 | args=[] 27 | 28 | evt=SimEvent(source, name, scheduled, args) 29 | scheduled=21.5 30 | evt2=SimEvent(TestSimEntity('Foo'), name, scheduled, args, Priority.HIGH) 31 | evt3=SimEvent(source, 'anotherSimEvent', 21.5, [], Priority.LOW) 32 | evt4=SimEvent(TestSimEntity('bar'), 'anEvent', 10.0, [1.0, 'Two']) 33 | 34 | heap = [] 35 | heappush(heap, evt) 36 | heappush(heap, evt2) 37 | heappush(heap, evt3) 38 | heappush(heap, evt4) 39 | 40 | copy = heap.copy() 41 | copy.sort() 42 | 43 | dump='Event List:' 44 | for e in copy: 45 | dump += '\n' + str(e) 46 | 47 | print(dump) 48 | 49 | el = EventList() 50 | 51 | evt = source.schedule('Foo', 1.0) 52 | evt1 = SimEntityBase().schedule('bar', 2.0, [1]) 53 | evt2 = SimEntityBase().schedule('boo', 2.0, [], priority=Priority.HIGH) 54 | 55 | print (EventList.dump()) 56 | 57 | 58 | print (Priority.DEFAULT < Priority.HIGH) 59 | print (Priority.DEFAULT > Priority.HIGH) 60 | # print(evt1 < evt2) 61 | # print(evt1 > evt2) 62 | 63 | copy = EventList.event_list 64 | copy.sort() 65 | for e in copy: 66 | print(e) 67 | 68 | print(evt1.__gt__(evt2)) 69 | 70 | print (getattr(source, 'process_sim_event')) 71 | method = getattr(source, 'process_sim_event') 72 | method(evt) 73 | -------------------------------------------------------------------------------- /simkit/test/TestSimkit2.py: -------------------------------------------------------------------------------- 1 | from simkit.base import SimEntityBase 2 | from simkit.base import EventList 3 | from simkit.base import Priority 4 | from simkit.simutil import SimpleStateChangeDumper 5 | 6 | class Pinger(SimEntityBase): 7 | 8 | def __init__(self): 9 | SimEntityBase.__init__(self) 10 | self.count = 0; 11 | 12 | def reset(self): 13 | self.count = 0 14 | 15 | def run(self): 16 | self.notify_state_change("count", self.count) 17 | self.schedule('ping', 1.1) 18 | 19 | def ping(self): 20 | self.count = self.count + 1 21 | self.notify_state_change('count', self.count) 22 | self.schedule('ping', 1.2) 23 | 24 | class Pinger2(SimEntityBase): 25 | 26 | def __init__(self, number=1): 27 | SimEntityBase.__init__(self) 28 | self.number = number 29 | self.name='Pinger2' 30 | 31 | def reset(self): 32 | pass 33 | 34 | def run(self): 35 | self.schedule('init', 0.0, 0) 36 | 37 | def init(self, i): 38 | if i < self.number - 1: 39 | self.schedule('init', 0.0, i + 1) 40 | 41 | if __name__=='__main__': 42 | 43 | pinger = Pinger() 44 | dumper = SimpleStateChangeDumper() 45 | pinger.add_state_change_listener(dumper) 46 | 47 | pinger2 = Pinger2(4) 48 | 49 | EventList.verbose = True 50 | EventList.stop_at_time(5.0) 51 | 52 | EventList.reset() 53 | EventList.start_simulation() 54 | 55 | 56 | -------------------------------------------------------------------------------- /simkit/test/TestSimpleMover.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.mover.simplemover import SimpleMover 2 | from simkit.examples.mover.simplemover import Point 3 | from simkit.examples.mover.simplemover import SimplePathMoverManager 4 | from simkit.base import EventList 5 | from simkit.simutil import SimpleStateChangeDumper 6 | 7 | start_loc = Point(20.0, 30.0) 8 | max_speed = 30.0 9 | simple_mover = SimpleMover(start_loc, max_speed) 10 | 11 | simple_mover.add_state_change_listener(SimpleStateChangeDumper()) 12 | 13 | print(simple_mover.describe()) 14 | 15 | path = [] 16 | path.append(Point(25.0, -20.0)) 17 | path.append(Point(50.0, 10.0)) 18 | path.append(Point(-100.0, -30.0)) 19 | 20 | simple_path_mover_manager = SimplePathMoverManager(simple_mover, path, True) 21 | print(simple_path_mover_manager.describe()) 22 | 23 | EventList.verbose = True 24 | EventList.reset() 25 | EventList.start_simulation() -------------------------------------------------------------------------------- /simkit/test/TestSimpleServer.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.arrivalprocess import ArrivalProcess 2 | from simkit.examples.simpleserver import SimpleServer 3 | from simkit.rand import RandomVariate 4 | from simkit.stats import SimpleStatsTimeVarying 5 | from simkit.base import EventList 6 | 7 | interarrival_time_generator = RandomVariate.instance('Uniform', min=0.9, max=2.2) 8 | arrival_process = ArrivalProcess(interarrival_time_generator) 9 | print(arrival_process.describe()) 10 | 11 | number_servers = 3 12 | service_time_generator = RandomVariate.instance('Gamma', alpha=2, beta=2.2) 13 | simple_server = SimpleServer(number_servers, service_time_generator) 14 | print(simple_server.describe()) 15 | 16 | arrival_process.add_sim_event_listener(simple_server) 17 | 18 | number_in_queue_stat = SimpleStatsTimeVarying('number_in_queue') 19 | number_available_servers_stat = SimpleStatsTimeVarying('number_available_servers') 20 | 21 | simple_server.add_state_change_listener(number_in_queue_stat) 22 | simple_server.add_state_change_listener(number_available_servers_stat) 23 | 24 | # simple_server.add_state_change_listener(SimpleStateChangeDumper()) 25 | 26 | service_mean = simple_server.service_time_generator.alpha * simple_server.service_time_generator.beta 27 | arrival_mean = (arrival_process.interarrival_time_generator.min + arrival_process.interarrival_time_generator.max) * 0.5 28 | intensity = service_mean / (simple_server.total_number_servers * arrival_mean) 29 | 30 | print('traffic intensity = {rho:.4f}'.format(rho = intensity)) 31 | 32 | stopTime = 100000 33 | # stopTime = 50 34 | # EventList.verbose = True 35 | 36 | EventList.stop_at_time(stopTime) 37 | EventList.reset() 38 | EventList.start_simulation() 39 | 40 | print('\nSimulation ended at simtime {time:,.2f}'.format(time=EventList.simtime)) 41 | 42 | # print(number_in_queue_stat) 43 | # print(number_available_servers_stat) 44 | 45 | print('{num:,d} customers arrived'.format(num=arrival_process.number_arrivals)) 46 | print('{num:,d} customers served'.format(num=simple_server.number_served)) 47 | 48 | print('Avg number in queue = {avg:.4f}'.format(avg=number_in_queue_stat.mean)) 49 | utilization = 1.0 - number_available_servers_stat.mean / simple_server.total_number_servers 50 | print("Avg utilization = {avg:.4f}".format(avg=utilization)) -------------------------------------------------------------------------------- /simkit/test/TestSimpleStatsRawData.py: -------------------------------------------------------------------------------- 1 | from simkit.stats import SimpleStatsTallyRawData 2 | from simkit.stats import SimpleStatsTally 3 | 4 | sstrd = SimpleStatsTallyRawData() 5 | sst = SimpleStatsTally() 6 | for x in range(1,11): 7 | sstrd.new_observation(x) 8 | sst.new_observation(x) 9 | print(sstrd.raw_data) 10 | print (sst) -------------------------------------------------------------------------------- /simkit/test/TestSimpleStatsTally.py: -------------------------------------------------------------------------------- 1 | from simkit.stats import SimpleStatsTally 2 | from simkit.rand import Exponential 3 | 4 | sst = SimpleStatsTally() 5 | print(sst) 6 | for i in range(1,10): 7 | sst.new_observation(i) 8 | 9 | print(sst) 10 | 11 | sst.reset() 12 | print(sst) 13 | rv = Exponential(3.7) 14 | number = 100000 15 | for x in range(number): 16 | sst.new_observation(rv.generate()) 17 | 18 | print('expected: ' + str(rv.mean)) 19 | print(sst) 20 | print(sst.mean) 21 | 22 | print(sst.halfwidth(.995)) 23 | 24 | sst.reset() 25 | 26 | for x in range(200): 27 | sst.new_observation(rv.generate()) 28 | print('{n:d} {hw:.4f}'.format(n=sst.count,hw=sst.halfwidth(0.995))) -------------------------------------------------------------------------------- /simkit/test/TestStopAtTime.py: -------------------------------------------------------------------------------- 1 | from simkit.base import EventList 2 | 3 | EventList.verbose = True 4 | EventList.stop_at_time(20.0) 5 | 6 | EventList.reset() 7 | EventList.start_simulation() 8 | 9 | EventList.stop_at_time(10.0) 10 | EventList.reset() 11 | EventList.start_simulation() -------------------------------------------------------------------------------- /simkit/test/TestStopOnEvent.py: -------------------------------------------------------------------------------- 1 | from simkit.base import EventList 2 | from simkit.base import SimEntityBase 3 | 4 | 5 | class Test(SimEntityBase): 6 | 7 | def run(self): 8 | self.schedule('init', 0.0, 0) 9 | 10 | def init(self, i): 11 | self.schedule('init', 0.0, i + 1) 12 | 13 | if __name__=='__main__': 14 | 15 | # interarrival_time_generator = RandomVariate.getInstance('Exponential', mean=3.0) 16 | # arrival_process = ArrivalProcess(interarrival_time_generator) 17 | # print(arrival_process.describe()) 18 | 19 | test = Test() 20 | 21 | EventList.stop_on_event(10, 'init') 22 | 23 | EventList.verbose = True 24 | EventList.reset() 25 | EventList.start_simulation() -------------------------------------------------------------------------------- /simkit/test/TestStoppedArrivals.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.arrivalprocess import StoppedArrivalProcess 2 | from simkit.rand import RandomVariate 3 | from simkit.base import EventList 4 | 5 | interarrival_time_generator = RandomVariate.instance('Gamma', alpha=2.1, beta=1.8) 6 | stop_time = 30.0 7 | stopped_arrival_process = StoppedArrivalProcess(interarrival_time_generator, stop_time) 8 | print(stopped_arrival_process.describe()) 9 | 10 | EventList.verbose = True 11 | 12 | EventList.reset() 13 | EventList.start_simulation() 14 | -------------------------------------------------------------------------------- /simkit/test/TestTransferLine.py: -------------------------------------------------------------------------------- 1 | from simkit.base import EventList 2 | from simkit.rand import RandomVariate 3 | from simkit.simutil import SimpleStateChangeDumper 4 | from simkit.base import Adapter 5 | from simkit.examples.transferline import TransferLine 6 | from simkit.examples.transferline import JobCreator 7 | 8 | job_creator = JobCreator(RandomVariate.instance('Exponential', mean=1.7)) 9 | print(job_creator.describe()) 10 | 11 | number_stations = 3 12 | number_machines = [5, 4, 2] 13 | service_times = [RandomVariate.instance('Gamma', alpha=3.2, beta=2.3), \ 14 | RandomVariate.instance('Uniform', min=4.5, max=6.7), 15 | RandomVariate.instance('Exponential', mean=3.0)] 16 | transfer_line = TransferLine(number_stations, number_machines, service_times) 17 | print(transfer_line.describe()) 18 | 19 | adapter = Adapter('job_arrival', 'arrival') 20 | adapter.connect(job_creator, transfer_line) 21 | 22 | transfer_line.add_state_change_listener(SimpleStateChangeDumper()) 23 | 24 | # EventList.stopAtTime(20.0) 25 | 26 | EventList.stop_on_event(10, 'job_complete') 27 | 28 | EventList.verbose = True 29 | EventList.reset() 30 | EventList.start_simulation() -------------------------------------------------------------------------------- /simkit/test/TestTruncatingSimpleStatsTimeVarying.py: -------------------------------------------------------------------------------- 1 | from simkit.examples.arrivalprocess import ArrivalProcess 2 | from simkit.examples.simpleserver import SimpleServer 3 | from simkit.rand import RandomVariate 4 | from simkit.stats import TruncatingSimpleStatsTimeVarying 5 | from simkit.base import EventList 6 | 7 | interarrival_time_generator = RandomVariate.instance('Uniform', min=0.9, max=2.2) 8 | arrival_process = ArrivalProcess(interarrival_time_generator) 9 | print(arrival_process.describe()) 10 | 11 | number_servers = 3 12 | service_time_generator = RandomVariate.instance('Gamma', alpha=2, beta=2.2) 13 | simple_server = SimpleServer(number_servers, service_time_generator) 14 | print(simple_server.describe()) 15 | 16 | arrival_process.add_sim_event_listener(simple_server) 17 | 18 | truncation_time = 50000 19 | number_in_queue_stat = TruncatingSimpleStatsTimeVarying(truncation_time, 'number_in_queue') 20 | number_available_servers_stat = TruncatingSimpleStatsTimeVarying(truncation_time, 'number_available_servers') 21 | 22 | simple_server.add_state_change_listener(number_in_queue_stat) 23 | simple_server.add_state_change_listener(number_available_servers_stat) 24 | 25 | # simple_server.add_state_change_listener(SimpleStateChangeDumper()) 26 | 27 | service_mean = simple_server.service_time_generator.alpha * simple_server.service_time_generator.beta 28 | arrival_mean = (arrival_process.interarrival_time_generator.min + arrival_process.interarrival_time_generator.max) * 0.5 29 | intensity = service_mean / (simple_server.total_number_servers * arrival_mean) 30 | 31 | print('\ntraffic intensity = {rho:.4f}'.format(rho = intensity)) 32 | 33 | stop_time = 150000 34 | # stopTime = 50 35 | 36 | # arrival_process_stat = TruncatingSimpleStatsTimeVarying(truncation_time, 'number_arrivals') 37 | # arrival_process.add_state_change_listener(arrival_process_stat) 38 | 39 | # EventList.verbose = True 40 | 41 | EventList.stop_at_time(stop_time + truncation_time) 42 | EventList.reset() 43 | EventList.start_simulation() 44 | 45 | print('\nSimulation ended at simtime {time:,.2f}'.format(time=EventList.simtime)) 46 | 47 | # print(arrival_process_stat) 48 | # print(number_in_queue_stat) 49 | # print(number_available_servers_stat) 50 | 51 | # print('{num:,d} customers arrived'.format(num=arrival_process.number_arrivals)) 52 | # print('{num:,d} customers served'.format(num=simple_server.number_served)) 53 | 54 | print('Avg number in queue = {avg:.4f}'.format(avg=number_in_queue_stat.mean)) 55 | utilization = 1.0 - number_available_servers_stat.mean / simple_server.total_number_servers 56 | print("Avg utilization = {avg:.4f}".format(avg=utilization)) -------------------------------------------------------------------------------- /simkit/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahbuss/DESpy/ce149ae5581017fc3df85971da9f73f6156fb300/simkit/test/__init__.py --------------------------------------------------------------------------------