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