LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
getRoutingMatrix.m
1function [rt,rtnodes,conn,chains,rtNodesByClass,rtNodesByStation] = getRoutingMatrix(self, arvRates)
2% [RT,RTNODES,CONNMATRIX,CHAINS,RTNODESBYCLASS,RTNODESBYSTATION] = GETROUTINGMATRIX(ARVRATES)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7if self.hasStruct
8 sn = self.sn;
9 idxSource = find(sn.nodetype == NodeType.Source);
10 idxSink = find(sn.nodetype == NodeType.Sink);
11 indexOpenClasses = find(isinf(sn.njobs));
12 hasOpen = ~isempty(indexOpenClasses);
13 if nargin<2
14 arvRates = sn.rates(sn.nodeToStation(idxSource),:);
15 end
16 nodes = self.nodes;
17 conn = sn.connmatrix;
18 I = sn.nnodes;
19 K = sn.nclasses;
20 NK = sn.njobs;
21 statefulNodes = find(sn.isstateful)';
22else
23 sn = self.sn;
24 idxSource = self.getIndexSourceNode;
25 idxSink = self.getIndexSinkNode;
26 indexOpenClasses = self.getIndexOpenClasses;
27 nodes = self.nodes;
28
29 conn = self.getConnectionMatrix;
30 sn.connmatrix = conn;
31 hasOpen = hasOpenClasses(self);
32
33 I = self.getNumberOfNodes;
34 K = self.getNumberOfClasses;
35 NK = self.getNumberOfJobs;
36 for ind=1:I
37 for k=1:K
38 if isempty(self.nodes{ind}.output.outputStrategy{k})
39 sn.routing(ind,k) = RoutingStrategy.DISABLED;
40 else
41 sn.routing(ind,k) = RoutingStrategy.fromText(nodes{ind}.output.outputStrategy{k}{2});
42 end
43 end
44 end
45 statefulNodes = self.getIndexStatefulNodes;
46end
47
48rtnodes = zeros(I*K);
49rtNodesByClass = {};
50rtNodesByStation = {};
51% The first loop considers the class at which a job enters the
52% target station
53for ind=1:I
54 node_i = nodes{ind};
55 outputStrategy = node_i.output.outputStrategy;
56 switch class(node_i.output)
57 case 'Forker'
58 for jnd=1:I
59 for k=1:K
60 if conn(ind,jnd)>0
61 if length(node_i.output.outputStrategy{k})>=3
62 fanout = length(node_i.output.outputStrategy{k}{3});
63 else
64 fanout = 1;
65 end
66 rtnodes((ind-1)*K+k,(jnd-1)*K+k)=1.0/fanout;
67 outputStrategy_k = outputStrategy{k};
68 switch sn.routing(ind,k)
69 case RoutingStrategy.PROB
70 if length(outputStrategy_k{end}) ~= sum(conn(ind,:))
71 line_warning(mfilename,'Fork should have 1.0 routing probability towards all outgoing links.');
72 end
73 for t=1:length(outputStrategy_k{end}) % for all outgoing links
74 if outputStrategy_k{end}{t}{2} < 1.0
75 line_warning(mfilename,sprintf('Fork should have 1.0 routing probability towards all outgoing links, but a routing probability is at %f.',outputStrategy_k{end}{t}{2}));
76 end
77 end
78 end
79 end
80 end
81 end
82 otherwise
83 isSink_i = isa(node_i,'Sink');
84 for k=1:K
85 outputStrategy_k = outputStrategy{k};
86 switch sn.routing(ind,k)
87 case RoutingStrategy.PROB
88 if isinf(NK(k)) || ~isSink_i
89 for t=1:length(outputStrategy_k{end}) % for all outgoing links
90 jnd = outputStrategy_k{end}{t}{1}.index;
91 rtnodes((ind-1)*K+k,(jnd-1)*K+k) = outputStrategy_k{end}{t}{2};
92 end
93 end
94 case RoutingStrategy.DISABLED
95 for jnd=1:I
96 if conn(ind,jnd)>0
97 if isa(self.classes{k},'SelfLoopingClass')
98 rtnodes((ind-1)*K+k,(jnd-1)*K+k) = 0;
99 else
100 % we set this to be non-zero as otherwise the
101 % classes that do not visit a classswitch are
102 % misconfigured in JMT
103 rtnodes((ind-1)*K+k,(jnd-1)*K+k) = 1/sum(conn(ind,:));
104 end
105 end
106 end
107 case {RoutingStrategy.RAND, RoutingStrategy.RROBIN, RoutingStrategy.WRROBIN, RoutingStrategy.JSQ}
108 if isinf(NK(k)) % open class
109 for jnd=1:I
110 if conn(ind,jnd)>0
111 rtnodes((ind-1)*K+k,(jnd-1)*K+k)=1/sum(conn(ind,:));
112 end
113 end
114 elseif ~isa(node_i,'Source') && ~isSink_i % don't route closed classes out of source nodes
115 connectionsClosed = conn;
116 if connectionsClosed(ind,idxSink)
117 connectionsClosed(ind,idxSink) = 0;
118 end
119 for jnd=1:I
120 if connectionsClosed(ind,jnd)>0
121 rtnodes((ind-1)*K+k,(jnd-1)*K+k)=1/(sum(connectionsClosed(ind,:)));
122 end
123 end
124 end
125 otherwise
126 for jnd=1:I
127 if conn(ind,jnd)>0
128 rtnodes((ind-1)*K+k,(jnd-1)*K+k) = GlobalConstants.FineTol;
129 end
130 end
131 %line_error(mfilename,[outputStrategy_k{2},' routing policy is not yet supported.']);
132 end
133 end
134 end
135end
136
137% The second loop corrects the first one at nodes that change
138% the class of the job in the service section.
139for ind=1:I % source
140 if isa(nodes{ind}.server,'StatelessClassSwitcher')
141 Pi = rtnodes(((ind-1)*K+1):ind*K,:);
142 Pcs = nodes{ind}.server.csFun(1:K,1:K);
143 rtnodes(((ind-1)*K+1):ind*K,:) = 0;
144 for jnd=1:I % destination
145 Pij = Pi(:,((jnd-1)*K+1):jnd*K); %Pij(r,s)
146 % Find the routing probability section determined by the router section in the first loop
147 rtnodes(((ind-1)*K+1) : ((ind-1)*K+K),(jnd-1)*K+(1:K)) = Pcs.*repmat(diag(Pij)',K,1);
148 end
149 elseif isa(nodes{ind}.server,'StatefulClassSwitcher')
150 Pi = rtnodes(((ind-1)*K+1):ind*K,:);
151 for r=1:K
152 for s=1:K
153 Pcs(r,s) = nodes{ind}.server.csFun(r,s,[],[]); % get csmask
154 end
155 end
156 rtnodes(((ind-1)*K+1):ind*K,:) = 0;
157 if isa(nodes{ind}.server,'CacheClassSwitcher')
158 for r=1:K
159 if (isempty(find(r == nodes{ind}.server.hitClass)) && isempty(find(r == nodes{ind}.server.missClass)))
160 Pcs(r,:) = Pcs(r,:)/sum(Pcs(r,:));
161 end
162 end
163
164 for r=1:K
165 if (isempty(find(r == nodes{ind}.server.hitClass)) && isempty(find(r == nodes{ind}.server.missClass)))
166 for jnd=1:I % destination
167 for s=1:K
168 Pi((ind-1)*K+r,(jnd-1)*K+s) = 0;
169 end
170 end
171 end
172 end
173
174 for r=1:K
175 if length(nodes{ind}.server.actualHitProb)>=r && length(nodes{ind}.server.hitClass)>=r
176 ph = nodes{ind}.server.actualHitProb(r);
177 pm = nodes{ind}.server.actualMissProb(r);
178 h = nodes{ind}.server.hitClass(r);
179 m = nodes{ind}.server.missClass(r);
180 rtnodes((ind-1)*K+r,(ind-1)*K+h) = ph;
181 rtnodes((ind-1)*K+r,(ind-1)*K+m) = pm;
182 else
183 if length(nodes{ind}.server.hitClass)>=r
184 h = nodes{ind}.server.hitClass(r);
185 m = nodes{ind}.server.missClass(r);
186 rtnodes((ind-1)*K+r,(ind-1)*K+h) = NaN;
187 rtnodes((ind-1)*K+r,(ind-1)*K+m) = NaN;
188 end
189 end
190 end
191 for jnd=1:I % destination
192 Pij = Pi(1:K,((jnd-1)*K+1):jnd*K); %Pij(r,s)
193 for r=1:K
194 if ~(isempty(find(r == nodes{ind}.server.hitClass)) && isempty(find(r == nodes{ind}.server.missClass)))
195 for s=1:K
196 % Find the routing probability section determined by the router section in the first loop
197 %Pnodes(((i-1)*K+1):i*K,((j-1)*K+1):j*K) = Pcs*Pij;
198 rtnodes((ind-1)*K+r,(jnd-1)*K+s) = Pcs(r,s)*Pij(s,s);
199 end
200 end
201 end
202 end
203 end
204 end
205end
206
207% Handle self-looping classes
208for r=1:K
209 if isa(self.classes{r},'SelfLoopingClass')
210 sl_refstat = self.classes{r}.refstat.index;
211 for ind=1:I % source
212 for jnd=1:I % destination
213 for s=1:K
214 rtnodes((ind-1)*K+r,(jnd-1)*K+s) = 0; % zero any prior setting
215 end
216 end
217 end
218 for s=1:K
219 if s==r
220 rtnodes((sl_refstat-1)*K+r,(sl_refstat-1)*K+s) = 1;
221 end
222 end
223 end
224end
225
226% ignore all chains containing a Pnodes column that sums to 0,
227% since these are classes that cannot arrive to the node
228% unless this column belongs to the source
229colsToIgnore = find(sum(rtnodes,1)==0);
230if hasOpen
231 colsToIgnore = setdiff(colsToIgnore,(idxSource-1)*K+(1:K));
232end
233
234% We route back from the sink to the source. Since open classes
235% have an infinite population, if there is a class switch QN
236% with the following chains
237% Source -> (A or B) -> C -> Sink
238% Source -> D -> Sink
239% We can re-route class C into the source either as A or B or C.
240% We here re-route back as C and leave for the chain analyzer
241% to detect that C is in a chain with A and B and change this
242% part.
243
244csmask = self.csMatrix;
245if isempty(csmask) % models not built with link(P)
246 % rtnodes+rtnodes' makes the matrix undirected, it helps grouping
247 % transient states with all the recurrent states their can reach
248 % into a single "chain" of the LINE model
249 [C,WCC]=weaklyconncomp(rtnodes+rtnodes');
250 WCC(colsToIgnore) = 0;
251 chainCandidates = cell(1,C);
252 for r=1:C
253 chainCandidates{r} = find(WCC==r);
254 end
255
256 chains = false(length(chainCandidates),K); % columns are classes, rows are chains
257 tmax = 0;
258 for t=1:length(chainCandidates)
259 if length(chainCandidates{t})>1
260 tmax = tmax+1;
261 chains(tmax,(mod(chainCandidates{t}-1,K)+1))=true;
262 end
263 end
264 chains(tmax+1:end,:)=[];
265else % this is faster since csmask is smaller than rtnodes
266 [C,WCC] = weaklyconncomp(csmask+csmask');
267
268 chainCandidates = cell(1,C);
269 for c=1:C
270 chainCandidates{c} = find(WCC==c);
271 end
272
273 chains = false(length(chainCandidates),K);
274 for t=1:length(chainCandidates)
275 chains(t,chainCandidates{t}) = true;
276 end
277end
278
279chains = unique(chains,'rows');
280try
281 chains = sortrows(chains,'descend');
282catch % old MATLABs
283 chains = sortrows(chains);
284end
285
286% overlapping chains can occur if a class that enters in a Cache
287% reappars downhill in the network in rtnodes in that case we merge
288% the two chains
289splitChains = [];
290for col=find(sum(chains,1)>1)
291 rows = find(chains(:,col));
292 for r=rows(2:end)'
293 chains(rows(1),:) = chains(rows(1),:) | chains(r,:);
294 splitChains(end+1)=r;
295 end
296end
297chains(splitChains,:)=[];
298
299% We now obtain the routing matrix P by ignoring the non-stateful
300% nodes and calculating by the stochastic complement method the
301% correct transition probabilities, that includes the effects
302% of the non-stateful nodes (e.g., ClassSwitch)
303statefulNodesClasses = [];
304for ind=statefulNodes %#ok<FXSET>
305 statefulNodesClasses(end+1:end+K)= ((ind-1)*K+1):(ind*K);
306end
307
308% this routes open classes back from the sink into the source
309% it will not work with non-renewal arrivals as it choses in which open
310% class to reroute a job with probability depending on the arrival rates
311if hasOpen
312 arvRates(isnan(arvRates)) = 0;
313 for s=indexOpenClasses
314 s_chain = find(chains(:,s));
315 others_in_chain = find(chains(s_chain,:)); %#ok<FNDSB>
316 rtnodes((idxSink-1)*K+others_in_chain,(idxSource-1)*K+others_in_chain) = repmat(arvRates(others_in_chain)/sum(arvRates(others_in_chain)),length(others_in_chain),1);
317 end
318end
319
320%% Hide the nodes that are not stateful
321rt = dtmc_stochcomp(rtnodes,statefulNodesClasses);
322
323% verify irreducibility
324% disabled as it casts warning in many models that are seemingly fine
325%try
326% eigen = eig(rt); % exception if rt has NaN or Inf
327% if sum(eigen>=1)>1
328% line_warning(mfilename, 'Solutions may be invalid, the routing matrix is reducible. The path of two or more classes form independent non-interacting models.');
329% end
330%end
331sn.rt = rt;
332sn.chains = chains;
333self.sn = sn;
334
335%% Compute optional outputs
336if nargout >= 5
337 rtNodesByClass = cellzeros(K,K,I,I);
338 for ind=1:I
339 for jnd=1:I
340 for r=1:K
341 for s=1:K
342 rtNodesByClass{s,r}(ind,jnd) = rtnodes((ind-1)*K+s,(jnd-1)*K+r);
343 end
344 end
345 end
346 end
347end
348
349if nargout >= 6
350 rtNodesByStation = cellzeros(I,I,K,K);
351 for ind=1:I %#ok<FXSET>
352 for jnd=1:I
353 for r=1:K
354 for s=1:K
355 rtNodesByStation{ind,jnd}(r,s) = rtnodes((ind-1)*K+s,(jnd-1)*K+r);
356 end
357 end
358 end
359 end
360end
361end
Definition mmt.m:92