Skip to content

Commit f63a7f4

Browse files
committed
reformat reneging destinations similar to rerouting; add jockeying docs
1 parent e165227 commit f63a7f4

File tree

8 files changed

+78
-96
lines changed

8 files changed

+78
-96
lines changed

ciw/import_params.py

-23
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ def create_network(
2020
ps_thresholds=None,
2121
server_priority_functions=None,
2222
reneging_time_distributions=None,
23-
reneging_destinations=None,
2423
service_disciplines=None,
2524
system_capacity=float('inf')
2625
):
@@ -63,8 +62,6 @@ def create_network(
6362
params["server_priority_functions"] = server_priority_functions
6463
if reneging_time_distributions is not None:
6564
params["reneging_time_distributions"] = reneging_time_distributions
66-
if reneging_destinations is not None:
67-
params["reneging_destinations"] = reneging_destinations
6865
if service_disciplines is not None:
6966
params["service_disciplines"] = service_disciplines
7067

@@ -123,7 +120,6 @@ def create_network_from_dictionary(params_input):
123120
params["baulking_functions"][clss_name],
124121
params["batching_distributions"][clss_name],
125122
params["reneging_time_distributions"][clss_name],
126-
params["reneging_destinations"][clss_name],
127123
class_change_time_distributions[clss_name],
128124
)
129125
n = Network(nodes, classes)
@@ -165,10 +161,6 @@ def fill_out_dictionary(params):
165161
if isinstance(params["reneging_time_distributions"], list):
166162
reneging_dists = params["reneging_time_distributions"]
167163
params["reneging_time_distributions"] = {"Customer": reneging_dists}
168-
if "reneging_destinations" in params:
169-
if isinstance(params["reneging_destinations"], list):
170-
reneging_dests = params["reneging_destinations"]
171-
params["reneging_destinations"] = {"Customer": reneging_dests}
172164

173165
class_names = sorted(params["arrival_distributions"].keys())
174166
params["customer_class_names"] = class_names
@@ -192,10 +184,6 @@ def fill_out_dictionary(params):
192184
class_name: [None for _ in range(len(params["number_of_servers"]))]
193185
for class_name in class_names
194186
},
195-
"reneging_destinations": {
196-
class_name: [-1 for _ in range(len(params["number_of_servers"]))]
197-
for class_name in class_names
198-
},
199187
"service_disciplines": [
200188
ciw.disciplines.FIFO for _ in range(len(params["number_of_servers"]))
201189
],
@@ -219,7 +207,6 @@ def validify_dictionary(params):
219207
== len(params["routing"])
220208
== len(params["batching_distributions"])
221209
== len(params["reneging_time_distributions"])
222-
== len(params["reneging_destinations"])
223210
)
224211
if not consistant_num_classes:
225212
raise ValueError("Ensure consistant number of classes is used throughout.")
@@ -229,13 +216,11 @@ def validify_dictionary(params):
229216
== set(params["routing"])
230217
== set(params["batching_distributions"])
231218
== set(params["reneging_time_distributions"])
232-
== set(params["reneging_destinations"])
233219
) and (
234220
len(params["arrival_distributions"])
235221
== len(params["service_distributions"])
236222
== len(params["batching_distributions"])
237223
== len(params["reneging_time_distributions"])
238-
== len(params["reneging_destinations"])
239224
)
240225
if not consistant_class_names:
241226
raise ValueError("Ensure consistant names for customer classes.")
@@ -245,7 +230,6 @@ def validify_dictionary(params):
245230
+ [len(obs) for obs in params["service_distributions"].values()]
246231
+ [len(obs) for obs in params["batching_distributions"].values()]
247232
+ [len(obs) for obs in params["reneging_time_distributions"].values()]
248-
+ [len(obs) for obs in params["reneging_destinations"].values()]
249233
+ [len(params["number_of_servers"])]
250234
+ [len(params["server_priority_functions"])]
251235
+ [len(params["queue_capacities"])]
@@ -296,13 +280,6 @@ def validify_dictionary(params):
296280
raise ValueError(
297281
"Ensure consistant customer classes used in class_change_time_distributions."
298282
)
299-
possible_destinations = list(range(1, params["number_of_nodes"] + 1)) + [-1]
300-
for dests in params["reneging_destinations"]:
301-
correct_destinations = all(
302-
d in possible_destinations for d in params["reneging_destinations"][dests]
303-
)
304-
if not correct_destinations:
305-
raise ValueError("Ensure all reneging destinations are possible.")
306283

307284
if not isinstance(params['system_capacity'], int) and params['system_capacity'] != float('inf'):
308285
raise ValueError("Ensure system capacity is a positive integer.")

ciw/network.py

-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ def __init__(
5252
baulking_functions,
5353
batching_distributions,
5454
reneging_time_distributions,
55-
reneging_destinations,
5655
class_change_time_distributions,
5756
):
5857
"""
@@ -65,7 +64,6 @@ def __init__(
6564
self.priority_class = priority_class
6665
self.baulking_functions = baulking_functions
6766
self.reneging_time_distributions = reneging_time_distributions
68-
self.reneging_destinations = reneging_destinations
6967
self.class_change_time_distributions = class_change_time_distributions
7068

7169

ciw/node.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,12 @@ def next_node_for_rerouting(self, ind):
538538
"""
539539
return self.simulation.routers[ind.customer_class].next_node_for_rerouting(ind, self.id_number)
540540

541+
def next_node_for_jockeying(self, ind):
542+
"""
543+
Finds the next node (for jockeing) according the routing method:
544+
"""
545+
return self.simulation.routers[ind.customer_class].next_node_for_jockeying(ind, self.id_number)
546+
541547
def preempt(self, individual_to_preempt, next_individual):
542548
"""
543549
Removes individual_to_preempt from service and replaces them with next_individual
@@ -640,10 +646,7 @@ def renege(self):
640646
"""
641647
reneging_individual = self.decide_between_simultaneous_individuals()
642648
reneging_individual.reneging_date = float("Inf")
643-
next_node_number = self.simulation.network.customer_classes[
644-
reneging_individual.customer_class
645-
].reneging_destinations[self.id_number - 1]
646-
next_node = self.simulation.nodes[next_node_number]
649+
next_node = self.next_node_for_jockeying(reneging_individual)
647650
self.individuals[reneging_individual.prev_priority_class].remove(reneging_individual)
648651
self.number_of_individuals -= 1
649652
reneging_individual.queue_size_at_departure = self.number_of_individuals

ciw/routing/routing.py

+18
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ def next_node_for_rerouting(self, ind, node_id):
3737
"""
3838
return self.routers[node_id - 1].next_node_for_rerouting(ind)
3939

40+
def next_node_for_jockeying(self, ind, node_id):
41+
"""
42+
Chooses the next node when jockeying to another node after reneging.
43+
"""
44+
return self.routers[node_id - 1].next_node_for_jockeying(ind)
45+
4046

4147
class TransitionMatrix(NetworkRouting):
4248
"""
@@ -101,6 +107,12 @@ def next_node_for_rerouting(self, ind, node_id):
101107
"""
102108
return self.next_node(ind, node_id)
103109

110+
def next_node_for_jockeying(self, ind, node_id):
111+
"""
112+
Chooses the next node when jockeying to another node after reneging.
113+
"""
114+
return self.simulation.nodes[-1]
115+
104116
class FlexibleProcessBased(ProcessBased):
105117
"""
106118
A class to route an individual based on a pre-defined process.
@@ -184,6 +196,12 @@ def next_node_for_rerouting(self, ind):
184196
"""
185197
return self.next_node(ind)
186198

199+
def next_node_for_jockeying(self, ind):
200+
"""
201+
Chooses the next node when jockeying to another node after reneging.
202+
"""
203+
return self.simulation.nodes[-1]
204+
187205

188206
class Probabilistic(NodeRouting):
189207
"""

ciw/tests/test_network.py

+7-27
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ def test_init_method(self):
7474
ciw.dists.Deterministic(1),
7575
]
7676
reneging_time_distributions = [None, None, None]
77-
reneging_destinations = [-1, -1, -1]
7877
class_change_time_distributions = [None]
7978

8079
CC = ciw.CustomerClass(
@@ -85,7 +84,6 @@ def test_init_method(self):
8584
baulking_functions,
8685
batching_distributions,
8786
reneging_time_distributions,
88-
reneging_destinations,
8987
class_change_time_distributions,
9088
)
9189
self.assertEqual(CC.arrival_distributions, arrival_distributions)
@@ -94,7 +92,6 @@ def test_init_method(self):
9492
self.assertEqual(CC.routing, routing)
9593
self.assertEqual(CC.priority_class, priority_class)
9694
self.assertEqual(CC.reneging_time_distributions, reneging_time_distributions)
97-
self.assertEqual(CC.reneging_destinations, reneging_destinations)
9895
self.assertEqual(CC.class_change_time_distributions, class_change_time_distributions)
9996

10097
# check baulking function works
@@ -133,7 +130,6 @@ def test_init_method(self):
133130
]
134131
baulking_functions = [None, None, example_baulking_function]
135132
reneging_time_distributions = [None, None, None]
136-
reneging_destinations = [-1, -1, -1]
137133
class_change_time_distributions = {'Class 0': None, 'Class 1': None}
138134
service_centres = [
139135
ciw.ServiceCentre(
@@ -150,7 +146,6 @@ def test_init_method(self):
150146
baulking_functions,
151147
batching_distributions,
152148
reneging_time_distributions,
153-
reneging_destinations,
154149
class_change_time_distributions,
155150
) for i in range(2)
156151
}
@@ -447,7 +442,7 @@ def test_raising_errors(self):
447442
"number_of_nodes": 1,
448443
"queue_capacities": [float("inf")],
449444
}
450-
params_list = [copy.deepcopy(params) for i in range(28)]
445+
params_list = [copy.deepcopy(params) for i in range(24)]
451446

452447
params_list[0]["number_of_classes"] = -2
453448
self.assertRaises(
@@ -601,37 +596,25 @@ def test_raising_errors(self):
601596
params_list[20]
602597
)
603598

604-
params_list[23]["reneging_time_distributions"] = {
599+
params_list[21]["reneging_time_distributions"] = {
605600
"Class 0": [ciw.dists.Exponential(1), ciw.dists.Exponential(1)]
606601
}
607602
self.assertRaises(
608603
ValueError,
609604
ciw.create_network_from_dictionary,
610-
params_list[23]
611-
)
612-
params_list[24]["reneging_destinations"] = {"Class 0": [-1, -1, -1]}
613-
self.assertRaises(
614-
ValueError,
615-
ciw.create_network_from_dictionary,
616-
params_list[24]
605+
params_list[21]
617606
)
618-
params_list[25]["reneging_destinations"] = {"Class 0": [7]}
607+
params_list[22]["class_change_time_distributions"] = {'Class 0': {'Class 0': None}, 'Class 1': {'Class 0': None, 'Class 1': None}}
619608
self.assertRaises(
620609
ValueError,
621610
ciw.create_network_from_dictionary,
622-
params_list[25]
611+
params_list[22]
623612
)
624-
params_list[26]["class_change_time_distributions"] = {'Class 0': {'Class 0': None}, 'Class 1': {'Class 0': None, 'Class 1': None}}
613+
params_list[23]["routing"]["Class 0"] = [['0.5']]
625614
self.assertRaises(
626615
ValueError,
627616
ciw.create_network_from_dictionary,
628-
params_list[26]
629-
)
630-
params_list[27]["routing"]["Class 0"] = [['0.5']]
631-
self.assertRaises(
632-
ValueError,
633-
ciw.create_network_from_dictionary,
634-
params_list[27]
617+
params_list[23]
635618
)
636619

637620
def test_raising_errors_routing(self):
@@ -942,7 +925,6 @@ def test_network_from_kwargs(self):
942925
number_of_servers=[2, 2],
943926
routing=[[0.0, 1.0], [0.2, 0.2]],
944927
reneging_time_distributions=[ciw.dists.Exponential(1), None],
945-
reneging_destinations=[2, -1],
946928
)
947929
self.assertEqual(N.number_of_nodes, 2)
948930
self.assertEqual(N.number_of_classes, 1)
@@ -956,8 +938,6 @@ def test_network_from_kwargs(self):
956938
str(N.customer_classes['Customer'].reneging_time_distributions[0]), "Exponential(rate=1)"
957939
)
958940
self.assertEqual(N.customer_classes['Customer'].reneging_time_distributions[1], None)
959-
self.assertEqual(N.customer_classes['Customer'].reneging_destinations[0], 2)
960-
self.assertEqual(N.customer_classes['Customer'].reneging_destinations[1], -1)
961941

962942
N = ciw.create_network(
963943
arrival_distributions={

ciw/tests/test_node.py

+45-3
Original file line numberDiff line numberDiff line change
@@ -1155,13 +1155,25 @@ def test_reneging_records(self):
11551155
self.assertEqual([r.queue_size_at_departure for r in reneging_recs], [1, 1])
11561156

11571157
def test_reneging_sends_to_destination(self):
1158+
class Jockey(ciw.routing.NodeRouting):
1159+
def next_node(self, ind):
1160+
"""
1161+
Chooses the exit node with probability 1.
1162+
"""
1163+
return self.simulation.nodes[-1]
1164+
1165+
def next_node_for_jockeying(self, ind):
1166+
"""
1167+
Chooses Node 2
1168+
"""
1169+
return self.simulation.nodes[2]
1170+
11581171
N = ciw.create_network(
11591172
arrival_distributions=[ciw.dists.Deterministic(7), None],
11601173
service_distributions=[ciw.dists.Deterministic(11), ciw.dists.Deterministic(2)],
1161-
routing=[[0.0, 0.0], [0.0, 0.0]],
1174+
routing=ciw.routing.NetworkRouting(routers=[Jockey(), ciw.routing.Leave()]),
11621175
number_of_servers=[1, 1],
11631176
reneging_time_distributions=[ciw.dists.Deterministic(3), None],
1164-
reneging_destinations=[2, -1],
11651177
)
11661178
Q = ciw.Simulation(N)
11671179
Q.simulate_until_max_time(20)
@@ -1180,6 +1192,37 @@ def test_reneging_sends_to_destination(self):
11801192
self.assertEqual([r.queue_size_at_arrival for r in recs_ind2], [1, 0])
11811193
self.assertEqual([r.queue_size_at_departure for r in recs_ind2], [1, 0])
11821194

1195+
def test_process_based_defaults_jockeyers_to_exit(self):
1196+
def route(self, ind):
1197+
"""
1198+
1-2
1199+
"""
1200+
return [1]
1201+
1202+
N = ciw.create_network(
1203+
arrival_distributions=[ciw.dists.Deterministic(7), None],
1204+
service_distributions=[ciw.dists.Deterministic(11), ciw.dists.Deterministic(2)],
1205+
routing=ciw.routing.ProcessBased(route_function=route),
1206+
number_of_servers=[1, 1],
1207+
reneging_time_distributions=[ciw.dists.Deterministic(3), None],
1208+
)
1209+
Q = ciw.Simulation(N)
1210+
Q.simulate_until_max_time(20)
1211+
recs = Q.get_all_records()
1212+
self.assertEqual([r.id_number for r in recs], [1, 2])
1213+
self.assertEqual([r.record_type for r in recs], ['service', 'renege'])
1214+
self.assertEqual([r.arrival_date for r in recs], [7, 14])
1215+
self.assertEqual([r.exit_date for r in recs], [18, 17])
1216+
self.assertEqual([r.waiting_time for r in recs], [0, 3])
1217+
self.assertEqual([r.node for r in recs], [1, 1])
1218+
self.assertEqual([r.service_time for r in recs], [11, nan])
1219+
self.assertEqual([r.service_start_date for r in recs], [7, nan])
1220+
self.assertEqual([r.service_end_date for r in recs], [18, nan])
1221+
self.assertEqual([r.server_id for r in recs], [1, nan])
1222+
self.assertEqual([r.customer_class for r in recs], ['Customer', 'Customer'])
1223+
self.assertEqual([r.queue_size_at_arrival for r in recs], [0, 1])
1224+
self.assertEqual([r.queue_size_at_departure for r in recs], [0, 1])
1225+
11831226
def test_reneging_none_dist(self):
11841227
N = ciw.create_network(
11851228
arrival_distributions={
@@ -1195,7 +1238,6 @@ def test_reneging_none_dist(self):
11951238
"Class 0": [ciw.dists.Deterministic(3)],
11961239
"Class 1": [None],
11971240
},
1198-
reneging_destinations={"Class 0": [-1], "Class 1": [-1]},
11991241
)
12001242
Q = ciw.Simulation(N)
12011243
self.assertTrue(Q.nodes[1].reneging)

docs/Guides/CustomerBehaviour/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ Contents:
88

99
baulking.rst
1010
reneging.rst
11+
jockeying.rst

0 commit comments

Comments
 (0)