Skip to content

Commit 6452c8c

Browse files
Merge pull request #149 from CiwPython/interrupted-inds-bug-fix
Interrupted inds bug fix
2 parents 1677466 + ece1eb1 commit 6452c8c

10 files changed

Lines changed: 90 additions & 23 deletions

File tree

43 Bytes
Binary file not shown.

ciw/auxiliary.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,10 @@ def truncated_normal(mean, sd):
4444
sample = random.normalvariate(mean, sd)
4545
while sample <= 0.0:
4646
sample = random.normalvariate(mean, sd)
47-
return sample
47+
return sample
48+
49+
def flatten_list(list_of_lists):
50+
flat = []
51+
for a_list in list_of_lists:
52+
flat += a_list
53+
return flat

ciw/individual.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def __init__(self, id_number, customer_class=0, priority_class=0):
2424
self.queue_size_at_arrival = False
2525
self.queue_size_at_departure = False
2626
self.destination = False
27+
self.interrupted = False
2728

2829
def __repr__(self):
2930
"""

ciw/node.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import networkx as nx
88

9-
from .auxiliary import random_choice
9+
from .auxiliary import random_choice, flatten_list
1010
from .data_record import DataRecord
1111
from .server import Server
1212

@@ -54,19 +54,20 @@ def __init__(self, id_, simulation):
5454
else:
5555
self.next_event_date = float("Inf")
5656
self.blocked_queue = []
57+
self.len_blocked_queue = 0
5758
if not isinf(self.c):
5859
self.servers = self.create_starting_servers()
5960
self.highest_id = self.c
6061
self.simulation.deadlock_detector.initialise_at_node(self)
6162
self.preempt = node.preempt
6263
self.interrupted_individuals = []
64+
self.number_interrupted_individuals = 0
6365
self.all_servers_total = []
6466
self.all_servers_busy = []
6567

6668
@property
6769
def all_individuals(self):
68-
return [i for priority_class in self.individuals
69-
for i in priority_class]
70+
return flatten_list(self.individuals)
7071

7172
def __repr__(self):
7273
"""
@@ -132,12 +133,20 @@ def begin_interrupted_individuals_service(self, current_time, srvr):
132133
resampling service time)
133134
"""
134135
ind = [i for i in self.interrupted_individuals][0]
136+
if ind.is_blocked:
137+
node_blocked_to = self.simulation.nodes[ind.destination]
138+
ind.destination = False
139+
node_blocked_to.blocked_queue.remove((self.id_number, ind.id_number))
140+
node_blocked_to.len_blocked_queue -= 1
141+
ind.is_blocked = False
135142
ind.service_time = self.get_service_time(ind.customer_class,
136143
current_time)
137144
ind.service_end_date = self.increment_time(self.get_now(current_time),
138145
ind.service_time)
146+
ind.interrupted = False
139147
self.attach_server(srvr, ind)
140148
self.interrupted_individuals.remove(ind)
149+
self.number_interrupted_individuals -= 1
141150

142151
def begin_service_if_possible_change_shift(self, current_time):
143152
"""
@@ -146,14 +155,16 @@ def begin_service_if_possible_change_shift(self, current_time):
146155
"""
147156
free_servers = [s for s in self.servers if not s.busy]
148157
for srvr in free_servers:
149-
if len(self.interrupted_individuals) > 0:
158+
if self.number_interrupted_individuals > 0:
150159
self.begin_interrupted_individuals_service(current_time, srvr)
151-
elif len([i for i in self.all_individuals if not i.server]) > 0:
152-
ind = [i for i in self.all_individuals if not i.server][0]
153-
ind.service_start_date = self.get_now(current_time)
154-
ind.service_end_date = self.increment_time(
155-
ind.service_start_date, ind.service_time)
156-
self.attach_server(srvr, ind)
160+
else:
161+
inds_without_server = [i for i in self.all_individuals if not i.server]
162+
if len(inds_without_server) > 0:
163+
ind = inds_without_server[0]
164+
ind.service_start_date = self.get_now(current_time)
165+
ind.service_end_date = self.increment_time(
166+
ind.service_start_date, ind.service_time)
167+
self.attach_server(srvr, ind)
157168

158169
def begin_service_if_possible_release(self, current_time):
159170
"""
@@ -162,14 +173,16 @@ def begin_service_if_possible_release(self, current_time):
162173
"""
163174
if self.free_server() and (not isinf(self.c)):
164175
srvr = self.find_free_server()
165-
if len(self.interrupted_individuals) > 0:
176+
if self.number_interrupted_individuals > 0:
166177
self.begin_interrupted_individuals_service(current_time, srvr)
167-
elif len([i for i in self.all_individuals if not i.server]) > 0:
168-
ind = [i for i in self.all_individuals if not i.server][0]
169-
ind.service_start_date = self.get_now(current_time)
170-
ind.service_end_date = self.increment_time(
171-
ind.service_start_date, ind.service_time)
172-
self.attach_server(srvr, ind)
178+
else:
179+
inds_without_server = [i for i in self.all_individuals if not i.server]
180+
if len(inds_without_server) > 0:
181+
ind = inds_without_server[0]
182+
ind.service_start_date = self.get_now(current_time)
183+
ind.service_end_date = self.increment_time(
184+
ind.service_start_date, ind.service_time)
185+
self.attach_server(srvr, ind)
173186

174187
def block_individual(self, individual, next_node):
175188
"""
@@ -181,8 +194,10 @@ def block_individual(self, individual, next_node):
181194
individual.customer_class)
182195
next_node.blocked_queue.append(
183196
(self.id_number, individual.id_number))
197+
next_node.len_blocked_queue += 1
184198
self.simulation.deadlock_detector.action_at_blockage(
185199
individual, next_node)
200+
self.simulation.unchecked_blockage = True
186201

187202
def change_customer_class(self,individual):
188203
"""
@@ -192,7 +207,7 @@ def change_customer_class(self,individual):
192207
if self.class_change:
193208
individual.previous_class = individual.customer_class
194209
individual.customer_class = random_choice(
195-
range(len(self.class_change)),
210+
range(self.simulation.network.number_of_classes),
196211
self.class_change[individual.previous_class])
197212
individual.prev_priority_class = individual.priority_class
198213
individual.priority_class = self.simulation.network.priority_class_mapping[individual.customer_class]
@@ -370,7 +385,7 @@ def release_blocked_individual(self, current_time):
370385
Releases an individual who becomes unblocked
371386
when another individual is released.
372387
"""
373-
if len(self.blocked_queue) > 0 and self.number_of_individuals < self.node_capacity:
388+
if self.len_blocked_queue > 0 and self.number_of_individuals < self.node_capacity:
374389
node_to_receive_from = self.simulation.nodes[
375390
self.blocked_queue[0][0]]
376391
individual_to_receive_index = [ind.id_number
@@ -379,6 +394,11 @@ def release_blocked_individual(self, current_time):
379394
individual_to_receive = node_to_receive_from.all_individuals[
380395
individual_to_receive_index]
381396
self.blocked_queue.pop(0)
397+
self.len_blocked_queue -= 1
398+
if individual_to_receive.interrupted:
399+
individual_to_receive.interrupted = False
400+
node_to_receive_from.interrupted_individuals.remove(individual_to_receive)
401+
node_to_receive_from.number_interrupted_individuals -= 1
382402
node_to_receive_from.release(individual_to_receive_index,
383403
self, current_time)
384404

@@ -409,13 +429,16 @@ def take_servers_off_duty(self):
409429
s.shift_end = self.next_event_date
410430
if s.cust is not False:
411431
self.interrupted_individuals.append(s.cust)
432+
s.cust.interrupted = True
433+
self.number_interrupted_individuals += 1
412434
self.interrupted_individuals[-1].service_end_date = False
413435
self.interrupted_individuals[-1].service_time = False
414436
self.interrupted_individuals.sort(key=lambda x: (x.priority_class,
415437
x.arrival_date))
416438
for obs in to_delete:
417439
self.kill_server(obs)
418440

441+
419442
def update_next_event_date(self, current_time):
420443
"""
421444
Finds the time of the next event at this node

ciw/simulation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def __init__(self, network,
5252
self.times_to_deadlock = {}
5353
self.rejection_dict = self.nodes[0].rejection_dict
5454
self.baulked_dict = self.nodes[0].baulked_dict
55+
self.unchecked_blockage = False
5556

5657
def __repr__(self):
5758
"""
@@ -268,7 +269,9 @@ def simulate_until_deadlock(self):
268269
current_state = self.statetracker.hash_state()
269270
if current_state not in self.times_dictionary:
270271
self.times_dictionary[current_state] = current_time
271-
deadlocked = self.deadlock_detector.detect_deadlock()
272+
if self.unchecked_blockage:
273+
deadlocked = self.deadlock_detector.detect_deadlock()
274+
self.unchecked_blockage = False
272275
if deadlocked:
273276
time_of_deadlock = current_time
274277
current_time = next_active_node.next_event_date

ciw/tests/test_auxiliary.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,10 @@ def test_randomchoice(self):
7777
self.assertEqual(choice_counts, {'Exit Node': 100})
7878
self.assertEqual(r1, r2)
7979

80-
80+
def test_flatten_list(self):
81+
for seed in range(20):
82+
random.seed(seed)
83+
all_classes = [[random.random() for _ in range(random.randrange(3, 30, 1))] for _ in range(random.randrange(5, 20, 1))]
84+
A = [i for priority in all_classes for i in priority]
85+
B = ciw.flatten_list(all_classes)
86+
self.assertEqual(A, B)

ciw/tests/test_node.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ def test_release_blocked_individual_method(self):
300300
'Individual 108'])
301301

302302
N1.blocked_queue = [(1, 1), (2, 100)]
303+
N1.len_blocked_queue = 2
303304
rel_ind = N1.individuals[0].pop(0)
304305
N1.detatch_server(rel_ind.server, rel_ind)
305306

ciw/tests/test_simulation.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,3 +768,29 @@ def test_find_generators(self):
768768
self.assertTrue(isinstance(Q_all.generators['Ser'][0][1], cycle))
769769
self.assertTrue(isinstance(Q_all.generators['Ser'][1][0], cycle))
770770
self.assertTrue(isinstance(Q_all.generators['Ser'][1][1], cycle))
771+
772+
def test_schedules_and_blockages_work_together(self):
773+
N = ciw.create_network(
774+
Arrival_distributions={
775+
'Class 0': [['Exponential', 0.5], ['Exponential', 0.9]],
776+
'Class 1': [['Exponential', 0.6], ['Exponential', 1.0]]},
777+
Service_distributions={
778+
'Class 0': [['Exponential', 0.8], ['Exponential', 1.2]],
779+
'Class 1': [['Exponential', 0.5], ['Exponential', 1.0]]},
780+
Number_of_servers=[([[1, 10], [0, 20], [2, 30]], True), 2],
781+
Transition_matrices={
782+
'Class 0': [[0.1, 0.3], [0.2, 0.2]],
783+
'Class 1': [[0.0, 0.6], [0.2, 0.1]]},
784+
Class_change_matrices={
785+
'Node 1': [[0.8, 0.2],
786+
[0.5, 0.5]],
787+
'Node 2': [[1.0, 0.0],
788+
[0.1, 0.9]]},
789+
Queue_capacities=[2, 2]
790+
)
791+
792+
ciw.seed(6)
793+
Q = ciw.Simulation(N, deadlock_detector='StateDigraph')
794+
Q.simulate_until_deadlock()
795+
ttd = Q.times_to_deadlock[((0, 0), (0, 0))]
796+
self.assertEqual(round(ttd, 5), 92.49468)

docs/Guides/set_distributions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ The system uses the following eight distributions:
4343
+ Always sample 0.2.
4444
+ :code:`['Empirical', [0.1, 0.1, 0.1, 0.2]`:
4545
+ Randomly sample from the numbers 0.1, 0.1, 0.1 and 0.2.
46-
+ :code:`['Custom', [[0.5, 0.2], [0.5, 0.4]]]`:
46+
+ :code:`['Custom', [0.2, 0.4], [0.5, 0.5]]`:
4747
+ Sample 0.2 half the time, and 0.4 half the time.
4848
+ :code:`['Exponential', 6.0]`:
4949
+ Sample from the `exponential <https://en.wikipedia.org/wiki/Exponential_distribution>`_ distribution with parameter :math:`\lambda = 6.0`. Expected mean of 0.1666...

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
'sphinx.ext.autodoc',
3434
'sphinx.ext.mathjax',
3535
'sphinx.ext.viewcode',
36+
'sphinx.ext.imgconverter',
3637
]
3738

3839
# Add any paths that contain templates here, relative to this directory.

0 commit comments

Comments
 (0)