Ciw
Ciw copied to clipboard
Generalising class names
When defining classes of individuals in the system, their names must be of the form "Class <i>" where i is an integer. For example:
>>> import ciw
>>> N = ciw.create_network(
... arrival_distributions={
... "child": [ciw.dists.Exponential(0.2)],
... "adult": [ciw.dists.Exponential(0.3)],
... },
... service_distributions={
... "child": [ciw.dists.Exponential(0.3)],
... "adult": [ciw.dists.Exponential(0.3)],
... },
... number_of_servers=[5],
... )
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-93-0eddbdb2b06e> in <module>
8 "adult": [ciw.dists.Exponential(0.3)],
9 },
---> 10 number_of_servers=[5],
11 )
~/anaconda3/lib/python3.7/site-packages/ciw/import_params.py in create_network(arrival_distributions, baulking_functions, class_change_matrices, number_of_servers, priority_classes, queue_capacities, service_distributions, routing, batching_distributions)
68 params['batching_distributions'] = batching_distributions
69
---> 70 return create_network_from_dictionary(params)
71
72
~/anaconda3/lib/python3.7/site-packages/ciw/import_params.py in create_network_from_dictionary(params_input)
109 """
110 params = fill_out_dictionary(params_input)
--> 111 validify_dictionary(params)
112 # Then make the Network object
113 arrivals = [params['arrival_distributions']['Class ' + str(clss)]
~/anaconda3/lib/python3.7/site-packages/ciw/import_params.py in validify_dictionary(params)
266 set(['Class ' + str(i) for i in range(params['number_of_classes'])]))
267 if not consistant_class_names:
--> 268 raise ValueError('Ensure correct names for customer classes.')
269 if all(isinstance(f, types.FunctionType) for f in params['routing']):
270 num_nodes_count = [
ValueError: Ensure correct names for customer classes.
Is this a necessary condition for ciw to function as it stands? If not, I don't see why the class names can't be recorded when the network is created and then placed when a record is created.
It isn't a major problem except when you have a number of external classes that you'd like to extract from ciw.
Hi @daffidwilde yes currently Ciw needs classes to be called 'Class 0', 'Class 1' etc. in order to force an ordering (unfortunatelt I think ciw's insides rely on this currently). I agree it would be much nicer to define own names, and would be more than happy for this to be changed.
Remind me @geraintpalmer, is there a purpose to class names?
What about not having names at all and instead of dictionaries you'd just have lists? (Essentially the names just become the indices of the list)
I think lists of lists were the original idea, but became difficult to read. Compare the current (from the tutorial:
>>> import ciw
>>> N = ciw.create_network(
... arrival_distributions={'Class 0': [ciw.dists.Exponential(1.0),
... ciw.dists.NoArrivals(),
... ciw.dists.NoArrivals()],
... 'Class 1': [ciw.dists.Exponential(2.0),
... ciw.dists.NoArrivals(),
... ciw.dists.NoArrivals()]},
... service_distributions={'Class 0': [ciw.dists.Exponential(4.0),
... ciw.dists.Exponential(1.0),
... ciw.dists.Deterministic(0.0)],
... 'Class 1': [ciw.dists.Exponential(6.0),
... ciw.dists.Deterministic(0.0),
... ciw.dists.Exponential(1.0)]},
... routing={'Class 0': [[0.0, 1.0, 0.0],
... [0.0, 0.0, 0.0],
... [0.0, 0.0, 0.0]],
... 'Class 1': [[0.0, 0.0, 1.0],
... [0.0, 0.0, 0.0],
... [0.0, 0.0, 0.0]]},
... number_of_servers=[1, 2, 3],
... )
To the possibility of lists of lists (there may be a better way to present this, but I think it does reduce readability):
>>> import ciw
>>> N = ciw.create_network(
... arrival_distributions=[[ciw.dists.Exponential(1.0),
... ciw.dists.NoArrivals(),
... ciw.dists.NoArrivals()],
... [ciw.dists.Exponential(2.0),
... ciw.dists.NoArrivals(),
... ciw.dists.NoArrivals()]],
... service_distributions=[[ciw.dists.Exponential(4.0),
... ciw.dists.Exponential(1.0),
... ciw.dists.Deterministic(0.0)],
... [ciw.dists.Exponential(6.0),
... ciw.dists.Deterministic(0.0),
... ciw.dists.Exponential(1.0)]],
... routing=[[[0.0, 1.0, 0.0],
... [0.0, 0.0, 0.0],
... [0.0, 0.0, 0.0]],
... [[0.0, 0.0, 1.0],
... [0.0, 0.0, 0.0],
... [0.0, 0.0, 0.0]]],
... number_of_servers=[1, 2, 3],
... )
It also makes setting priority classes more readable:
priority_classes={'Class 0': 0,
'Class 1': 1,
'Class 2': 1}
I think the idea of strings to represent customer classes. I like @daffidwilde's idea of allowing any string to represent a customer class, I think thta would make things much more reabable and meaningful to the user.
What do you both think @daffidwilde? @drvinceknight?
What about not having names at all and instead of dictionaries you'd just have lists? (Essentially the names just become the indices of the list)
I prefer — from a usability perspective — to have a dictionary rather than nested lists.
Internally, if the names are just being treated like indices then I imagine the dictionary could be iterated over in another way so ciw is completely class name invariant.
Sorry @geraintpalmer, it appears we replied at the same time. I concur with everything you’ve said.
Internally, if the names are just being treated like indices then I imagine the dictionary could be iterated over in another way so ciw is completely class name invariant.
Yes I like this. Shouldn't be too many changes - and would still be back compatible :+1:
I'm not sure I see the improved readability using dictionaries but:
would still be back compatible
:+1:
(FYI, depending on how it's implemented given that internally lists are in fact just dictionaries it could be possible to do this in such a way where you could pass lists without it actually needing special consideration.)
it could be possible to do this in such a way where you could pass lists without it actually needing special consideration.
That would be jolly nice to have both. I suspect that my suggestion would be implemented in a very similar way.