You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/Guides/Routing/process_based.rst
+107-6Lines changed: 107 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@
4
4
How to Define Process-Based Routing
5
5
===================================
6
6
7
-
Ciw has the capability to run simulations with process-based routing. This means a customer's entire route is determined at the start and not determined probablistically as they progress through the network.
7
+
Ciw has the capability to run simulations with process-based routing. This means a customer's entire route is determined at the start and not determined probabilistically as they progress through the network.
8
8
This allows routes to account for an individuals history, for example, repeating nodes a certain number of times.
9
9
10
10
A customer's entire route is determined at the start, generated from a routing function, that takes in an individual and returns a route, which is a list containing the order of the nodes they are to visit. The function should also take in the simulation itself, allowing time and state-dependent routing. For example::
@@ -36,7 +36,7 @@ Let's run this and look at the routes of those that have left the system.
36
36
>>> Q = ciw.Simulation(N)
37
37
>>> Q.simulate_until_max_time(100.0)
38
38
39
-
>>> inds = Q.nodes[-1].all_individuals #Get's all individuals from exit node
39
+
>>> inds = Q.nodes[-1].all_individuals #Gets all individuals from exit node
40
40
>>> set([tuple(dr.node for dr in ind.data_records) for ind in inds]) # Get's all unique routes of completed individuals
41
41
{(1, 1, 1)}
42
42
@@ -58,7 +58,7 @@ Lets make a network with three nodes with the following routes:
58
58
+ have 50% chance of routing to Node 1, and then exit.
59
59
+ There are no arrivals at Node 3.
60
60
61
-
For this we will require two routing functions: :code:`routing_function_Node_1`, :code:`routing_function_Node_2`::
61
+
For this we will require a routing function that returns different things depending on the individual's starting node::
62
62
63
63
>>> import random
64
64
>>> def routing_function(ind, simulation):
@@ -71,15 +71,116 @@ For this we will require two routing functions: :code:`routing_function_Node_1`,
71
71
... return [2, 3]
72
72
... return [2, 1]
73
73
74
-
As there are no arrivals at Node 3, no customer will need routing assigned here. However, we need to use the placeholder function :code:`ciw.no_routing` to account for this::
In the examples above, once a route was sampled, the customer's entire journey was set out before them. However, with a :code:`FlexibleProcessBased` object, we can define sequences of sets of nodes that must be visited in order. Within a set of nodes, either the individual must visit at least one node here, or must visit all nodes here, but the order is irrelevant. This is defined with the :code:`rule` keyword.
91
+
92
+
Consider for example the following sequence of sets of destinations::
93
+
94
+
[[1, 2, 3], [4], [5, 6]]
95
+
96
+
There are three sets of nodes in the sequence, the set :code:`[1, 2, 3]`, followed by the set :code:`[4]`, followed by the set :code:`[5, 6]`. Routes are then determined by the :code:`rule` keyword:
97
+
98
+
+ :code:`rule='any'`: this means that at just one node from each set should be visited, in the order of the sets. The choice of which node is chosen from each set is set with the :code:`choice` keyword. Valid routes include (1, 4, 5), (2, 4, 5), and (3, 4, 6), amongst others.
99
+
+ :code:`rule='all'`: this means that every node in a set must be visited before moving on to the next set. The order at which a node is visited in a set is set with the :code:`choice` keyword. Valid routes include (1, 2, 3, 4, 5, 6), (3, 2, 1, 4, 6, 5), and (3, 1, 2, 4, 5, 6), amongst others.
100
+
101
+
The current options for choices are:
102
+
- :code:`'random'`: randomly chooses a node from the set.
103
+
- :code:`'jsq'`: chooses the node with the smallest queue from the set (like the :ref:`join-shortest-queue<jsq>` router).
104
+
- :code:`'lb'`: chooses the node with the least number of customers present from the set (like the :ref:`load-balancing<load_balancing>` router).
105
+
106
+
When all nodes in a set must be visited, these rules apply to choosing the next node from the set minus the nodes already visited, applied at the current time when the choice is made.
107
+
108
+
Example::
109
+
110
+
>>> def routing_function(ind, simulation):
111
+
... return [[1, 2], [3], [1, 2]]
112
+
113
+
A route where the first and third sets include nodes 1 and 2, and the second set only includes node 3. All customers arrive to node 4. Let's compare the :code:`'any'` and :code:`'all'` rules. First with :code:`'any'`::
114
+
115
+
>>> N = ciw.create_network(
116
+
... arrival_distributions=[
117
+
... None,
118
+
... None,
119
+
... None,
120
+
... ciw.dists.Exponential(rate=1)
121
+
... ],
122
+
... service_distributions=[
123
+
... ciw.dists.Exponential(rate=2),
124
+
... ciw.dists.Exponential(rate=2),
125
+
... ciw.dists.Exponential(rate=2),
126
+
... ciw.dists.Exponential(rate=2),
127
+
... ],
128
+
... number_of_servers=[1, 1, 1, 1],
129
+
... routing=ciw.routing.FlexibleProcessBased(
130
+
... route_function=routing_function,
131
+
... rule='any',
132
+
... choice='random'
133
+
... )
134
+
... )
135
+
>>> ciw.seed(0)
136
+
>>> Q = ciw.Simulation(N)
137
+
>>> Q.simulate_until_max_customers(6)
138
+
>>> routes = [[dr.node for dr in ind.data_records] for ind in Q.nodes[-1].all_individuals]
139
+
>>> for route in routes:
140
+
... print(route)
141
+
[4, 2, 3, 2]
142
+
[4, 1, 3, 1]
143
+
[4, 1, 3, 1]
144
+
[4, 1, 3, 1]
145
+
[4, 2, 3, 1]
146
+
[4, 2, 3, 1]
147
+
148
+
We see that all customers that completed their journey arrived at node 4, took either node 1 or 2 first, then node 3, then either node 1 or 2.
149
+
150
+
Now compare with :code:`'all'`::
151
+
152
+
>>> N = ciw.create_network(
153
+
... arrival_distributions=[
154
+
... None,
155
+
... None,
156
+
... None,
157
+
... ciw.dists.Exponential(rate=1)
158
+
... ],
159
+
... service_distributions=[
160
+
... ciw.dists.Exponential(rate=2),
161
+
... ciw.dists.Exponential(rate=2),
162
+
... ciw.dists.Exponential(rate=2),
163
+
... ciw.dists.Exponential(rate=2),
164
+
... ],
165
+
... number_of_servers=[1, 1, 1, 1],
166
+
... routing=ciw.routing.FlexibleProcessBased(
167
+
... route_function=routing_function,
168
+
... rule='all',
169
+
... choice='random'
170
+
... )
171
+
... )
172
+
>>> ciw.seed(0)
173
+
>>> Q = ciw.Simulation(N)
174
+
>>> Q.simulate_until_max_customers(6)
175
+
>>> routes = [[dr.node for dr in ind.data_records] for ind in Q.nodes[-1].all_individuals]
176
+
>>> for route in routes:
177
+
... print(route)
178
+
[4, 2, 1, 3, 2, 1]
179
+
[4, 1, 2, 3, 2, 1]
180
+
[4, 2, 1, 3, 2, 1]
181
+
[4, 1, 2, 3, 2, 1]
182
+
[4, 1, 2, 3, 1, 2]
183
+
[4, 1, 2, 3, 2, 1]
184
+
185
+
We see that all customers that completed their journey arrived at node 4, took both node 1 or 2 in either order, then node 3, then both node 1 or 2 in either order.
0 commit comments