Skip to content

Commit d223453

Browse files
Merge pull request #246 from CiwPython/for3.2.0
For 3.2.0
2 parents 2fd69b0 + 8155d5e commit d223453

File tree

98 files changed

+8785
-6447
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+8785
-6447
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
strategy:
1010
matrix:
1111
os: [ubuntu-latest, macOS-latest, windows-latest]
12-
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
12+
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
1313

1414
steps:
1515
- uses: actions/checkout@v2

README.rst

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ Install with :code:`pip install ciw`.
3232

3333
Current supported version of Python:
3434

35-
- Python 3.7
3635
- Python 3.8
3736
- Python 3.9
3837
- Python 3.10
3938
- Python 3.11
39+
- Python 3.12
4040

4141
Usage
4242
-----
@@ -75,23 +75,25 @@ Features
7575

7676
A number of other features are also implemented, including:
7777

78-
+ `Type I blocking <https://ciw.readthedocs.io/en/latest/Tutorial-II/tutorial_vi.html>`_
78+
+ `Type I blocking <https://ciw.readthedocs.io/en/latest/Guides/Queues/queue_capacities.html>`_
7979
+ `A large range of sampling distributions <https://ciw.readthedocs.io/en/latest/Reference/distributions.html>`_
80-
+ `Phase-Type distributions <https://ciw.readthedocs.io/en/latest/Guides/phasetype.html>`_
81-
+ `Time-dependent and state-dependent distributions <https://ciw.readthedocs.io/en/latest/Guides/time_dependent.html>`_
82-
+ `Batch arrivals <https://ciw.readthedocs.io/en/latest/Guides/batching.html>`_
83-
+ `Baulking customers <https://ciw.readthedocs.io/en/latest/Guides/baulking.html>`_
84-
+ `Reneging customers <https://ciw.readthedocs.io/en/latest/Guides/reneging.html>`_
85-
+ `Processor sharing <https://ciw.readthedocs.io/en/latest/Guides/processor-sharing.html>`_
86-
+ `Multiple customer classes <https://ciw.readthedocs.io/en/latest/Tutorial-II/tutorial_vii.html>`_
87-
+ `Priorities <https://ciw.readthedocs.io/en/latest/Guides/priority.html>`_
88-
+ `Server priorities <https://ciw.readthedocs.io/en/latest/Guides/server_priority.html>`_
89-
+ `Service disciplines <https://ciw.readthedocs.io/en/latest/Guides/service_disciplines.html>`_
90-
+ `Customers changing classes <https://ciw.readthedocs.io/en/latest/Guides/dynamic_customerclasses.html>`_
91-
+ `Server schedules <https://ciw.readthedocs.io/en/latest/Guides/server_schedule.html>`_
92-
+ `Slotted services <https://ciw.readthedocs.io/en/latest/Guides/slotted.html>`_
93-
+ `State tracking <https://ciw.readthedocs.io/en/latest/Guides/state_trackers.html>`_
94-
+ `Stopping the simulation after a certain amount of customers <https://ciw.readthedocs.io/en/latest/Guides/sim_numcusts.html>`_
95-
+ `Process-based routing <https://ciw.readthedocs.io/en/latest/Guides/process_based.html>`_
96-
+ `Deadlock detection <https://ciw.readthedocs.io/en/latest/Guides/deadlock.html>`_
80+
+ `Phase-Type distributions <https://ciw.readthedocs.io/en/latest/Guides/Distributions/phasetype.html>`_
81+
+ `Time-dependent and state-dependent distributions <https://ciw.readthedocs.io/en/latest/Guides/Distributions/time_dependent.html>`_
82+
+ `Batch arrivals <https://ciw.readthedocs.io/en/latest/Guides/Arrivals/batching.html>`_
83+
+ `Baulking customers <https://ciw.readthedocs.io/en/latest/Guides/CustomerBehaviour/baulking.html>`_
84+
+ `Reneging customers <https://ciw.readthedocs.io/en/latest/Guides/CustomerBehaviour/reneging.html>`_
85+
+ `Processor sharing <https://ciw.readthedocs.io/en/latest/Guides/Services/processor-sharing.html>`_
86+
+ `Multiple customer classes <https://ciw.readthedocs.io/en/latest/Guides/CustomerClasses/customer-classes.html>`_
87+
+ `Priorities <https://ciw.readthedocs.io/en/latest/Guides/CustomerClasses/priority.html>`_
88+
+ `Server priorities <https://ciw.readthedocs.io/en/latest/Guides/Services/server_priority.html>`_
89+
+ `Service disciplines <https://ciw.readthedocs.io/en/latest/Guides/Services/service_disciplines.html>`_
90+
+ `Customers changing classes while queueing <https://ciw.readthedocs.io/en/latest/Guides/CustomerClasses/change-class-while-queueing.html>`_
91+
+ `Customers changing classes after service <https://ciw.readthedocs.io/en/latest/Guides/CustomerClasses/change-class-after-service.html>`_
92+
+ `Server schedules <https://ciw.readthedocs.io/en/latest/Guides/Services/server_schedule.html>`_
93+
+ `Slotted services <https://ciw.readthedocs.io/en/latest/Guides/Services/slotted.html>`_
94+
+ `State tracking <https://ciw.readthedocs.io/en/latest/Guides/System/state_trackers.html>`_
95+
+ `Stopping the simulation after a certain amount of customers <https://ciw.readthedocs.io/en/latest/Guides/Simulation/sim_numcusts.html>`_
96+
+ `Process-based routing <https://ciw.readthedocs.io/en/latest/Guides/Routing/process_based.html>`_
97+
+ `Logical routing <https://ciw.readthedocs.io/en/latest/Guides/Reference/routers.html>`_
98+
+ `Deadlock detection <https://ciw.readthedocs.io/en/latest/Guides/System/deadlock.html>`_
9799

ciw/arrival_node.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def __init__(self, simulation):
2828
self.number_of_individuals_per_class = {clss: 0 for clss in self.simulation.network.customer_class_names}
2929
self.number_accepted_individuals = 0
3030
self.number_accepted_individuals_per_class = {clss: 0 for clss in self.simulation.network.customer_class_names}
31+
self.system_capacity = self.simulation.network.system_capacity
3132
self.event_dates_dict = {
3233
nd + 1: {clss: False for clss in self.simulation.network.customer_class_names
3334
} for nd in range(self.simulation.network.number_of_nodes)
@@ -91,9 +92,9 @@ def have_event(self):
9192
priority_class,
9293
simulation=self.simulation,
9394
)
94-
if self.simulation.network.process_based:
95-
next_individual.route = self.simulation.network.customer_classes[next_individual.customer_class].routing[self.next_node - 1](next_individual)
9695
next_node = self.simulation.transitive_nodes[self.next_node - 1]
96+
next_individual.starting_node = next_node.id_number
97+
self.simulation.routers[next_individual.customer_class].initialise_individual(next_individual)
9798
self.release_individual(next_node, next_individual)
9899

99100
self.event_dates_dict[self.next_node][self.next_class] = self.increment_time(
@@ -153,7 +154,7 @@ def release_individual(self, next_node, next_individual):
153154
Either rejects the next_individual die to lack of capacity,
154155
or sends that individual to baulk or not.
155156
"""
156-
if next_node.number_of_individuals >= next_node.node_capacity:
157+
if (next_node.number_of_individuals >= next_node.node_capacity) or (self.simulation.number_of_individuals >= self.system_capacity):
157158
self.record_rejection(next_node, next_individual)
158159
self.simulation.nodes[-1].accept(next_individual, completed=False)
159160
else:

ciw/import_params.py

Lines changed: 66 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import ciw.dists
44
from .network import *
55
from .schedules import *
6+
from .routing import *
67

78

89
def create_network(
@@ -21,6 +22,7 @@ def create_network(
2122
reneging_time_distributions=None,
2223
reneging_destinations=None,
2324
service_disciplines=None,
25+
system_capacity=float('inf')
2426
):
2527
"""
2628
Takes in kwargs, creates dictionary.
@@ -41,6 +43,7 @@ def create_network(
4143
"arrival_distributions": arrival_distributions,
4244
"number_of_servers": number_of_servers,
4345
"service_distributions": service_distributions,
46+
'system_capacity': system_capacity
4447
}
4548

4649
if baulking_functions is not None:
@@ -115,35 +118,19 @@ def create_network_from_dictionary(params_input):
115118
)
116119
)
117120
for clss_name in params['customer_class_names']:
118-
if all(isinstance(f, types.FunctionType) or isinstance(f, types.MethodType) for f in params["routing"]):
119-
classes[clss_name] = CustomerClass(
120-
params['arrival_distributions'][clss_name],
121-
params['service_distributions'][clss_name],
122-
params['routing'],
123-
params["priority_classes"][clss_name],
124-
params["baulking_functions"][clss_name],
125-
params["batching_distributions"][clss_name],
126-
params["reneging_time_distributions"][clss_name],
127-
params["reneging_destinations"][clss_name],
128-
class_change_time_distributions[clss_name],
129-
)
130-
else:
131-
classes[clss_name] = CustomerClass(
132-
params['arrival_distributions'][clss_name],
133-
params['service_distributions'][clss_name],
134-
params['routing'][clss_name],
135-
params["priority_classes"][clss_name],
136-
params["baulking_functions"][clss_name],
137-
params["batching_distributions"][clss_name],
138-
params["reneging_time_distributions"][clss_name],
139-
params["reneging_destinations"][clss_name],
140-
class_change_time_distributions[clss_name],
141-
)
121+
classes[clss_name] = CustomerClass(
122+
params['arrival_distributions'][clss_name],
123+
params['service_distributions'][clss_name],
124+
params['routing'][clss_name],
125+
params["priority_classes"][clss_name],
126+
params["baulking_functions"][clss_name],
127+
params["batching_distributions"][clss_name],
128+
params["reneging_time_distributions"][clss_name],
129+
params["reneging_destinations"][clss_name],
130+
class_change_time_distributions[clss_name],
131+
)
142132
n = Network(nodes, classes)
143-
if all(isinstance(f, types.FunctionType) or isinstance(f, types.MethodType) for f in params["routing"]):
144-
n.process_based = True
145-
else:
146-
n.process_based = False
133+
n.system_capacity = params['system_capacity']
147134
return n
148135

149136

@@ -159,9 +146,16 @@ def fill_out_dictionary(params):
159146
srv_dists = params["service_distributions"]
160147
params["service_distributions"] = {"Customer": srv_dists}
161148
if "routing" in params:
162-
if all(isinstance(f, list) for f in params["routing"]):
163-
rtng_mat = params["routing"]
164-
params["routing"] = {"Customer": rtng_mat}
149+
if isinstance(params["routing"], list):
150+
transition_matrix = params["routing"]
151+
params["routing"] = {"Customer": routing.TransitionMatrix(transition_matrix=transition_matrix)}
152+
elif isinstance(params["routing"], dict):
153+
for clss in params["routing"]:
154+
if isinstance(params["routing"][clss], list):
155+
transition_matrix = params["routing"][clss]
156+
params["routing"][clss] = routing.TransitionMatrix(transition_matrix=transition_matrix)
157+
else:
158+
params["routing"] = {"Customer": params["routing"]}
165159
if "baulking_functions" in params:
166160
if isinstance(params["baulking_functions"], list):
167161
blk_fncs = params["baulking_functions"]
@@ -184,7 +178,7 @@ def fill_out_dictionary(params):
184178

185179
default_dict = {
186180
"name": "Simulation",
187-
"routing": {class_name: [[0.0]] for class_name in class_names},
181+
"routing": {class_name: routing.TransitionMatrix(transition_matrix=[[0.0]]) for class_name in class_names},
188182
"number_of_nodes": len(params["number_of_servers"]),
189183
"number_of_classes": len(class_names),
190184
"queue_capacities": [float("inf") for _ in range(len(params["number_of_servers"]))],
@@ -208,6 +202,7 @@ def fill_out_dictionary(params):
208202
"service_disciplines": [
209203
ciw.disciplines.FIFO for _ in range(len(params["number_of_servers"]))
210204
],
205+
"system_capacity": float('inf')
211206
}
212207

213208
for a in default_dict:
@@ -220,87 +215,46 @@ def validify_dictionary(params):
220215
Raises errors if there is something wrong with the
221216
parameters dictionary.
222217
"""
223-
if all(isinstance(f, types.FunctionType) or isinstance(f, types.MethodType) for f in params["routing"]):
224-
consistant_num_classes = (
225-
params["number_of_classes"]
226-
== len(params["arrival_distributions"])
227-
== len(params["service_distributions"])
228-
== len(params["batching_distributions"])
229-
== len(params["reneging_time_distributions"])
230-
== len(params["reneging_destinations"])
231-
)
232-
else:
233-
consistant_num_classes = (
234-
params["number_of_classes"]
235-
== len(params["arrival_distributions"])
236-
== len(params["service_distributions"])
237-
== len(params["routing"])
238-
== len(params["batching_distributions"])
239-
== len(params["reneging_time_distributions"])
240-
== len(params["reneging_destinations"])
241-
)
218+
consistant_num_classes = (
219+
params["number_of_classes"]
220+
== len(params["arrival_distributions"])
221+
== len(params["service_distributions"])
222+
== len(params["routing"])
223+
== len(params["batching_distributions"])
224+
== len(params["reneging_time_distributions"])
225+
== len(params["reneging_destinations"])
226+
)
242227
if not consistant_num_classes:
243228
raise ValueError("Ensure consistant number of classes is used throughout.")
244-
if all(isinstance(f, types.FunctionType) or isinstance(f, types.MethodType) for f in params["routing"]):
245-
consistant_class_names = (
246-
set(params["arrival_distributions"])
247-
== set(params["service_distributions"])
248-
== set(params["batching_distributions"])
249-
== set(params["reneging_time_distributions"])
250-
== set(params["reneging_destinations"])
251-
)
252-
else:
253-
consistant_class_names = (
254-
set(params["arrival_distributions"])
255-
== set(params["service_distributions"])
256-
== set(params["routing"])
257-
== set(params["batching_distributions"])
258-
== set(params["reneging_time_distributions"])
259-
== set(params["reneging_destinations"])
260-
) and (
261-
len(params["arrival_distributions"])
262-
== len(params["service_distributions"])
263-
== len(params["batching_distributions"])
264-
== len(params["reneging_time_distributions"])
265-
== len(params["reneging_destinations"])
266-
)
229+
consistant_class_names = (
230+
set(params["arrival_distributions"])
231+
== set(params["service_distributions"])
232+
== set(params["routing"])
233+
== set(params["batching_distributions"])
234+
== set(params["reneging_time_distributions"])
235+
== set(params["reneging_destinations"])
236+
) and (
237+
len(params["arrival_distributions"])
238+
== len(params["service_distributions"])
239+
== len(params["batching_distributions"])
240+
== len(params["reneging_time_distributions"])
241+
== len(params["reneging_destinations"])
242+
)
267243
if not consistant_class_names:
268244
raise ValueError("Ensure consistant names for customer classes.")
269-
if all(isinstance(f, types.FunctionType) or isinstance(f, types.MethodType) for f in params["routing"]):
270-
num_nodes_count = (
271-
[params["number_of_nodes"]]
272-
+ [len(obs) for obs in params["arrival_distributions"].values()]
273-
+ [len(obs) for obs in params["service_distributions"].values()]
274-
+ [len(obs) for obs in params["batching_distributions"].values()]
275-
+ [len(obs) for obs in params["reneging_time_distributions"].values()]
276-
+ [len(obs) for obs in params["reneging_destinations"].values()]
277-
+ [len(params["routing"])]
278-
+ [len(params["number_of_servers"])]
279-
+ [len(params["server_priority_functions"])]
280-
+ [len(params["queue_capacities"])]
281-
)
282-
else:
283-
num_nodes_count = (
284-
[params["number_of_nodes"]]
285-
+ [len(obs) for obs in params["arrival_distributions"].values()]
286-
+ [len(obs) for obs in params["service_distributions"].values()]
287-
+ [len(obs) for obs in params["routing"].values()]
288-
+ [len(obs) for obs in params["batching_distributions"].values()]
289-
+ [len(obs) for obs in params["reneging_time_distributions"].values()]
290-
+ [len(obs) for obs in params["reneging_destinations"].values()]
291-
+ [len(row) for row in [obs for obs in params["routing"].values()][0]]
292-
+ [len(params["number_of_servers"])]
293-
+ [len(params["server_priority_functions"])]
294-
+ [len(params["queue_capacities"])]
295-
+ [len(params["service_disciplines"])]
296-
)
245+
num_nodes_count = (
246+
[params["number_of_nodes"]]
247+
+ [len(obs) for obs in params["arrival_distributions"].values()]
248+
+ [len(obs) for obs in params["service_distributions"].values()]
249+
+ [len(obs) for obs in params["batching_distributions"].values()]
250+
+ [len(obs) for obs in params["reneging_time_distributions"].values()]
251+
+ [len(obs) for obs in params["reneging_destinations"].values()]
252+
+ [len(params["number_of_servers"])]
253+
+ [len(params["server_priority_functions"])]
254+
+ [len(params["queue_capacities"])]
255+
)
297256
if len(set(num_nodes_count)) != 1:
298257
raise ValueError("Ensure consistant number of nodes is used throughout.")
299-
if not all(isinstance(f, types.FunctionType) or isinstance(f, types.MethodType) for f in params["routing"]):
300-
for clss in params["routing"].values():
301-
for row in clss:
302-
if sum(row) > 1.0 or min(row) < 0.0 or max(row) > 1.0:
303-
raise ValueError("Ensure that routing matrix is valid.")
304258
neg_numservers = any(
305259
[(isinstance(obs, int) and obs < 0) for obs in params["number_of_servers"]]
306260
)
@@ -352,3 +306,8 @@ def validify_dictionary(params):
352306
)
353307
if not correct_destinations:
354308
raise ValueError("Ensure all reneging destinations are possible.")
309+
310+
if not isinstance(params['system_capacity'], int) and params['system_capacity'] != float('inf'):
311+
raise ValueError("Ensure system capacity is a positive integer.")
312+
if params['system_capacity'] <= 0:
313+
raise ValueError("Ensure system capacity is a positive integer.")

0 commit comments

Comments
 (0)