Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalising class names #167

Open
daffidwilde opened this issue Dec 11, 2019 · 8 comments
Open

Generalising class names #167

daffidwilde opened this issue Dec 11, 2019 · 8 comments

Comments

@daffidwilde
Copy link
Contributor

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.

@geraintpalmer
Copy link
Member

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.

@drvinceknight
Copy link
Contributor

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)

@geraintpalmer
Copy link
Member

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?

@daffidwilde
Copy link
Contributor Author

daffidwilde commented Dec 12, 2019

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
Copy link
Contributor Author

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

@geraintpalmer
Copy link
Member

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 👍

@drvinceknight
Copy link
Contributor

I'm not sure I see the improved readability using dictionaries but:

would still be back compatible

👍

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

@daffidwilde
Copy link
Contributor Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants