LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
runAnalyzer.m
1function runtime = runAnalyzer(self, options)
2% RUNTIME = RUN()
3% Run the solver
4
5T0=tic;
6iter = NaN;
7if nargin<2
8 options = self.getOptions;
9end
10% Wall-clock time-budget launch marker (see options.timeout / lineTimeoutExceeded)
11options.timeout_tic = T0;
12
13if strcmp(options.lang,'python')
14 line_debug(options, 'FLD: using lang=python, delegating to native line_solver');
15 [QN,UN,RN,TN,AN,WN,runtime] = PYLINE.getAvg(self.name, self.model, options);
16 self.setAvgResults(QN,UN,RN,TN,AN,WN,[],[],runtime,options.method,NaN);
17 return
18end
19
20sn = getStruct(self); % this gets modified later on so pass by copy
21
22% Convert non-Markovian distributions to PH
23sn = sn_nonmarkov_toph(sn, options);
24
25orig_method = options.method;
26self.runAnalyzerChecks(options);
27
28% Finite Capacity Region: the fluid ODEs do not enforce the aggregate
29% per-region job limit and would silently return the unconstrained answer.
30if isfield(sn,'nregions') && sn.nregions > 0
31 line_error(mfilename,'This model uses a Finite Capacity Region (addRegion), which is not supported by SolverFLD. Use SolverCTMC, SolverJMT, SolverSSA or SolverLDES, or setCapacity for a single-station limit.');
32end
33
34Solver.resetRandomGeneratorSeed(options.seed);
35
36% Show library attribution if verbose and not yet shown
37if options.verbose ~= VerboseLevel.SILENT && ~GlobalConstants.isLibraryAttributionShown()
38 libs = SolverFLD.getLibrariesUsed(sn, options);
39 if ~isempty(libs)
40 line_printf('The solver will leverage %s.\n', strjoin(libs, ', '));
41 GlobalConstants.setLibraryAttributionShown(true);
42 end
43end
44
45%options.lang = 'java';
46
47hasOpenClasses = sn_has_open_classes(sn);
48switch options.lang
49 case {'java'}
50 line_debug(options, 'FLD: using lang=java, delegating to JLINE');
51 jmodel = LINE2JLINE(self.model);
52 %M = jmodel.getNumberOfStatefulNodes;
53 M = jmodel.getNumberOfStations;
54 R = jmodel.getNumberOfClasses;
55 switch options.method
56 case {'default', 'closing', 'matrix', 'rmf'}
57 jsolver = JLINE.SolverFluid(jmodel, options);
58 otherwise
59 line_warning(mfilename,'This solver does not support the specified method. Setting to default.\n');
60 options.method = 'default';
61 jsolver = JLINE.SolverFluid(jmodel, options);
62 end
63 [QN,UN,RN,WN,AN,TN] = JLINE.arrayListToResults(jsolver.getAvgTable);
64 runtime = toc(T0);
65 CN = [];
66 XN = [];
67 QN = reshape(QN',R,M)';
68 UN = reshape(UN',R,M)';
69 RN = reshape(RN',R,M)';
70 WN = reshape(WN',R,M)';
71 AN = reshape(AN',R,M)';
72 TN = reshape(TN',R,M)';
73 lG = NaN;
74 lastiter = NaN;
75 self.setAvgResults(QN,UN,RN,TN,AN,WN,CN,XN,runtime,options.method,lastiter);
76 self.result.Prob.logNormConstAggr = lG;
77 self.result.solverSpecific.sn = JLINE.from_jline_struct(jmodel);
78 self.result.solverSpecific.odeStateVec = JLINE.from_jline_matrix(jsolver.result.odeStateVec);
79 return
80 case 'matlab'
81 line_debug(options, 'FLD: using lang=matlab');
82 switch options.method
83 case {'matrix','pnorm'}
84 line_debug(options, 'FLD method: matrix/pnorm requested');
85 % Matrix method now supports mixed/open models per Ruuskanen et al., PEVA 151 (2021)
86 if sn_has_dps(sn)
87 line_error(mfilename,'The matrix solver does not support DPS scheduling. Use options.method=''closing'' instead.');
88 end
89 case {'closing'}
90 options.method = 'closing';
91 line_debug(options, 'FLD method: closing');
92 case {'default'}
93 % Use rmf for cache models, closing for DPS, matrix otherwise
94 if any(sn.nodetype == NodeType.Cache)
95 options.method = 'rmf';
96 elseif sn_has_dps(sn)
97 options.method = 'closing';
98 else
99 options.method = 'matrix';
100 end
101 line_debug(options, 'FLD default method resolved to: %s', options.method);
102 case {'statedep','softmin'}
103 line_debug(options, 'FLD method: %s', options.method);
104 % do nothing
105 case {'tbi','fluid.tbi'}
106 options.method = 'tbi';
107 line_debug(options, 'FLD method: trajectory-based iteration');
108 % Trajectory-based iteration (Sheldon, Tuncer, Casale, IEEE T-ITS):
109 % cell-decomposed waveform relaxation of the closing ODEs
110 if hasOpenClasses
111 line_error(mfilename,'The tbi method supports closed models only.');
112 end
113 if any(sn.nodetype == NodeType.Cache)
114 line_error(mfilename,'The tbi method does not support caching stations. Use options.method=''rmf'' instead.');
115 end
116 case {'diffusion','fluid.diffusion'}
117 line_debug(options, 'FLD method: diffusion approximation');
118 % Diffusion approximation - validated in solver_fluid_diffusion
119 % do nothing here, validation happens in the solver itself
120 case {'mfq','fluid.mfq'}
121 line_debug(options, 'FLD method: Markovian fluid queue');
122 % Markovian fluid queue method for single-queue analysis
123 % Validation and fallback happens in solver_fluid_analyzer
124 case {'rmf','fluid.rmf'}
125 line_debug(options, 'FLD method: refined mean field (cache analysis)');
126 % Refined mean field method for multi-list cache analysis
127 % Uses DDPP framework with 1/N correction
128 otherwise
129 line_error(mfilename,sprintf('The ''%s'' method is unsupported by this solver.',options.method));
130 end
131end
132
133if isinf(options.timespan(1))
134 if options.verbose == 2
135 line_warning(mfilename,'%s requires options.timespan(1) to be finite. Setting it to 0.\n',mfilename);
136 end
137 options.timespan(1) = 0;
138end
139
140if options.timespan(1) == options.timespan(2)
141 line_warning(mfilename,'%s: timespan is a single point, unsupported. Setting options.timespace(1) to 0.\n',mfilename);
142 options.timespan(1) = 0;
143end
144
145if self.enableChecks && ~self.supports(self.model)
146 line_error(mfilename,'This model contains features not supported by the solver.');
147end
148
149self.setOptions(options);
150
151M = sn.nstations;
152K = sn.nclasses;
153
154%%
155lastSol= [];
156Q = zeros(M,K); R = zeros(M,K); T = zeros(M,K);
157U = zeros(M,K); C = zeros(1,K); X = zeros(1,K);
158Qt=[];
159s0 = sn.space;
160s0prior = sn.stateprior;
161s0_sz = cellfun(@(x) size(x,1), s0)';
162s0_id = pprod(s0_sz-1);
163cur_state = sn.state;
164while s0_id>=0 % for all possible initial states
165 s0prior_val = 1;
166 for ind=1:sn.nnodes
167 if sn.isstateful(ind)
168 isf = sn.nodeToStateful(ind);
169 s0prior_val = s0prior_val * s0prior{isf}(1+s0_id(isf)); % update prior
170 %sn.state{isf} = s0{isf}(1+s0_id(isf),:); % assign initial state to network
171 self.model.nodes{ind}.setState(s0{isf}(1+s0_id(isf),:));
172 end
173 end
174 sn = self.model.getStruct;
175 sn = sn_nonmarkov_toph(sn, options); % Re-apply conversion after fresh struct
176 if s0prior_val > 0
177 %useJLine = false;
178 %if useJLine
179 % [Qfull, Ufull, Rfull, Tfull, Cfull, Xfull, t, Qfull_t, Ufull_t, Tfull_t, lastSol] = JLINE.runFluidAnalyzer(self.model, options);
180 %else
181 [Qfull, Ufull, Rfull, Tfull, Cfull, Xfull, t, Qfull_t, Ufull_t, Tfull_t, lastSol, iter, aoiResults] = solver_fluid_analyzer(sn, options);
182 %end
183
184 [t,uniqueIdx] = unique(t);
185 if isempty(lastSol) % if solution fails
186 Q = NaN*ones(M,K); R = NaN*ones(M,K);
187 T = NaN*ones(M,K); U = NaN*ones(M,K);
188 C = NaN*ones(1,K); X = NaN*ones(1,K);
189 Qt = cell(M,K); Ut = cell(M,K); Tt = cell(M,K);
190 for ist=1:M
191 for r=1:K
192 Qt{ist,r} = [NaN,NaN];
193 Ut{ist,r} = [NaN,NaN];
194 Tt{ist,r} = [NaN,NaN];
195 end
196 end
197 else
198 if isempty(self.result) && max(size(Qt))==0 %~exist('Qt','var')
199 Q = Qfull*s0prior_val;
200 R = Rfull*s0prior_val;
201 T = Tfull*s0prior_val;
202 U = Ufull*s0prior_val;
203 C = Cfull*s0prior_val;
204 X = Xfull*s0prior_val;
205 Qt = cell(M,K);
206 Ut = cell(M,K);
207 Tt = cell(M,K);
208 for ist=1:M
209 for r=1:K
210 if ~isempty(Qfull_t{ist,r}) && length(Qfull_t{ist,r}) >= max(uniqueIdx)
211 Qfull_t{ist,r} = Qfull_t{ist,r}(uniqueIdx);
212 Ufull_t{ist,r} = Ufull_t{ist,r}(uniqueIdx);
213 Tfull_t{ist,r} = Tfull_t{ist,r}(uniqueIdx);
214 Qt{ist,r} = [Qfull_t{ist,r} * s0prior_val,t];
215 Ut{ist,r} = [Ufull_t{ist,r} * s0prior_val,t];
216 Tt{ist,r} = [Tfull_t{ist,r} * s0prior_val,t];
217 else
218 Qt{ist,r} = [NaN,NaN];
219 Ut{ist,r} = [NaN,NaN];
220 Tt{ist,r} = [NaN,NaN];
221 end
222 end
223 end
224 else
225 Q = Q + Qfull*s0prior_val;
226 R = R + Rfull*s0prior_val;
227 T = T + Tfull*s0prior_val;
228 U = U + Ufull*s0prior_val;
229 C = C + Cfull*s0prior_val;
230 X = X + Xfull*s0prior_val;
231 for ist=1:M
232 for r=1:K
233 [t,uniqueIdx] = unique(t);
234 Qfull_t{ist,r} = Qfull_t{ist,r}(uniqueIdx);
235 Ufull_t{ist,r} = Ufull_t{ist,r}(uniqueIdx);
236 % Tfull_t{i,r} = Tfull_t{i,r}(uniqueIdx);
237
238 tunion = union(Qt{ist,r}(:,2), t);
239 dataOld = interp1(Qt{ist,r}(:,2),Qt{ist,r}(:,1),tunion);
240 dataNew = interp1(t,Qfull_t{ist,r},tunion);
241 Qt{ist,r} = [dataOld + s0prior_val * dataNew, tunion];
242
243 dataOld = interp1(Ut{ist,r}(:,2),Ut{ist,r}(:,1),tunion);
244 dataNew = interp1(t,Ufull_t{ist,r},tunion);
245 Ut{ist,r} = [dataOld + s0prior_val * dataNew, tunion];
246
247 % dataOld = interp1(Tt{i,r}(:,2),Tt{i,r}(:,1),tunion);
248 % dataNew = interp1(t,Tfull_t{i,r},tunion);
249 % Tt{i,r} = [dataOld + s0prior_val * dataNew, tunion];
250 end
251 end
252 end
253 end
254 end
255 s0_id=pprod(s0_id,s0_sz-1); % update initial state
256end
257% Now we restore the original state
258for ind=1:sn.nnodes
259 if sn.isstateful(ind)
260 isf = sn.nodeToStateful(ind);
261 self.model.nodes{ind}.setState(cur_state{isf});
262 end
263end
264runtime = toc(T0);
265self.result.solverSpecific = lastSol;
266% Store AoI results if available
267if exist('aoiResults', 'var') && ~isempty(aoiResults)
268 self.result.solverSpecific.aoiResults = aoiResults;
269end
270QN = Q; UN=U; RN=R; TN=T; CN=C; XN=X;
271
272% For cache models solved with rmf, update hit/miss probs in the model
273if any(strcmp(options.method, {'rmf','fluid.rmf'})) && any(sn.nodetype == NodeType.Cache)
274 caches = find(sn.nodetype == NodeType.Cache);
275 % Retrieve hitprob/missprob from the cacheqn result stored in lastSol
276 if isfield(lastSol, 'cacheHitProb')
277 for cIdx = 1:length(caches)
278 ind = caches(cIdx);
279 self.model.nodes{ind}.setResultHitProb(lastSol.cacheHitProb(cIdx,:));
280 self.model.nodes{ind}.setResultMissProb(lastSol.cacheMissProb(cIdx,:));
281 end
282 self.model.refreshStruct(true);
283 sn = self.model.getStruct(true);
284 end
285end
286
287% Compute average arrival rate at steady-state
288AN = sn_get_arvr_from_tput(sn, TN, self.getAvgTputHandles());
289if strcmp(orig_method,'default') && ~strcmp(options.method,'default')
290 self.setAvgResults(QN,UN,RN,TN,AN,[],CN,XN,runtime,['default/',options.method],iter);
291else
292 self.setAvgResults(QN,UN,RN,TN,AN,[],CN,XN,runtime,options.method,iter);
293end
294Rt={}; Xt={}; Ct={};
295self.setTranAvgResults(Qt,Ut,Rt,Tt,Ct,Xt,runtime);
296if lineTimeoutExceeded(options)
297 self.result.Avg.timedOut = true;
298 line_warning(mfilename,'Solver stopped after the wall-clock time budget (options.timeout=%gs) was exceeded; returning the interim solution.\n', options.timeout);
299end
300end
Definition fjtag.m:157
Definition Station.m:245