Skip to content

Commit 75aaeb2

Browse files
Merge pull request #143 from CiwPython/time-dependent-batches
Time dependent batches
2 parents 0dd6632 + a0959db commit 75aaeb2

7 files changed

Lines changed: 89 additions & 9 deletions

File tree

AUTHORS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ Ciw has been developed by the following contributors:
1010
+ `Alex Carney <https://github.com/alcarney/>`_
1111
+ `Adam Johnson <https://github.com/adamchainz/>`_
1212
+ `Nikoleta Glynatsi <https://github.com/Nikoleta-v3/>`_
13+
+ `caipirginka <https://github.com/caipirginka>`_

ciw/arrival_node.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ def have_event(self):
8989
self.next_event_date))
9090
self.batch_size_dict[self.next_node][
9191
self.next_class] = self.batch_size(
92-
self.next_node, self.next_class)
92+
self.next_node, self.next_class,
93+
self.next_event_date)
9394
self.find_next_event_date()
9495

9596
def increment_time(self, original, increment):
@@ -116,7 +117,7 @@ def initialise_batch_size_dict(self):
116117
for nd in self.batch_size_dict:
117118
for clss in self.batch_size_dict[nd]:
118119
self.batch_size_dict[nd][
119-
clss] = self.batch_size(nd, clss)
120+
clss] = self.batch_size(nd, clss, 0.0)
120121

121122
def inter_arrival(self, nd, clss, current_time):
122123
"""
@@ -127,10 +128,15 @@ def inter_arrival(self, nd, clss, current_time):
127128
return self.simulation.inter_arrival_times[nd][clss](current_time)
128129
return self.simulation.inter_arrival_times[nd][clss]()
129130

130-
def batch_size(self, nd, clss):
131+
def batch_size(self, nd, clss, current_time):
131132
"""
132133
Samples the batch size for next class and node.
133134
"""
135+
if self.simulation.network.customer_classes[
136+
clss].batching_distributions[nd-1][0] == "TimeDependent":
137+
return self.simulation.batch_sizes[nd][clss](
138+
self.event_dates_dict[nd][clss]
139+
)
134140
return self.simulation.batch_sizes[nd][clss]()
135141

136142
def record_baulk(self, next_node):

ciw/import_params.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def validify_dictionary(params):
221221
batch_dists = [params['Batching_distributions']['Class ' + str(i)][j][0] for i in range(params['Number_of_classes']) for j in range(params['Number_of_nodes'])]
222222
if not set(batch_dists).issubset(set([
223223
'Deterministic', 'Empirical', 'Custom',
224-
'Sequential'])):
224+
'Sequential','TimeDependent'])):
225225
raise ValueError('Ensure that valid Batching Distributions are used.')
226226
neg_numservers = any([(isinstance(obs, int) and obs < 0) for obs in params['Number_of_servers']])
227227
valid_capacities = all([((isinstance(obs, int) and obs >= 0) or obs=='Inf') for obs in params['Queue_capacities']])

ciw/simulation.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,12 @@ def check_userdef_dist(self, func):
6868
raise ValueError("UserDefined func must return positive float.")
6969
return sample
7070

71-
def check_timedependent_dist(self, func, current_time):
71+
def check_timedependent_dist(self, func, kind, current_time):
7272
sample = func(current_time)
73-
if not isinstance(sample, float) or sample < 0:
73+
if kind in ['Arr', 'Ser'] and not isinstance(sample, float) or sample < 0:
7474
raise ValueError("TimeDependent func must return positive float.")
75+
if kind in ['Bch'] and not isinstance(sample, int) or sample < 0:
76+
raise ValueError("TimeDependent batching func must return positive integer.")
7577
return sample
7678

7779

@@ -142,7 +144,7 @@ def find_distributions(self, n, c, kind):
142144
return lambda : random_choice(empirical_dist)
143145
return lambda : random_choice(self.source(c, n, kind)[1])
144146
if self.source(c, n, kind)[0] == 'TimeDependent':
145-
return lambda t : self.check_timedependent_dist(self.source(c, n, kind)[1], t)
147+
return lambda t : self.check_timedependent_dist(self.source(c, n, kind)[1], kind, t)
146148
if self.source(c, n, kind)[0] == 'Sequential':
147149
return lambda : next(self.generators[kind][n][c])
148150

ciw/tests/test_arrival_node.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import unittest
22
import ciw
33

4+
def time_dependent_batches(current_time):
5+
if current_time < 11.0:
6+
return 5
7+
return 1
8+
49
class TestArrivalNode(unittest.TestCase):
510

611
def test_init_method(self):
@@ -341,3 +346,31 @@ def test_batching_multi_classes(self):
341346
self.assertEqual(arrivals, [20, 20, 20, 23, 23, 25])
342347
self.assertEqual(nodes, [1, 1, 1, 1, 1, 1])
343348
self.assertEqual(classes, [0, 0, 0, 1, 1, 2])
349+
350+
def test_batching_time_dependent(self):
351+
N = ciw.create_network(
352+
Arrival_distributions=[['Sequential', [5.0, 5.0, 2.0, 1.0, 1000.0]]],
353+
Service_distributions=[['Deterministic', 2]],
354+
Number_of_servers=[1],
355+
Batching_distributions=[['TimeDependent', time_dependent_batches]]
356+
)
357+
Q = ciw.Simulation(N)
358+
Q.simulate_until_max_time(30.0)
359+
recs = Q.get_all_records()
360+
361+
self.assertEqual(len(Q.nodes[-1].all_individuals), 12)
362+
self.assertEqual(len(Q.nodes[1].all_individuals), 0)
363+
364+
recs = Q.get_all_records()
365+
self.assertEqual([r.arrival_date for r in recs], [5.0, 5.0, 5.0, 5.0, 5.0, 10.0, 10.0, 10.0, 10.0, 10.0, 12.0, 13.0])
366+
self.assertEqual([r.exit_date for r in recs], [7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 21.0, 23.0, 25.0, 27.0, 29.0])
367+
368+
def test_error_batching_time_dependent(self):
369+
params = {
370+
'Arrival_distributions': [['Sequential', [5.0, 5.0, 2.0, 1.0, 1000.0]]],
371+
'Service_distributions': [['Deterministic', 2]],
372+
'Number_of_servers': [1],
373+
'Batching_distributions': [['TimeDependent', lambda t: 1.5]]
374+
}
375+
N = ciw.create_network(**params)
376+
self.assertRaises(ValueError, ciw.Simulation, N)

docs/Guides/batching.rst

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,44 @@ Note:
4646
+ :code:`Empirical`
4747
+ :code:`Custom`
4848
+ :code:`Sequential`
49+
+ :code:`TimeDependent`
4950
+ If the keyword :code:`Batching_distributions` is omitted, then no batching is assumed. That is only one customer arrives at a time. Equivalent to :code:`['Deterministic', 1]`.
5051
+ If some nodes/customer classes require no batching, but others do, please use :code:`['Deterministic', 1]`.
51-
+ Batch arrivals may lead to :ref:`simultaneous events <simultaneous_events>`, please take care.
52+
+ Batch arrivals may lead to :ref:`simultaneous events <simultaneous_events>`, please take care.
53+
54+
55+
---------------------------------
56+
How to Set Time Dependent Batches
57+
---------------------------------
58+
59+
Ciw allows batching distributions to be time dependent.
60+
That is the batch size, the number of customers arriving simultaneously, is sampled from a distribution that varies with time.
61+
62+
Let's show an example, we wish to have batch sizes of 2 for the first 10 time units, but batch sizes of 1 thereafter.
63+
Define a time dependent batching distribution::
64+
65+
>>> def time_dependent_batches(t):
66+
... if t < 10.0:
67+
... return 2
68+
... return 1
69+
70+
Now use this when defining a network:
71+
72+
>>> import ciw
73+
>>> N = ciw.create_network(
74+
... Arrival_distributions=[['Deterministic', 3.0]],
75+
... Service_distributions=[['Deterministic', 0.5]],
76+
... Batching_distributions=[['TimeDependent', time_dependent_batches]],
77+
... Number_of_servers=[1]
78+
... )
79+
80+
We'll simulate this for 16 time units.
81+
Now at times 3, 6, and 9 we would expect 2 customers arriving (a total of 6).
82+
And at times 12 and 15 we would expect 1 customer arriving (a total of 2).
83+
So 8 customers in total should finish service::
84+
85+
>>> Q = ciw.Simulation(N)
86+
>>> Q.simulate_until_max_time(16.0)
87+
>>> len(Q.nodes[-1].all_individuals)
88+
8
89+

docs/Guides/time_dependent.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
How to Define Time Dependent Distributions
55
==========================================
66

7-
In Ciw we can get a time dependent distribution, that is a service time or inter-arrival time distribution that changes as the simulation time progresses.
7+
In Ciw we can get a time dependent distribution, that is a service time, inter-arrival time, or batching distribution that changes as the simulation time progresses.
88
In order to do this a time dependent function, that returns a sampled time, must be defined.
99
This must take in a time variable `t`.
1010

0 commit comments

Comments
 (0)