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
10sn = self.model.getStruct();
11
12if strcmp(self.options.lang,'java') && ~strcmp(self.name,'SolverDES')
13 T0=tic;
14 M = sn.nstations;
15 R = sn.nclasses;
16 % Force fresh Java model conversion each time to ensure current state
17 self.model.obj = [];
18 self.setLang();
19 self.obj.getOptions.verbose = jline.VerboseLevel.STD;
20 SolverResult = self.obj.getAvg();
21 QN = JLINE.from_jline_matrix(SolverResult.QN);
22 UN = JLINE.from_jline_matrix(SolverResult.UN);
23 RN = JLINE.from_jline_matrix(SolverResult.RN);
24 TN = JLINE.from_jline_matrix(SolverResult.TN);
25 AN = JLINE.from_jline_matrix(SolverResult.AN);
26 WN = JLINE.from_jline_matrix(SolverResult.WN);
27 runtime=SolverResult.runtime;
28 method=SolverResult.method;
29 % Extract cache hit/miss probabilities from Java model
30 for ind = 1:sn.nnodes
31 if sn.nodetype(ind) == NodeType.Cache
32 jnode = self.model.obj.getNodeByIndex(ind-1);
33 hitRatioVec = JLINE.from_jline_matrix(jnode.getHitRatio());
34 missRatioVec = JLINE.from_jline_matrix(jnode.getMissRatio());
35 % Store per-class hit/miss probabilities matching MATLAB format:
36 % only parent classes with hitClass>0 have non-zero entries
37 hitClass = self.model.nodes{ind}.getHitClass;
38 nk = length(hitClass);
39 hitprob = zeros(1, nk);
40 missprob = zeros(1, nk);
41 for k = 1:nk
42 if hitClass(k) > 0 && k <= length(hitRatioVec)
43 hitprob(k) = hitRatioVec(k);
44 missprob(k) = missRatioVec(k);
45 end
46 end
47 self.model.nodes{ind}.setResultHitProb(hitprob);
48 self.model.nodes{ind}.setResultMissProb(missprob);
49 end
50 end
51 if any(sn.nodetype == NodeType.Cache)
52 self.model.refreshStruct(true);
53 end
54 self.setAvgResults(QN,UN,RN,TN,AN,WN,[],[],runtime,method,1);
55 QNclass = reshape(QN,M,R);
56 UNclass = reshape(UN,M,R);
57 RNclass = reshape(RN,M,R);
58 TNclass = reshape(TN,M,R);
59 ANclass = reshape(AN,M,R);
60 WNclass = reshape(WN,M,R);
61 return
62end
63
64%%
65if nargin == 1 % no parameter
66 if isempty(self.model.handles) || ~isfield(self.model.handles,'Q') || ...
67 ~isfield(self.model.handles,'U') || ~isfield(self.model.handles,'R') || ...
68 ~isfield(self.model.handles,'T') || ~isfield(self.model.handles,'A') || ...
69 ~isfield(self.model.handles,'W')
70 reset(self); % reset results in case there are partial results saved
71 end
72 [Q,U,R,T,A,W] = self.getAvgHandles;
73elseif nargin == 2
74 handlers = Q;
75 [Q,U,R,T,A,W] = deal(handlers{:}); % set Q=handlers{1}, U=handlers{2}, ...
76end
77
78if isfield(self.options,'timespan')
79 if isfinite(self.options.timespan(2))
80 line_error(mfilename,'The getAvg method does not support the timespan option, use the getTranAvg method instead.');
81 end
82else
83 self.options.timespan = [0,Inf];
84end
85
86if ~self.hasAvgResults() || ~self.options.cache
87 runAnalyzer(self);
88 % the next line is required because getAvg can alter the chain
89 % structure in the presence of caches so we need to reload sn
90 sn = self.model.getStruct;
91 if ~self.hasAvgResults
92 line_error(mfilename,'Unable to return results for this model.');
93 end
94end % else return cached value
95
96
97M = sn.nstations;
98K = sn.nclasses;
99
100% Check if this is an SPN model (Places don't have response times)
101hasSPN = any(sn.nodetype == NodeType.Place) || any(sn.nodetype == NodeType.Transition);
102
103if ~isempty(R)
104 RNclass = filterMetric(R, self.result.Avg.R, [], sn, K, M);
105else
106 RNclass = [];
107end
108
109if ~isempty(Q)
110 % For SPNs, don't zero Q based on R because Places don't have response times
111 if hasSPN
112 zeroMaskQ = [];
113 else
114 zeroMaskQ = RNclass < 10 * GlobalConstants.FineTol;
115 end
116 QNclass = filterMetric(Q, self.result.Avg.Q, zeroMaskQ, sn, K, M);
117else
118 QNclass = [];
119end
120
121if ~isempty(U)
122 % For SPNs, don't zero U based on R because Places don't have response times
123 if hasSPN
124 zeroMaskU = [];
125 else
126 zeroMaskU = RNclass < 10 * GlobalConstants.FineTol;
127 end
128 UNclass = filterMetric(U, self.result.Avg.U, zeroMaskU, sn, K, M);
129else
130 UNclass = [];
131end
132
133if ~isempty(T)
134 TNclass = filterMetric(T, self.result.Avg.T, [], sn, K, M);
135else
136 TNclass = [];
137end
138
139if ~isempty(A)
140 zeroMask = false(size(RNclass));
141 zeroMask(sn.nodeToStation(sn.nodetype==NodeType.Source),:) = true;
142 ANclass = filterMetric(A, self.result.Avg.A, zeroMask, sn, K, M);
143else
144 ANclass = [];
145end
146
147if ~isempty(W)
148 WNclass = sn_get_residt_from_respt(sn, RNclass, W);
149else
150 WNclass = [];
151end
152
153if ~isempty(UNclass)
154 unstableQueues = find(sum(UNclass,2)>0.99 * sn.nservers);
155 if any(unstableQueues) && any(isinf(sn.njobs))
156 line_warning(mfilename,'The model has unstable queues, performance metrics may grow unbounded.\n')
157 end
158end
159end
160
161function outData = filterMetric(handle, metric, zeroMask, sn, K, M)
162% post-process Avg measure
163outData = zeros(M, K);
164for k = 1:K
165 for i = 1:M
166 if ~handle{i,k}.disabled && ~isempty(metric)
167 outData(i,k) = metric(i,k);
168 else
169 outData(i,k) = NaN;
170 end
171 end
172end
173
174% NaN values indicate that a metric is disabled
175outData(isnan(outData)) = 0;
176% set to zero entries associated to immediate transitions
177outData(zeroMask) = 0;
178% round to zero numerical perturbations
179outData(outData < GlobalConstants.FineTol) = 0;
180
181% set to zero metrics for classes that are unreachable
182% but skip this check for fork-join models where the visits calculation
183% doesn't correctly capture the parent class visiting Join and downstream nodes
184% also skip when no chains are defined (routing not specified)
185% also skip for SPN models where Places don't have traditional visits
186hasForkJoin = any(sn.nodetype == NodeType.Fork) && any(sn.nodetype == NodeType.Join);
187hasSPN = any(sn.nodetype == NodeType.Place) || any(sn.nodetype == NodeType.Transition);
188
189if sn.nchains > 0 && ~hasSPN % Only check reachability if chains are defined and not SPN
190 for k = 1:K
191 c = sn.chains(:, k)>0;
192 if any(c) % Only check if class k belongs to a chain
193 for i = 1:M
194 if sn.visits{c}(i,k) == 0
195 % For fork-join models, don't zero out if the metric has a non-zero value
196 % from simulation - the visits calculation doesn't capture fork-join semantics
197 % where Join outputs the parent class
198 if hasForkJoin && ~isempty(metric) && metric(i,k) > GlobalConstants.FineTol
199 continue; % Trust the simulation result
200 end
201 outData(i,k) = 0;
202 end
203 end
204 end
205 end
206end
207end
Definition mmt.m:93