Skip to content

Commit 78d270b

Browse files
Merge pull request #196 from CiwPython/dynamic-customer-classes
Dynamic customer classes
2 parents 7f700e2 + d137424 commit 78d270b

18 files changed

+972
-129
lines changed

ciw/data_record.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
DataRecord = namedtuple('Record', [
44
'id_number',
55
'customer_class',
6+
'original_customer_class',
67
'node',
78
'arrival_date',
89
'waiting_time',

ciw/import_params.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def get_distribution(dist):
1111
Returns instances of the distribution classes that
1212
correspond to the indicator string in the .yml file.
1313
"""
14-
if dist is None:
14+
if (dist is None) or (dist == 'None'):
1515
return None
1616
if dist[0] == 'Uniform':
1717
return ciw.dists.Uniform(dist[1], dist[2])
@@ -38,6 +38,7 @@ def get_distribution(dist):
3838
def create_network(arrival_distributions=None,
3939
baulking_functions=None,
4040
class_change_matrices=None,
41+
class_change_time_distributions=None,
4142
number_of_servers=None,
4243
priority_classes=None,
4344
queue_capacities=None,
@@ -47,7 +48,8 @@ def create_network(arrival_distributions=None,
4748
ps_thresholds=None,
4849
server_priority_functions=None,
4950
reneging_time_distributions=None,
50-
reneging_destinations=None):
51+
reneging_destinations=None,
52+
):
5153
"""
5254
Takes in kwargs, creates dictionary.
5355
"""
@@ -64,6 +66,8 @@ def create_network(arrival_distributions=None,
6466
params['baulking_functions'] = baulking_functions
6567
if class_change_matrices != None:
6668
params['class_change_matrices'] = class_change_matrices
69+
if class_change_time_distributions is not None:
70+
params['class_change_time_distributions'] = class_change_time_distributions
6771
if priority_classes != None:
6872
params['priority_classes'] = priority_classes
6973
if queue_capacities != None:
@@ -116,6 +120,10 @@ def create_network_from_yml(directory_name):
116120
for clss in params['reneging_time_distributions']:
117121
dists = [get_distribution(dist) for dist in params['reneging_time_distributions'][clss]]
118122
params['reneging_time_distributions'][clss] = dists
123+
if 'class_change_time_distributions' in params:
124+
for clss, dist_original in enumerate(params['class_change_time_distributions']):
125+
dists = [get_distribution(dist) for dist in dist_original]
126+
params['class_change_time_distributions'][clss] = dists
119127
validify_dictionary(params)
120128
return create_network_from_dictionary(params)
121129

@@ -127,6 +135,8 @@ def create_network_from_dictionary(params_input):
127135
params = fill_out_dictionary(params_input)
128136
validify_dictionary(params)
129137
# Then make the Network object
138+
number_of_classes = params['number_of_classes']
139+
number_of_nodes = params['number_of_nodes']
130140
arrivals = [params['arrival_distributions']['Class ' + str(clss)]
131141
for clss in range(len(params['arrival_distributions']))]
132142
services = [params['service_distributions']['Class ' + str(clss)]
@@ -136,17 +146,23 @@ def create_network_from_dictionary(params_input):
136146
else:
137147
routing = [params['routing']['Class ' + str(clss)]
138148
for clss in range(len(params['routing']))]
139-
priorities = [params['priority_classes']['Class ' + str(clss)]
140-
for clss in range(len(params['priority_classes']))]
149+
if isinstance(params['priority_classes'], dict):
150+
priorities = [params['priority_classes']['Class ' + str(clss)]
151+
for clss in range(len(params['priority_classes']))]
152+
preempt_priorities = [False for _ in range(number_of_nodes)]
153+
if isinstance(params['priority_classes'], tuple):
154+
priorities = [params['priority_classes'][0]['Class ' + str(clss)]
155+
for clss in range(len(params['priority_classes'][0]))]
156+
preempt_priorities = params['priority_classes'][1]
141157
baulking_functions = [params['baulking_functions']['Class ' + str(clss)]
142158
for clss in range(len(params['baulking_functions']))]
143159
batches = [params['batching_distributions']['Class ' + str(clss)]
144160
for clss in range(len(params['batching_distributions']))]
145-
number_of_classes = params['number_of_classes']
146-
number_of_nodes = params['number_of_nodes']
147161
queueing_capacities = [float(i) if i == "Inf" else i for i in params['queue_capacities']]
148162
class_change_matrices = params.get('class_change_matrices',
149163
{'Node ' + str(nd + 1): None for nd in range(number_of_nodes)})
164+
class_change_time_distributions = params.get('class_change_time_distributions',
165+
[[None for clss1 in range(number_of_classes)] for clss2 in range(number_of_classes)])
150166
number_of_servers, schedules, nodes, classes, preempts = [], [], [], [], []
151167
for c in params['number_of_servers']:
152168
if isinstance(c, (tuple, list)):
@@ -174,6 +190,7 @@ def create_network_from_dictionary(params_input):
174190
class_change_matrices['Node ' + str(nd + 1)],
175191
schedules[nd],
176192
preempts[nd],
193+
preempt_priorities[nd],
177194
params['ps_thresholds'][nd],
178195
params['server_priority_functions'][nd]))
179196
for clss in range(number_of_classes):
@@ -186,7 +203,8 @@ def create_network_from_dictionary(params_input):
186203
baulking_functions[clss],
187204
batches[clss],
188205
params['reneging_time_distributions']['Class ' + str(clss)],
189-
params['reneging_destinations']['Class ' + str(clss)]))
206+
params['reneging_destinations']['Class ' + str(clss)],
207+
class_change_time_distributions[clss]))
190208
else:
191209
classes.append(CustomerClass(
192210
arrivals[clss],
@@ -196,7 +214,8 @@ def create_network_from_dictionary(params_input):
196214
baulking_functions[clss],
197215
batches[clss],
198216
params['reneging_time_distributions']['Class ' + str(clss)],
199-
params['reneging_destinations']['Class ' + str(clss)]))
217+
params['reneging_destinations']['Class ' + str(clss)],
218+
class_change_time_distributions[clss]))
200219

201220
n = Network(nodes, classes)
202221
if all(isinstance(f, types.FunctionType) for f in params['routing']):
@@ -365,6 +384,10 @@ def validify_dictionary(params):
365384
for row in nd:
366385
if sum(row) > 1.0 or min(row) < 0.0 or max(row) > 1.0:
367386
raise ValueError('Ensure that class change matrix is valid.')
387+
if 'class_change_time_distributions' in params:
388+
wrong_num_classes = any(len(row) != params['number_of_classes'] for row in params['class_change_time_distributions']) or (len(params['class_change_time_distributions']) != params['number_of_classes'])
389+
if wrong_num_classes:
390+
raise ValueError('Ensure correct number of customer classes used in class_change_time_distributions.')
368391
for n in params['number_of_servers']:
369392
if isinstance(n, str) and n != 'Inf':
370393
if n not in params:

ciw/individual.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def __init__(self, id_number, customer_class=0, priority_class=0, simulation=Fal
1919
self.previous_class = customer_class
2020
self.priority_class = priority_class
2121
self.prev_priority_class = priority_class
22+
self.original_class = customer_class
2223
self.is_blocked = False
2324
self.server = False
2425
self.queue_size_at_arrival = False

ciw/network.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ def __init__(self,
1212
queueing_capacity,
1313
class_change_matrix=None,
1414
schedule=None,
15-
preempt=False,
15+
schedule_preempt=False,
16+
priority_preempt=False,
1617
ps_threshold=1,
1718
server_priority_function=None):
1819
"""
@@ -22,10 +23,11 @@ def __init__(self,
2223
self.queueing_capacity = queueing_capacity
2324
self.class_change_matrix = class_change_matrix
2425
self.schedule = schedule
25-
self.preempt = preempt
26+
self.schedule_preempt = schedule_preempt
27+
self.priority_preempt = priority_preempt
2628
self.ps_threshold = ps_threshold
2729
self.server_priority_function = server_priority_function
28-
30+
self.class_change_time = False
2931

3032
class CustomerClass(object):
3133
"""
@@ -46,7 +48,8 @@ def __init__(self,
4648
baulking_functions,
4749
batching_distributions,
4850
reneging_time_distributions,
49-
reneging_destinations):
51+
reneging_destinations,
52+
class_change_time_distributions):
5053
"""
5154
Initialises the CutomerCass object.
5255
"""
@@ -58,6 +61,7 @@ def __init__(self,
5861
self.baulking_functions = baulking_functions
5962
self.reneging_time_distributions = reneging_time_distributions
6063
self.reneging_destinations = reneging_destinations
64+
self.class_change_time_distributions = class_change_time_distributions
6165

6266
class Network(object):
6367
"""
@@ -81,3 +85,6 @@ def __init__(self, service_centres, customer_classes):
8185
node.reneging = False
8286
else:
8387
node.reneging = True
88+
if any(dist is not None for clss in customer_classes for dist in clss.class_change_time_distributions):
89+
for node in self.service_centres:
90+
node.class_change_time = True

0 commit comments

Comments
 (0)