Skip to content

Commit e57e18a

Browse files
Merge pull request #97 from CiwPython/time_dependent
Time dependent
2 parents 869382e + 33d8e9d commit e57e18a

File tree

8 files changed

+258
-55
lines changed

8 files changed

+258
-55
lines changed

ciw/arrival_node.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ def __repr__(self):
3333

3434
def decide_baulk(self, next_node, next_individual):
3535
"""
36-
Either makes an individual baulk, or sends the individual to the next node
36+
Either makes an individual baulk, or sends the individual
37+
to the next node
3738
"""
3839
if next_node.baulking_functions[self.next_class] == None:
3940
self.send_individual(next_node, next_individual)
4041
else:
4142
rnd_num = random()
42-
if rnd_num < next_node.baulking_functions[self.next_class](next_node.number_of_individuals):
43+
if rnd_num < next_node.baulking_functions[self.next_class](
44+
next_node.number_of_individuals):
4345
self.record_baulk(next_node)
4446
else:
4547
self.send_individual(next_node, next_individual)
@@ -65,7 +67,8 @@ def have_event(self):
6567
Send new arrival to relevent node.
6668
"""
6769
self.number_of_individuals += 1
68-
priority_class = self.simulation.network.priority_class_mapping[self.next_class]
70+
priority_class = self.simulation.network.priority_class_mapping[
71+
self.next_class]
6972
next_individual = Individual(self.number_of_individuals,
7073
self.next_class,
7174
priority_class)
@@ -75,7 +78,8 @@ def have_event(self):
7578
self.next_class] = self.increment_time(
7679
self.event_dates_dict[self.next_node][
7780
self.next_class], self.inter_arrival(
78-
self.next_node, self.next_class))
81+
self.next_node, self.next_class,
82+
self.next_event_date))
7983
self.find_next_event_date()
8084

8185
def increment_time(self, original, increment):
@@ -92,12 +96,15 @@ def initialise_event_dates_dict(self):
9296
for nd in self.event_dates_dict:
9397
for cls in self.event_dates_dict[nd]:
9498
self.event_dates_dict[nd][
95-
cls] = self.simulation.inter_arrival_times[nd][cls]()
99+
cls] = self.inter_arrival(nd, cls, 0.0)
96100

97-
def inter_arrival(self, nd, cls):
101+
def inter_arrival(self, nd, cls, current_time):
98102
"""
99103
Samples the inter-arrival time for next class and node.
100104
"""
105+
if self.simulation.network.customer_classes[
106+
cls].arrival_distributions[nd-1][0] == "TimeDependent":
107+
return self.simulation.inter_arrival_times[nd][cls](current_time)
101108
return self.simulation.inter_arrival_times[nd][cls]()
102109

103110
def record_baulk(self, next_node):

ciw/exactnode.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ def increment_time(self, original, increment):
1414
"""
1515
return Decimal(str(original)) + Decimal(str(increment))
1616

17-
def get_service_time(self, cls):
17+
def get_service_time(self, cls, current_time):
1818
"""
1919
Returns a service time for the given customer class
2020
"""
21+
if self.simulation.network.customer_classes[cls].service_distributions[self.id_number-1][0] == "TimeDependent":
22+
return Decimal(str(self.simulation.service_times[self.id_number][cls](current_time)))
2123
return Decimal(str(self.simulation.service_times[self.id_number][cls]()))
2224

25+
2326
def get_now(self, current_time):
2427
"""
2528
Gets the current time
@@ -39,8 +42,11 @@ def increment_time(self, original, increment):
3942
"""
4043
return Decimal(str(original)) + Decimal(str(increment))
4144

42-
def inter_arrival(self, nd, cls):
45+
def inter_arrival(self, nd, cls, current_time):
4346
"""
4447
Samples the inter-arrival time for next class and node.
4548
"""
49+
if self.simulation.network.customer_classes[cls].arrival_distributions[nd-1][0] == "TimeDependent":
50+
return Decimal(str(self.simulation.inter_arrival_times[nd][cls](current_time)))
4651
return Decimal(str(self.simulation.inter_arrival_times[nd][cls]()))
52+

ciw/import_params.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
def create_network(params):
88
"""
9-
Identifies the type of parameters that is input and calls the correct function
9+
Identifies the type of parameters that is input and
10+
calls the correct function
1011
"""
1112
if isinstance(params, dict):
1213
return create_network_from_dictionary(params)
@@ -174,7 +175,8 @@ def validify_dictionary(params):
174175
if not set(dists).issubset(set([
175176
'Uniform', 'Triangular', 'Deterministic',
176177
'Exponential', 'Gamma', 'Lognormal',
177-
'Weibull', 'Empirical', 'Custom', 'UserDefined'])):
178+
'Weibull', 'Empirical', 'Custom', 'UserDefined',
179+
'TimeDependent'])):
178180
raise ValueError('Ensure that valid Arrival and Service Distributions are used.')
179181
neg_numservers = any([(isinstance(obs, int) and obs <= 0) for obs in params['Number_of_servers']])
180182
valid_capacities = all([((isinstance(obs, int) and obs >= 0) or obs=='Inf') for obs in params['Queue_capacities']])

ciw/node.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ def __init__(self, id_, simulation):
2828
self.schedule = [list(pair) for pair in zip(boundaries, servers)]
2929
self.c = self.schedule[0][1]
3030
raw_schedule_boundaries = [row[0] for row in raw_schedule]
31-
self.date_generator = self.date_from_schedule_generator(raw_schedule_boundaries)
31+
self.date_generator = self.date_from_schedule_generator(
32+
raw_schedule_boundaries)
3233
self.next_shift_change = next(self.date_generator)
3334
else:
3435
self.c = node.number_of_servers
@@ -114,7 +115,7 @@ def begin_service_if_possible_accept(self,
114115
"""
115116
next_individual.arrival_date = self.get_now(current_time)
116117
next_individual.service_time = self.get_service_time(
117-
next_individual.customer_class)
118+
next_individual.customer_class, current_time)
118119
if self.free_server():
119120
if self.c < float('Inf'):
120121
self.attach_server(self.find_free_server(),
@@ -130,8 +131,10 @@ def begin_interrupted_individuals_service(self, current_time, srvr):
130131
"""
131132
ind = [i for i in self.interrupted_individuals][0]
132133
self.attach_server(srvr, ind)
133-
ind.service_time = self.get_service_time(ind.customer_class)
134-
ind.service_end_date = self.increment_time(self.get_now(current_time), ind.service_time)
134+
ind.service_time = self.get_service_time(ind.customer_class,
135+
current_time)
136+
ind.service_end_date = self.increment_time(self.get_now(current_time),
137+
ind.service_time)
135138
self.interrupted_individuals.remove(ind)
136139

137140
def begin_service_if_possible_change_shift(self, current_time):
@@ -348,10 +351,13 @@ def release_blocked_individual(self, current_time):
348351
node_to_receive_from.release(individual_to_receive_index,
349352
self, current_time)
350353

351-
def get_service_time(self, cls):
354+
def get_service_time(self, cls, current_time):
352355
"""
353356
Returns a service time for the given customer class
354357
"""
358+
if self.simulation.network.customer_classes[cls].service_distributions[
359+
self.id_number-1][0] == 'TimeDependent':
360+
return self.simulation.service_times[self.id_number][cls](current_time)
355361
return self.simulation.service_times[self.id_number][cls]()
356362

357363
def take_servers_off_duty(self):
@@ -429,11 +435,15 @@ def write_individual_record(self, individual):
429435
individual.destination = False
430436

431437
def date_from_schedule_generator(self, boundaries):
432-
"""A generator that yields the next time according to a given schedule"""
438+
"""
439+
A generator that yields the next time according to a given schedule.
440+
"""
433441
boundaries_len = len(boundaries)
434442
index = 0
435443
date = 0
436444
while True:
437-
date = self.increment_time(boundaries[index % boundaries_len], (index) // boundaries_len * boundaries[-1])
445+
date = self.increment_time(
446+
boundaries[index % boundaries_len],
447+
(index) // boundaries_len * boundaries[-1])
438448
index += 1
439449
yield date

ciw/simulation.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ class Simulation(object):
2222
"""
2323
Overall simulation class
2424
"""
25-
def __init__(self, network, exact=False, name='Simulation', tracker=False, deadlock_detector=False,
25+
def __init__(self, network,
26+
exact=False,
27+
name='Simulation',
28+
tracker=False,
29+
deadlock_detector=False,
2630
node_class=None, arrival_node_class=None):
2731
"""
2832
Initialise a queue instance.
@@ -62,9 +66,16 @@ def check_userdef_dist(self, func):
6266
"""
6367
sample = func()
6468
if not isinstance(sample, float) or sample < 0:
65-
raise ValueError("UserDefined function must return positive float.")
69+
raise ValueError("UserDefined func must return positive float.")
6670
return sample
6771

72+
def check_timedependent_dist(self, func, current_time):
73+
sample = func(current_time)
74+
if not isinstance(sample, float) or sample < 0:
75+
raise ValueError("TimeDependent func must return positive float.")
76+
return sample
77+
78+
6879
def choose_tracker(self, tracker, deadlock_detector):
6980
"""
7081
Chooses the state tracker to use for the simulation.
@@ -128,6 +139,8 @@ def find_distributions(self, n, c, kind):
128139
empirical_dist = self.import_empirical(self.source(c, n, kind)[1])
129140
return lambda : nprandom.choice(empirical_dist)
130141
return lambda : nprandom.choice(self.source(c, n, kind)[1])
142+
if self.source(c, n, kind)[0] == 'TimeDependent':
143+
return lambda t : self.check_timedependent_dist(self.source(c, n, kind)[1], t)
131144

132145
def find_next_active_node(self):
133146
"""

ciw/tests/test_arrival_node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def test_no_arrivals_example(self):
126126
Q = ciw.Simulation(ciw.create_network(params))
127127
AN = Q.nodes[0]
128128
self.assertEqual(AN.simulation.network.customer_classes[0].arrival_distributions[0], 'NoArrivals')
129-
self.assertEqual(AN.inter_arrival(1, 0), float('Inf'))
129+
self.assertEqual(AN.inter_arrival(1, 0, 0.0), float('Inf'))
130130

131131
def test_rejection_dict(self):
132132
params = {'Arrival_distributions':[['Deterministic', 3.0], ['Deterministic', 4.0]],
@@ -137,7 +137,7 @@ def test_rejection_dict(self):
137137
Q = ciw.Simulation(ciw.create_network(params))
138138
self.assertEqual(Q.rejection_dict, {1: {0: []}, 2: {0:[]}})
139139
Q.simulate_until_max_time(20)
140-
self.assertEqual(Q.rejection_dict, {1: {0: [9.0, 12.0, 18.0]}, 2: {0:[12.0, 16.0]}})
140+
self.assertEqual(Q.rejection_dict, {1: {0: [9.0, 12.0, 18.0]}, 2: {0: [12.0, 16.0]}})
141141

142142
def test_send_individual(self):
143143
params = {'Arrival_distributions':[['Exponential', 3.0]],

0 commit comments

Comments
 (0)