Ciw icon indicating copy to clipboard operation
Ciw copied to clipboard

Generalising class names

Open daffidwilde opened this issue 5 years ago • 8 comments

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.

daffidwilde avatar Dec 11 '19 15:12 daffidwilde

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.

geraintpalmer avatar Dec 11 '19 17:12 geraintpalmer

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)

drvinceknight avatar Dec 12 '19 08:12 drvinceknight

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?

geraintpalmer avatar Dec 12 '19 09:12 geraintpalmer

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.

daffidwilde avatar Dec 12 '19 09:12 daffidwilde

Sorry @geraintpalmer, it appears we replied at the same time. I concur with everything you’ve said.

daffidwilde avatar Dec 12 '19 09:12 daffidwilde

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:

geraintpalmer avatar Dec 12 '19 09:12 geraintpalmer

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.)

drvinceknight avatar Dec 12 '19 09:12 drvinceknight

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.

daffidwilde avatar Dec 12 '19 09:12 daffidwilde