Home Can Ciw Queues be Initialized With Individuals?
Post
Cancel

Can Ciw Queues be Initialized With Individuals?

Although this issue has been considered in the past, Ciw does not have an officially supported way to begin a waitlist (i.e. queue) with a collection of individuals. For simulated real-world systems it can be valuable for the waitlist to be initialized in a realistic state, but Ciw will begin the simulation with the waitlists being empty.

Here is a small example of putting individuals on the waitlist.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import ciw

import pandas as pd

N = ciw.create_network(
    arrival_distributions=[ciw.dists.Exponential(rate=0.2)],
    service_distributions=[ciw.dists.Exponential(rate=0.1)],
    number_of_servers=[3]
)


Q = ciw.Simulation(N)

# Directly setting individuals to Node 1 (a queue)
Q.nodes[1].individuals = [[ciw.Individual(-i) for i in range(100)]]

Q.simulate_until_max_time(1000)

recs = pd.DataFrame(Q.get_all_records())

Note that not everything about the individuals will be handled as an ordinary instance of Individual as they would be created in the arrival node with certain attributes assigned to them. We can take a look at how it affects the results by running print(recs[recs.id_number <= 0].sort_values(by='id_number').to_markdown()) after running the previous code block in an interactive session. This produced the following table:

 id_numbercustomer_classoriginal_customer_classnodearrival_datewaiting_timeservice_start_dateservice_timeservice_end_datetime_blockedexit_datedestinationqueue_size_at_arrivalqueue_size_at_departureserver_idrecord_type
10-9CustomerCustomer1False73.862473.86244.74678.6084078.6084-1False12service
9-8CustomerCustomer1False70.847970.84793.0144673.8624073.8624-1False22service
12-7CustomerCustomer1False65.072365.072330.163395.2356095.2356-1False33service
7-6CustomerCustomer1False59.632759.63275.4396765.0723065.0723-1False43service
8-5CustomerCustomer1False54.862154.862115.985970.8479070.8479-1False32service
11-4CustomerCustomer1False51.33951.33932.755184.0941084.0941-1False21service
6-3CustomerCustomer1False49.748649.74869.8840359.6327059.6327-1False53service
3-2CustomerCustomer1False42.85642.8566.8926649.7486049.7486-1False53service
2-1CustomerCustomer1False34.205734.20578.6502542.856042.856-1False63service
50CustomerCustomer1False20.363820.363834.498254.8621054.8621-1False42service

Notably, the arrival_date and queue_size_at_arrival at arrival are False. The id_number columns are non-positive to avoid conflicts with instances of Individual that are added later. Otherwise you can end up with collisions like having two individuals with the ID of 1. The IDs being set to non-positive values is an easy way to circumvent these collisions.

Something a bit more insideous for this hack is that self.simulation internal to these custom instances of Individual will also be False. If you ever use service disiplines that require accessing something else about the system’s state via the instance of Simulation, you will get errors or unexpected behaviour. Here is another version of the code that uses a service discipline to print out the default value of self.simulation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import ciw

import pandas as pd


def print_has_simulation_LIFO(individuals):

    if individuals[-1].id_number <= 0:
        print(individuals[-1].id_number, individuals[-1].simulation)

    return individuals[-1]

N = ciw.create_network(
    arrival_distributions=[ciw.dists.Exponential(rate=0.2)],
    service_distributions=[ciw.dists.Exponential(rate=0.1)],
    service_disciplines=[print_has_simulation_LIFO],
    number_of_servers=[3]
)


Q = ciw.Simulation(N)
Q.nodes[1].individuals = [[ciw.Individual(-i) for i in range(10)]]

Q.simulate_until_max_time(1000)

The above will print out something like this:

1
2
3
4
5
6
7
8
9
10
-9 False
-8 False
-7 False
-6 False
-5 False
-4 False
-3 False
-2 False
-1 False
0 False

What we can do is assign the simulation instance to self.simulation when we initialize each instance of Individual. Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import ciw

import pandas as pd


def print_has_simulation_LIFO(individuals):

    if individuals[-1].id_number <= 0:
        print(individuals[-1].id_number, individuals[-1].simulation)

    return individuals[-1]

N = ciw.create_network(
    arrival_distributions=[ciw.dists.Exponential(rate=0.2)],
    service_distributions=[ciw.dists.Exponential(rate=0.1)],
    service_disciplines=[print_has_simulation_LIFO],
    number_of_servers=[3]
)


Q = ciw.Simulation(N)

# Ensure the instances have the simulation instance
init_individuals = []
for i in range(10):
    initial_ind = ciw.Individual(-i, simulation=Q)
    init_individuals.append(initial_ind)
    
Q.nodes[1].individuals = [init_individuals]

Q.simulate_until_max_time(1000)

The above will print out something like this:

1
2
3
4
5
6
7
8
9
10
-9 Simulation
-8 Simulation
-7 Simulation
-6 Simulation
-5 Simulation
-4 Simulation
-3 Simulation
-2 Simulation
-1 Simulation
0 Simulation

And perhaps you want to set arrival_date and queue_size_at_arrival, and change some of the other default values. You can likewise set those values either through the __init__ method, or by assigning attributes after you have instantiated the object.

It doesn’t seem elegant, but it appears that in this post we have shown how to prepare queues to be initialized with individuals already waiting on them.

This post is licensed under CC BY 4.0 by the author.

Arithmetic of Neutrosophic Numbers as Vector Functions

Divergence of the Neutrosophic Differential Operator