1function [rt, rtfun, rtnodes, sn] = refreshRoutingMatrix(self, rates)
2% [
RT, RTFUN, CSMASK, RTNODES, SN] = REFRESHROUTINGMATRIX(RATES)
4% Copyright (c) 2012-2026, Imperial College London
10 line_error(mfilename,
'refreshRoutingMatrix cannot retrieve station rates, pass them as an input parameters.');
18stateful = find(sn.isstateful)
';
20indSource = find(sn.nodetype == NodeType.Source);
21indOpenClasses = find(sn.njobs == Inf);
23 arvRates(r) = rates(sn.nodeToStation(indSource),r);
26[rt, rtnodes, linksmat, chains] = self.getRoutingMatrix(arvRates);
32 if all(sn.routing(:,r) == -1)
33 line_error(mfilename,sprintf('Routing strategy in
class %d
is unspecified at all
nodes.
',r));
38isStateDep = any(sn.isstatedep(:,3));
40rnodefuncell = cell(M*K,M*K);
47 if sn.isstatedep(ind,3)
48 switch sn.routing(ind,r)
49 case RoutingStrategy.RROBIN
50 rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(state_before, state_after) sub_rr(ind, jnd, r, s, linksmat, state_before, state_after);
51 case RoutingStrategy.WRROBIN
52 rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(state_before, state_after) sub_wrr(ind, jnd, r, s, linksmat, state_before, state_after);
53 case RoutingStrategy.JSQ
54 rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(state_before, state_after) sub_jsq(ind, jnd, r, s, linksmat, state_before, state_after);
55 case RoutingStrategy.RL
56 rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(state_before, state_after) sub_rl(ind, jnd, r, s, linksmat, state_before, state_after);
58 rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(~,~) rtnodes((ind-1)*K+r, (jnd-1)*K+s);
61 rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(~,~) rtnodes((ind-1)*K+r, (jnd-1)*K+s);
69statefulNodesClasses = [];
70for ind=getIndexStatefulNodes(self)
71 statefulNodesClasses(end+1:end+K)= ((ind-1)*K+1):(ind*K);
74% we now generate the node routing matrix for the given state and then
75% lump the states for non-stateful nodes so that run gives the routing
76% table for stateful nodes only
77statefulNodesClasses = [];
79 statefulNodesClasses(end+1:end+K)= ((ind-1)*K+1):(ind*K);
83 rtfunraw = @(state_before, state_after) dtmc_stochcomp(cell2mat(cellfun(@(f) f(state_before, state_after), rnodefuncell,'UniformOutput
',false)), statefulNodesClasses);
85 %rtfun = memoize(rtfunraw); % memoize to reduce the number of stoch comp calls
86 %rtfun.CacheSize = 6000^2;
88 rtfun = @(state_before, state_after) dtmc_stochcomp(rtnodes, statefulNodesClasses);
91nchains = size(chains,1);
92inchain = cell(1,nchains);
94 inchain{c} = find(chains(c,:));
99%sn.rtorig = RoutingMatrix.rtnodes2rtorig(sn); %% causes issues to
100%JLINE.from_line_links in convering example_closedModel_6
106 if range(sn.refstat(inchain{c}))>0
107 line_error(mfilename,sprintf('Classes within chain %d (
classes: %s) have different reference stations.
',c,mat2str(find(sn.chains(c,:)))));
112 function p = sub_rr(ind, jnd, r, s, linksmat, state_before, state_after)
113 % P = SUB_RR(IND, JND, R, S, LINKSMAT, STATE_BEFORE, STATE_AFTER)
115 R = self.sn.nclasses;
116 isf = self.sn.nodeToStateful(ind);
117 if isempty(state_before{isf})
118 p = min(linksmat(ind,jnd),1);
121 p = double(state_after{isf}(end-R+r)==jnd);
128 function p = sub_wrr(ind, jnd, r, s, linksmat, state_before, state_after)
129 % P = SUB_WRR(IND, JND, R, S, LINKSMAT, STATE_BEFORE, STATE_AFTER)
131 R = self.sn.nclasses;
132 isf = self.sn.nodeToStateful(ind);
133 if isempty(state_before{isf})
134 p = min(linksmat(ind,jnd),1);
137 p = double(state_after{isf}(end-R+r)==jnd);
144 function p = sub_jsq(ind, jnd, r, s, linksmat, state_before, state_after) %#ok<INUSD>
145 % P = SUB_JSQ(IND, JND, R, S, LINKSMAT, STATE_BEFORE, STATE_AFTER) %#OK<INUSD>
147 isf = self.sn.nodeToStateful(ind);
148 if isempty(state_before{isf})
149 p = min(linksmat(ind,jnd),1);
152 n = Inf*ones(1,self.sn.nnodes);
153 for knd=1:self.sn.nnodes
155 ksf = self.sn.nodeToStateful(knd);
156 n(knd) = State.toMarginal(self.sn, knd, state_before{ksf});
160 p = 1 / sum(n == min(n));
171 function p = sub_rl(ind, jnd, r, s, linksmat, state_before, state_after) %#ok<INUSD>
172 % P = SUB_RL(IND, JND, R, S, LINKSMAT, STATE_BEFORE, STATE_AFTER) %#OK<INUSD>
174 isf = self.sn.nodeToStateful(ind);
175 if isempty(state_before{isf})
176 p = min(linksmat(ind,jnd),1);
179 % ----- new added contents ----- %
180 if self.nodes{ind}.output.outputStrategy{1,r}{5}==0 % state_size=0, use tabular value fn
182 line_error(mfilename,'no support for multiple
classes');
184 value_function = self.nodes{ind}.output.outputStrategy{1,r}{3};
185 nodes_need_action = self.nodes{ind}.output.outputStrategy{1,r}{4};
187 if ~isempty(find(nodes_need_action==ind, 1))
188 indQueue = find(self.sn.nodetype == NodeType.Queue);
189 v = Inf*ones(1, self.sn.nnodes); % value fn
190 n = Inf*ones(1, self.sn.nnodes); % queue length
191 x = zeros(1, length(indQueue)); % current state
192 for knd_idx=1:length(indQueue)
193 knd = indQueue(knd_idx);
194 ksf = self.sn.nodeToStateful(knd); %% does the state removes the job from the departure node already?
195 x(knd_idx) = State.toMarginal(self.sn, knd, state_before{ksf});
198 for knd = 1:self.sn.nnodes
199 if linksmat(ind, knd)
201 tmp(indQueue == knd) = tmp(indQueue == knd) + 1;
202 if max(tmp) <= size(value_function, 1)
203 ttmp = num2cell(tmp);
204 v(knd) = value_function(ttmp{:});
206 ksf = self.sn.nodeToStateful(knd);
207 n(knd) = State.toMarginal(self.sn, knd, state_before{ksf});
211 if min(v) < Inf && max(x+1) < size(value_function, 1) % in action space
213 p = 1 / sum(v == min(v));
217 else % not in action space, use JSQ
219 p = 1 / sum(n == min(n));
225 else % not in nodes_need_action: this node doesn't use RL results, use JSQ
226 n = Inf*ones(1,self.sn.nnodes);
227 for knd=1:self.sn.nnodes
229 ksf = self.sn.nodeToStateful(knd);
230 n(knd) = State.toMarginal(self.sn, knd, state_before{ksf});
234 p = 1 / sum(n == min(n));
240 elseif self.nodes{ind}.output.outputStrategy{1,r}{5}>0 % state_size>0, use fn approx
for value fn
242 line_error(mfilename,
'no support for multiple classes');
244 coeff = self.nodes{ind}.output.outputStrategy{1,r}{3};
245 nodes_need_action = self.nodes{ind}.output.outputStrategy{1,r}{4};
246 stateSize = self.nodes{ind}.output.outputStrategy{1,r}{5};
248 if ~isempty(find(nodes_need_action==ind, 1))
249 indQueue = find(self.sn.nodetype == NodeType.Queue);
250 v = Inf*ones(1, self.sn.nnodes); % value fn
251 n = Inf*ones(1, self.sn.nnodes); % queue length
252 x = zeros(1, length(indQueue)); % current state
253 for knd_idx=1:length(indQueue)
254 knd = indQueue(knd_idx);
255 ksf = self.sn.nodeToStateful(knd); %% does the state removes the job from the departure node already?
256 x(knd_idx) = State.toMarginal(self.sn, knd, state_before{ksf});
258 for knd = 1:self.sn.nnodes
259 if linksmat(ind, knd)
261 tmp(indQueue == knd) = tmp(indQueue == knd) + 1;
264 for i = 1:length(tmp)
265 for j = i:length(tmp)
266 tmp_vec(end+1) = tmp(i)* tmp(j);
269 v(knd) = tmp_vec * coeff.
';
271 ksf = self.sn.nodeToStateful(knd);
272 n(knd) = State.toMarginal(self.sn, knd, state_before{ksf});
275 if min(v) < Inf && max(x+1) < stateSize % in action space
277 p = 1 / sum(v == min(v));
281 else % not in action space, use JSQ
283 p = 1 / sum(n == min(n));
288 else % not in nodes_need_action: this node doesn't use RL results, use JSQ
289 n = Inf*ones(1,self.sn.nnodes);
290 for knd=1:self.sn.nnodes
292 ksf = self.sn.nodeToStateful(knd);
293 n(knd) = State.toMarginal(self.sn, knd, state_before{ksf});
297 p = 1 / sum(n == min(n));
303 else % no value fn, use JSQ
304 % ----- end of
new added contents ----- %
306 n = Inf*ones(1,self.sn.nnodes);
307 for knd=1:self.sn.nnodes
309 ksf = self.sn.nodeToStateful(knd);
310 n(knd) = State.toMarginal(self.sn, knd, state_before{ksf});
314 p = 1 / sum(n == min(n));
360% function [rt, rtfun, rtnodes, sn] = refreshRoutingMatrix(self, rates)
361% % [
RT, RTFUN, CSMASK, RTNODES, SN] = REFRESHROUTINGMATRIX(RATES)
363% % Copyright (c) 2012-2026, Imperial College London
364% % All rights reserved.
369% line_error(mfilename,
'refreshRoutingMatrix cannot retrieve station rates, pass them as an input parameters.');
376% arvRates = zeros(1,K);
377% stateful = find(sn.isstateful)
';
379% indSource = find(sn.nodetype == NodeType.Source);
380% indOpenClasses = find(sn.njobs == Inf);
381% for r = indOpenClasses
382% arvRates(r) = rates(sn.nodeToStation(indSource),r);
385% [rt, rtnodes, linksmat, chains] = self.getRoutingMatrix(arvRates);
389% if self.enableChecks
391% if all(sn.routing(:,r) == -1)
392% line_error(mfilename,sprintf('Routing strategy in
class %d
is unspecified at all
nodes.
',r));
397% isStateDep = any(sn.isstatedep(:,3));
399% rnodefuncell = cell(M*K,M*K);
406% if sn.isstatedep(ind,3)
407% switch sn.routing(ind,r)
408% case RoutingStrategy.RROBIN
409% rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(state_before, state_after) sub_rr(ind, jnd, r, s, linksmat, state_before, state_after);
410% case RoutingStrategy.WRROBIN
411% rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(state_before, state_after) sub_wrr(ind, jnd, r, s, linksmat, state_before, state_after);
412% case RoutingStrategy.JSQ
413% rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(state_before, state_after) sub_jsq(ind, jnd, r, s, linksmat, state_before, state_after);
415% rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(~,~) rtnodes((ind-1)*K+r, (jnd-1)*K+s);
418% rnodefuncell{(ind-1)*K+r, (jnd-1)*K+s} = @(~,~) rtnodes((ind-1)*K+r, (jnd-1)*K+s);
426% statefulNodesClasses = [];
427% for ind=getIndexStatefulNodes(self)
428% statefulNodesClasses(end+1:end+K)= ((ind-1)*K+1):(ind*K);
431% % we now generate the node routing matrix for the given state and then
432% % lump the states for non-stateful nodes so that run gives the routing
433% % table for stateful nodes only
434% statefulNodesClasses = [];
436% statefulNodesClasses(end+1:end+K)= ((ind-1)*K+1):(ind*K);
440% rtfunraw = @(state_before, state_after) dtmc_stochcomp(cell2mat(cellfun(@(f) f(state_before, state_after), rnodefuncell,'UniformOutput
',false)), statefulNodesClasses);
442% %rtfun = memoize(rtfunraw); % memoize to reduce the number of stoch comp calls
443% %rtfun.CacheSize = 6000^2;
445% rtfun = @(state_before, state_after) dtmc_stochcomp(rtnodes, statefulNodesClasses);
448% nchains = size(chains,1);
449% inchain = cell(1,nchains);
451% inchain{c} = find(chains(c,:));
455% sn.rtnodes = rtnodes;
458% sn.nchains = nchains;
459% sn.inchain = inchain;
461% if range(sn.refstat(inchain{c}))>0
462% line_error(mfilename,sprintf('Classes within chain %d (
classes: %s) have different reference stations.
',c,mat2str(find(sn.chains(c,:)))));
467% function p = sub_rr(ind, jnd, r, s, linksmat, state_before, state_after)
468% % P = SUB_RR(IND, JND, R, S, LINKSMAT, STATE_BEFORE, STATE_AFTER)
471% isf = sn.nodeToStateful(ind);
472% if isempty(state_before{isf})
473% p = min(linksmat(ind,jnd),1);
476% p = double(state_after{isf}(end-R+r)==jnd);
483% function p = sub_wrr(ind, jnd, r, s, linksmat, state_before, state_after)
484% % P = SUB_WRR(IND, JND, R, S, LINKSMAT, STATE_BEFORE, STATE_AFTER)
487% isf = sn.nodeToStateful(ind);
488% if isempty(state_before{isf})
489% p = min(linksmat(ind,jnd),1);
492% p = double(state_after{isf}(end-R+r)==jnd);
499% function p = sub_jsq(ind, jnd, r, s, linksmat, state_before, state_after) %#ok<INUSD>
500% % P = SUB_JSQ(IND, JND, R, S, LINKSMAT, STATE_BEFORE, STATE_AFTER) %#OK<INUSD>
502% isf = sn.nodeToStateful(ind);
503% if isempty(state_before{isf})
504% p = min(linksmat(ind,jnd),1);
507% n = Inf*ones(1,sn.nnodes);
509% if linksmat(ind,knd)
510% ksf = sn.nodeToStateful(knd);
511% n(knd) = State.toMarginal(sn, knd, state_before{ksf});
515% p = 1 / sum(n == min(n));