LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
getAvg.m
1function [QNclass,UNclass,RNclass,TNclass,ANclass,WNclass] = getAvg(self,Q,U,R,T,A,W)
2% [QNCLASS,UNCLASS,RNCLASS,TNCLASS,ANCLASS,WNCLASS] = GETAVG(SELF,Q,U,R,T,A,W)
3%
4% Compute steady-state average metrics (queue length, utilization, response time,
5% throughput, arrival rate, residence time) for all stations and job classes.
6%
7% Copyright (c) 2012-2026, Imperial College London
8% All rights reserved.
9
10if isa(self.model, 'LayeredNetwork')
11 % Java-backed LQN simulation (SolverLDES): per-LQN-element vectors,
12 % same convention as SolverLN/SolverLQNS getAvg
13 self.obj.getAvg(); % runs the LN LDES analyzer
14 avgTable = self.obj.getLNAvgTable();
15 [QNclass,UNclass,RNclass,WNclass,ANclass,TNclass] = JLINE.arrayListToResults(avgTable);
16 return
17end
18
19sn = self.model.getStruct();
20
21if strcmp(self.options.lang,'java') && ~strcmp(self.name,'SolverLDES')
22 T0=tic;
23 M = sn.nstations;
24 R = sn.nclasses;
25 % Force fresh Java model conversion each time to ensure current state
26 % Only re-create the Java model if the model is not already Java-native
27 if ~self.model.isJavaNative()
28 self.model.obj = [];
29 self.setLang();
30 end
31 self.obj.getOptions.verbose = jline.VerboseLevel.STD;
32 % Carry the JLINE-side fork-join (MMT) iterate across the model rebuild so
33 % layers warm-start as in lang='matlab'. see _kb/05-solvers-overview.md
34 isMVA = isa(self,'SolverMVA');
35 if isMVA && self.options.config.fj_warmstart && ~isempty(self.fjForkLambda)
36 self.obj.setForkWarmStart(JLINE.from_line_matrix(self.fjForkLambda));
37 end
38 SolverResult = self.obj.getAvg();
39 if isMVA && self.model.hasFork
40 self.fjForkLambda = JLINE.from_jline_matrix(self.obj.getForkWarmStart());
41 end
42 QN = JLINE.from_jline_matrix(SolverResult.QN);
43 UN = JLINE.from_jline_matrix(SolverResult.UN);
44 RN = JLINE.from_jline_matrix(SolverResult.RN);
45 TN = JLINE.from_jline_matrix(SolverResult.TN);
46 AN = JLINE.from_jline_matrix(SolverResult.AN);
47 WN = JLINE.from_jline_matrix(SolverResult.WN);
48 runtime=SolverResult.runtime;
49 method=SolverResult.method;
50 % Extract cache hit/miss probabilities from Java model
51 for ind = 1:sn.nnodes
52 if sn.nodetype(ind) == NodeType.Cache
53 jnode = self.model.obj.getNodeByIndex(ind-1);
54 hitRatioVec = JLINE.from_jline_matrix(jnode.getHitRatio());
55 missRatioVec = JLINE.from_jline_matrix(jnode.getMissRatio());
56 % Store per-class hit/miss probabilities matching MATLAB format:
57 % only parent classes with hitClass>0 have non-zero entries
58 hitClass = self.model.nodes{ind}.getHitClass;
59 nk = length(hitClass);
60 hitprob = zeros(1, nk);
61 missprob = zeros(1, nk);
62 for k = 1:nk
63 if hitClass(k) > 0 && k <= length(hitRatioVec)
64 hitprob(k) = hitRatioVec(k);
65 missprob(k) = missRatioVec(k);
66 end
67 end
68 self.model.nodes{ind}.setResultHitProb(hitprob);
69 self.model.nodes{ind}.setResultMissProb(missprob);
70 % Retrieval-cache extras (delayed-hit ratio, per-list hit ratio and
71 % expected latency) copied raw from the Java cache so getAvgCacheTable
72 % matches the native path. Empty for plain caches.
73 self.model.nodes{ind}.setResultDelayedHitProb(JLINE.from_jline_matrix(jnode.getDelayedHitRatio()));
74 self.model.nodes{ind}.setResultHitProbList(JLINE.from_jline_matrix(jnode.getHitRatioByList()));
75 self.model.nodes{ind}.setResultItemProb(JLINE.from_jline_matrix(jnode.getItemProb()));
76 self.model.nodes{ind}.setResultResidT(JLINE.from_jline_matrix(jnode.getResidT()));
77 end
78 end
79 if any(sn.nodetype == NodeType.Cache)
80 self.model.refreshStruct(true);
81 end
82 self.setAvgResults(QN,UN,RN,TN,AN,WN,[],[],runtime,method,1);
83 % Finite Capacity Region (FCR) metrics appended as pseudo-stations
84 % M+1..M+F (JMT only). see _kb/06-solver-catalog.md for rationale
85 if sn.nregions > 0 && strcmp(self.name,'SolverJMT')
86 jnode = self.obj.getAvgNode();
87 Qn = JLINE.from_jline_matrix(jnode.QN);
88 Un = JLINE.from_jline_matrix(jnode.UN);
89 Rn = JLINE.from_jline_matrix(jnode.RN);
90 Wn = JLINE.from_jline_matrix(jnode.WN);
91 Tn = JLINE.from_jline_matrix(jnode.TN);
92 An = JLINE.from_jline_matrix(jnode.AN);
93 for f = 1:sn.nregions
94 self.result.Avg.Q(M+f,:) = Qn(sn.nnodes+f,:);
95 self.result.Avg.U(M+f,:) = Un(sn.nnodes+f,:);
96 self.result.Avg.R(M+f,:) = Rn(sn.nnodes+f,:);
97 self.result.Avg.W(M+f,:) = Wn(sn.nnodes+f,:);
98 self.result.Avg.T(M+f,:) = Tn(sn.nnodes+f,:);
99 self.result.Avg.A(M+f,:) = An(sn.nnodes+f,:);
100 end
101 end
102 QNclass = reshape(QN,M,R);
103 UNclass = reshape(UN,M,R);
104 RNclass = reshape(RN,M,R);
105 TNclass = reshape(TN,M,R);
106 ANclass = reshape(AN,M,R);
107 WNclass = reshape(WN,M,R);
108 return
109end
110
111%%
112if nargin == 1 % no parameter
113 if isempty(self.model.handles) || ~isfield(self.model.handles,'Q') || ...
114 ~isfield(self.model.handles,'U') || ~isfield(self.model.handles,'R') || ...
115 ~isfield(self.model.handles,'T') || ~isfield(self.model.handles,'A') || ...
116 ~isfield(self.model.handles,'W')
117 reset(self); % reset results in case there are partial results saved
118 end
119 [Q,U,R,T,A,W] = self.getAvgHandles;
120elseif nargin == 2
121 handlers = Q;
122 [Q,U,R,T,A,W] = deal(handlers{:}); % set Q=handlers{1}, U=handlers{2}, ...
123end
124
125if isfield(self.options,'timespan')
126 if isfinite(self.options.timespan(2))
127 line_error(mfilename,'The getAvg method does not support the timespan option, use the getTranAvg method instead.');
128 end
129else
130 self.options.timespan = [0,Inf];
131end
132
133if ~self.hasAvgResults() || ~self.options.cache
134 runAnalyzer(self);
135 % the next line is required because getAvg can alter the chain
136 % structure in the presence of caches so we need to reload sn
137 sn = self.model.getStruct;
138 if ~self.hasAvgResults
139 line_error(mfilename,'Unable to return results for this model.');
140 end
141end % else return cached value
142
143
144M = sn.nstations;
145K = sn.nclasses;
146
147% Check if this is an SPN model (Places don't have response times)
148hasSPN = any(sn.nodetype == NodeType.Place) || any(sn.nodetype == NodeType.Transition);
149
150if ~isempty(R)
151 RNclass = filterMetric(R, self.result.Avg.R, [], sn, K, M);
152else
153 RNclass = [];
154end
155
156if ~isempty(Q)
157 % For SPNs, don't zero Q based on R because Places don't have response times
158 if hasSPN
159 zeroMaskQ = [];
160 else
161 zeroMaskQ = RNclass < 10 * GlobalConstants.FineTol;
162 end
163 QNclass = filterMetric(Q, self.result.Avg.Q, zeroMaskQ, sn, K, M);
164else
165 QNclass = [];
166end
167
168if ~isempty(U)
169 % For SPNs, don't zero U based on R because Places don't have response times
170 if hasSPN
171 zeroMaskU = [];
172 else
173 zeroMaskU = RNclass < 10 * GlobalConstants.FineTol;
174 end
175 UNclass = filterMetric(U, self.result.Avg.U, zeroMaskU, sn, K, M);
176else
177 UNclass = [];
178end
179
180if ~isempty(T)
181 TNclass = filterMetric(T, self.result.Avg.T, [], sn, K, M);
182else
183 TNclass = [];
184end
185
186if ~isempty(A)
187 zeroMask = false(size(RNclass));
188 zeroMask(sn.nodeToStation(sn.nodetype==NodeType.Source),:) = true;
189 ANclass = filterMetric(A, self.result.Avg.A, zeroMask, sn, K, M);
190else
191 ANclass = [];
192end
193
194if ~isempty(W)
195 WNclass = sn_get_residt_from_respt(sn, RNclass, W);
196else
197 WNclass = [];
198end
199
200if ~isempty(UNclass)
201 % Finite-server open station unstable when offered load rho >= 1: Util capped
202 % at 1.0, QLen/RespT set Inf. see _kb/06-solver-catalog.md for rationale
203 anyUnstable = false;
204 if any(isinf(sn.njobs)) && ~isempty(TNclass)
205 srcStations = sn.nodeToStation(sn.nodetype==NodeType.Source);
206 openCls = isinf(sn.njobs(:).');
207 for i = 1:size(UNclass,1)
208 c = sn.nservers(i);
209 if ~isfinite(c) || c <= 0 || any(srcStations==i)
210 continue % infinite-server / delay / source station
211 end
212 rho = zeros(1,size(UNclass,2));
213 for r = 1:size(UNclass,2)
214 if sn.rates(i,r) > 0 && TNclass(i,r) > 0
215 rho(r) = TNclass(i,r) / (c * sn.rates(i,r));
216 end
217 end
218 rhoOpen = sum(rho(openCls));
219 rhoTot = sum(rho);
220 if rhoOpen >= 1 && rhoTot > 0
221 anyUnstable = true;
222 UNclass(i,:) = rho / rhoTot; % station total capped to 1.0
223 % Saturated station: QLen/RespT Inf for open classes.
224 % see _kb/06-solver-catalog.md for rationale
225 if ~isempty(RNclass)
226 RNclass(i,openCls) = Inf;
227 end
228 if ~isempty(QNclass)
229 QNclass(i,openCls) = Inf;
230 end
231 end
232 end
233 end
234 if anyUnstable
235 line_warning(mfilename,'The model has unstable queues (utilization >= 1); station utilization is reported capped at 1.0, queue length and response time as Inf.\n')
236 end
237end
238end
239
240function outData = filterMetric(handle, metric, zeroMask, sn, K, M)
241% post-process Avg measure
242outData = zeros(M, K);
243for k = 1:K
244 for i = 1:M
245 if ~handle{i,k}.disabled && ~isempty(metric)
246 outData(i,k) = metric(i,k);
247 else
248 outData(i,k) = NaN;
249 end
250 end
251end
252
253% NaN values indicate that a metric is disabled
254outData(isnan(outData)) = 0;
255% set to zero entries associated to immediate transitions
256outData(zeroMask) = 0;
257% round to zero numerical perturbations
258outData(outData < GlobalConstants.FineTol) = 0;
259
260% Zero metrics for unreachable classes, but trust the simulation for
261% fork-join, cache, spawn-fed and SPN models where the routing-based visit
262% equations do not apply. see _kb/06-solver-catalog.md for rationale
263hasForkJoin = any(sn.nodetype == NodeType.Fork) && any(sn.nodetype == NodeType.Join);
264hasSPN = any(sn.nodetype == NodeType.Place) || any(sn.nodetype == NodeType.Transition);
265hasCache = any(sn.nodetype == NodeType.Cache);
266
267spawnFedChain = false(sn.nchains, 1);
268if isfield(sn, 'classspawn') && ~isempty(sn.classspawn)
269 for k2 = 1:min(K, length(sn.classspawn))
270 sc2 = sn.classspawn(k2);
271 if sc2 >= 0 && sc2 < K
272 spawnFedChain(sn.chains(:, sc2+1) > 0) = true;
273 end
274 end
275end
276
277if sn.nchains > 0 && ~hasSPN % Only check reachability if chains are defined and not SPN
278 for k = 1:K
279 c = sn.chains(:, k)>0;
280 if any(c) % Only check if class k belongs to a chain
281 for i = 1:M
282 if sn.visits{c}(i,k) == 0
283 % For fork-join models, don't zero out if the metric has a non-zero value
284 % from simulation - the visits calculation doesn't capture fork-join semantics
285 % where Join outputs the parent class
286 if (hasForkJoin || hasCache || any(spawnFedChain(c))) && ~isempty(metric) && metric(i,k) > GlobalConstants.FineTol
287 continue; % Trust the simulation result
288 end
289 outData(i,k) = 0;
290 end
291 end
292 end
293 end
294end
295end
Definition fjtag.m:161