├── .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 |
4 |
5 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/libraries/R_User_Library.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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
--------------------------------------------------------------------------------