Git Product home page Git Product logo

Comments (5)

guglielmosanchini avatar guglielmosanchini commented on August 11, 2024

Yes please, it would be very useful. If you have to model passengers arriving at a train station for example, it would be nice to have a fixed number of customers for each node too(corresponding in this example to a train)
Cause in the meantime I am using a "dummy solution", that is to say that when the last passenger of that train arrives, I put the next interarrival time equal to 100000hours, so that no one else comes to that node during the simulation.

from ciw.

geraintpalmer avatar geraintpalmer commented on August 11, 2024

Thank @KAI10

We'd be keen to see what you have done. I'm not sure I quite understand what you mean by "Fixed Number of Customers from each class" though, can you explain further please?

from ciw.

KAI10 avatar KAI10 commented on August 11, 2024

@geraintpalmer Sure.

Currently, there are features for multiple customer classes, and for simulating up to a certain number of customers. For example:

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],
)
Q.simulate_until_max_customers(100)

However, in the example above, number of customers of Class 0 and Class 1 is not directly specified. It is only ensured that the total number of customers simulated is 100.

The feature that I am talking about lets a user specify the number of customers for each class. An example is shown below:

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],
    number_of_customers_per_class = [80, 20] # specifying how many customers to simulate for each class, i.e 80 for Class 0, 20 for Class 1
)

Q.simulate_until_max_customers(100)

from ciw.

geraintpalmer avatar geraintpalmer commented on August 11, 2024

Hi @KAI10 I think this can be done by defining custom distributions:

As an example consider the following system:

N = ciw.create_network(
    arrival_distributions={
        'Class 0': [ciw.dists.Exponential(1)],
        'Class 1': [ciw.dists.Exponential(1)]},
    service_distributions={
        'Class 0': [ciw.dists.Exponential(3)],
        'Class 1': [ciw.dists.Exponential(3)]},
    number_of_servers=[2]
)

Running we get:

>>> ciw.seed(0)
>>> Q = ciw.Simulation(N)
>>> Q.simulate_until_max_time(100)
>>> recs = Q.get_all_records()

>>> len([r for r in recs if r.customer_class==0])
88

>>> len([r for r in recs if r.customer_class==1])
105

88 customers in of Class 0 and 105 customers of Class 1.


Now say we only want 40 customers of Class 0. We can difine a new Exponential distribution:

class LimitedExponential(ciw.dists.Exponential):
    def __init__(self, rate, limit):
        super().__init__(rate)
        self.limit = limit
        self.number_of_customers_sampled = 0
    
    def sample(self, t=None, ind=None):
        if self.number_of_customers_sampled < self.limit:
            self.number_of_customers_sampled += 1
            return super().sample()
        else:
            return float('Inf')

And using it we get:

>>> N = ciw.create_network(
...     arrival_distributions={
...         'Class 0': [LimitedExponential(1, 40)],
...         'Class 1': [ciw.dists.Exponential(1)]},
...     service_distributions={
...         'Class 0': [ciw.dists.Exponential(3)],
...         'Class 1': [ciw.dists.Exponential(3)]},
...     number_of_servers=[2]
... )

>>> ciw.seed(0)
>>> Q = ciw.Simulation(N)
>>> Q.simulate_until_max_time(100)
>>> recs = Q.get_all_records()

>>> len([r for r in recs if r.customer_class==0])
40

>>> len([r for r in recs if r.customer_class==1])
105

After 40 observations were sampled, the next arrival happens at time infinity, so no more arivals.


The above only works if all customers of that class arrive at the same node (hence using the same Distribution object). If you want to track the total number of customers of that class, then you'll need to define a true state dependent distribution that looks at the number of customers of that class that have arrived altogether. This is possible in the latest version of Ciw, v2.1.1 (https://ciw.readthedocs.io/en/latest/Guides/time_dependent.html), where the Distributions objects have access to the Simulation object itself, and hence its state.

For example:

class LimitedExponential(ciw.dists.Exponential):
    def __init__(self, rate, limit):
        super().__init__(rate)
        self.limit = limit
    
    def __repr__(self):
        return 'Limited Exponential ' + str(self.rate)
    
    def sample(self, t=None, ind=None):
        if self.simulation.nodes[0].number_of_individuals_per_class[0] < self.limit:
            return super().sample()
        else:
            return float('Inf')

So we are checking against self.simulation.nodes[0].number_of_individuals_per_class[0], the number of individuals of class 0 created at the arrival node.

>>> N = ciw.create_network(
...     arrival_distributions={
...         'Class 0': [LimitedExponential(1, 40), LimitedExponential(1, 40), LimitedExponential(1, 40)],
...         'Class 1': [ciw.dists.Exponential(1), ciw.dists.Exponential(1), ciw.dists.Exponential(1)]},
...     service_distributions={
...         'Class 0': [ciw.dists.Exponential(3), ciw.dists.Exponential(3), ciw.dists.Exponential(3)],
...         'Class 1': [ciw.dists.Exponential(3), ciw.dists.Exponential(3), ciw.dists.Exponential(3)]},
...     number_of_servers=[2, 2, 2],
...     routing={
...         'Class 0': [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
...         'Class 1': [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]}
... )

>>> ciw.seed(0)
>>> Q = ciw.Simulation(N)
>>> Q.simulate_until_max_time(100)
>>> recs = Q.get_all_records()

>>> len([r for r in recs if r.customer_class==0])
42

>>> len([r for r in recs if r.customer_class==1])
300

Notice here, even though we set the limit as 40, we sample 42 customers. Thats because we have 3 different nodes, all sampling a new customer when the number of customers is below 40. For example, when the number of customers is 39, all three will be allowed to sample a customer, because all three see that the simulation has not reached the limit of number of customers. I am not sure yet how to get around that.


I hope this helps, let me know what you think.

from ciw.

KAI10 avatar KAI10 commented on August 11, 2024

The solution you suggested is clean, at least cleaner than what I did, which was adding some conditions in arrival_node.py. I definitely prefer your solution. Thanks. I am closing the issue.

from ciw.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.