LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
runAnalyzer.m
1function runtime = runAnalyzer(self, options)
2% TSIM = RUN()
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7Tstart=tic;
8
9if nargin<2 %%~exist('options','var')
10 options = self.getOptions;
11end
12
13line_debug(options, 'JMT analyzer starting: lang=%s, samples=%d, seed=%d', options.lang, options.samples, options.seed);
14
15self.runAnalyzerChecks(options);
16Solver.resetRandomGeneratorSeed(options.seed);
17
18switch options.lang
19 case 'java'
20 line_debug(options, 'JMT: using lang=java, delegating to JLINE SolverJMT');
21 jmodel = LINE2JLINE(self.model);
22 M = jmodel.getNumberOfStations;
23 R = jmodel.getNumberOfClasses;
24 jsolver = JLINE.SolverJMT(jmodel, options);
25 T0=tic;
26 [QN,UN,RN,WN,AN,TN] = JLINE.arrayListToResults(jsolver.getAvgTable);
27 runtime = toc(T0);
28 CN = [];
29 XN = [];
30 QN = reshape(QN',R,M)';
31 UN = reshape(UN',R,M)';
32 RN = reshape(RN',R,M)';
33 WN = reshape(WN',R,M)';
34 AN = reshape(AN',R,M)';
35 TN = reshape(TN',R,M)';
36 lG = NaN;
37 lastiter = NaN;
38 self.setAvgResults(QN,UN,RN,TN,AN,WN,CN,XN,runtime,options.method,lastiter);
39 self.result.Prob.logNormConstAggr = lG;
40 self.result.solverSpecific.sn = JLINE.from_jline_struct(jmodel);
41 self.result.Prob.logNormConstAggr = lG;
42 return
43 case 'matlab'
44 line_debug(options, 'JMT: using lang=matlab');
45
46 if ~isfield(options,'verbose')
47 options.verbose = 0;
48 end
49
50 if ~isfield(options,'force')
51 options.force = false;
52 end
53
54 if ~isfield(options,'keep')
55 options.keep = false;
56 end
57
58 if self.enableChecks && ~self.supports(self.model)
59 % if options.verbose
60 line_error(mfilename,'This model contains features not supported by the solver.');
61 % end
62 % runtime = toc(T0);
63 % return
64 end
65
66 if ~isfield(options,'samples')
67 options.samples = 1e4; % default: this is the samples / measure, not the total number of simulation events, which can be much larger.
68 elseif options.samples < 5e3
69 %if ~strcmpi(options.method,'jmva.ls')
70 line_warning(mfilename,'JMT requires at least 5000 samples for each metric, the current value is %d. Starting the simulation with 5000 samples.\n', options.samples);
71 %end
72 options.samples = 5e3;
73 line_debug(options, 'JMT: sample size adjusted to minimum 5000');
74 end
75
76 if ~isfield(options,'verbose')
77 options.verbose = 0;
78 end
79
80 if ~isfield(options,'keep')
81 options.verbose = false;
82 end
83
84 if ~isfield(options,'seed')
85 options.seed = randi([1,1e6]);
86 end
87 self.seed = options.seed;
88
89 if ~isfield(options,'timespan')
90 options.timespan = [0,Inf];
91 else
92 self.maxSimulatedTime = options.timespan(2);
93 end
94
95 if ~self.model.hasInitState
96 self.model.initDefault;
97 end
98
99 self.maxSamples = options.samples;
100 % Sync xmlParser maxSamples with solver (ensures corrected sample count is used in XML)
101 self.xmlParser.maxSamples = options.samples;
102 sn = self.getStruct;
103
104 % Auto-detect transient mode: switch to 'replication' if finite timespan with default method
105 if strcmpi(options.method, 'default') && isfield(options, 'timespan') && isfinite(options.timespan(2))
106 options.method = 'replication';
107 line_debug(options, 'Finite timespan [%f,%f] detected, auto-switching to replication method', options.timespan(1), options.timespan(2));
108 end
109
110 switch options.method
111 case {'jsim','default'}
112 if strcmpi(options.method, 'default')
113 line_debug(options, 'JMT: default method resolved to JSIM');
114 end
115 line_debug(options, 'JMT: using JSIM method (discrete-event simulation), samples=%d, seed=%d', options.samples, options.seed);
116 fname = self.writeJSIM(sn);
117 cmd = ['java -cp "',getJMTJarPath(self),filesep,'JMT.jar" jmt.commandline.Jmt sim "',fname,'" -seed ',num2str(options.seed),' --illegal-access=permit'];
118 if options.verbose
119 line_printf('JMT model: %s\n',fname);
120 end
121 if options.verbose == VerboseLevel.DEBUG
122 line_printf('JMT command: %s\n',cmd);
123 end
124 [status, cmdoutput] = system(cmd);
125 runtime = toc(Tstart);
126 if status ~= 0 && options.verbose
127 line_printf('\nJMT command failed with status %d. Output:\n%s\n', status, cmdoutput);
128 end
129 self.getResults;
130 if ~options.keep && isfolder(getFilePath(self))
131 rmdir(getFilePath(self),'s');
132 end
133 %if options.verbose
134 % line_printf('\nJMT analysis (seed: %d) completed. Runtime: %f seconds.\n',options.seed,runtime);
135 %end
136 self.setAvgResults(self.result.Avg.Q,self.result.Avg.U,self.result.Avg.R,self.result.Avg.T,self.result.Avg.A,self.result.Avg.W,[],[],runtime,options.method,1);
137
138 % Set cache hit/miss ratios from JMT Cache Hit Rate metric
139 for ind = 1:sn.nnodes
140 if sn.nodetype(ind) == NodeType.Cache
141 hitclass = sn.nodeparam{ind}.hitclass;
142 nclasses = length(hitclass);
143 actualhitprob = zeros(1, nclasses);
144 actualmissprob = zeros(1, nclasses);
145
146 % Check if JMT returned Cache Hit Rate metrics
147 fieldName = sprintf('node%d', ind);
148 if isfield(self.result, 'CacheHitRate') && isfield(self.result.CacheHitRate, fieldName)
149 actualhitprob = self.result.CacheHitRate.(fieldName);
150 actualmissprob = 1 - actualhitprob;
151 end
152
153 % Set the results on the Cache node
154 self.model.nodes{ind}.setResultHitProb(actualhitprob);
155 self.model.nodes{ind}.setResultMissProb(actualmissprob);
156
157 % Also update the cached sn struct directly
158 sn.nodeparam{ind}.actualhitprob = actualhitprob;
159 sn.nodeparam{ind}.actualmissprob = actualmissprob;
160 self.model.sn.nodeparam{ind}.actualhitprob = actualhitprob;
161 self.model.sn.nodeparam{ind}.actualmissprob = actualmissprob;
162 end
163 end
164 case {'replication'}
165 line_debug(options, 'JMT: using replication method (transient simulation), iter_max=%d', options.iter_max);
166 options = self.getOptions;
167 initSeed = self.options.seed;
168 initTimeSpan = self.options.timespan;
169 self.options.timespan(1) = self.options.timespan(2);
170 if isfield(options,'timespan') && isfinite(options.timespan(2))
171 tu = [];
172 validReplications = 0;
173 for it=1:options.iter_max
174 self.options.seed = initSeed + it -1;
175 TranSysStateAggr{it} = sampleSysAggr(self);
176 % Skip replications with empty or invalid time vectors
177 if isempty(TranSysStateAggr{it}.t) || ~isvector(TranSysStateAggr{it}.t)
178 line_warning(mfilename, 'Replication %d produced empty/invalid time series, skipping.', it);
179 continue;
180 end
181 validReplications = validReplications + 1;
182 if isempty(tu)
183 tu = TranSysStateAggr{it}.t;
184 else
185 % we need to limit the time series at the minimum
186 % as otherwise the predictor of the state cannot
187 % take into account constraints that exist on the
188 % state space
189 tumax = min(max(tu),max(TranSysStateAggr{it}.t));
190 tu = union(tu, TranSysStateAggr{it}.t);
191 tu = tu(tu<=tumax);
192 end
193 end
194 if validReplications == 0
195 line_error(mfilename, 'No valid replications produced. Cannot compute transient averages.');
196 return;
197 end
198 QNt = cellzeros(sn.nstations, sn.nclasses, length(tu), 2);
199 UNt = cellzeros(sn.nstations, sn.nclasses, length(tu), 2);
200 TNt = cellzeros(sn.nstations, sn.nclasses, length(tu), 2);
201 M = sn.nstations;
202 K = sn.nclasses;
203 for jst=1:M
204 for r=1:K
205 QNt{jst,r}(:,2) = tu;
206 UNt{jst,r}(:,2) = tu;
207 TNt{jst,r}(:,2) = tu;
208 for it=1:options.iter_max
209 % Skip invalid replications
210 if isempty(TranSysStateAggr{it}.t) || ~isvector(TranSysStateAggr{it}.t)
211 continue;
212 end
213 qlenAt_t = interp1(TranSysStateAggr{it}.t, TranSysStateAggr{it}.state{jst}(:,r), tu,'previous');
214 avgQlenAt_t = qlenAt_t;
215 %avgQlenAt_t = cumsum(qlenAt_t .*[0;diff(tu)])./tu;
216 avgQlenAt_t(isnan(avgQlenAt_t))=0;
217 QNt{jst,r}(:,1) = QNt{jst,r}(:,1) + (1/validReplications) * avgQlenAt_t;
218 end
219 for it=1:options.iter_max
220 % Skip invalid replications
221 if isempty(TranSysStateAggr{it}.t) || ~isvector(TranSysStateAggr{it}.t)
222 continue;
223 end
224 if isfinite(sn.nservers(jst))
225 occupancyAt_t = interp1(TranSysStateAggr{it}.t, min(TranSysStateAggr{it}.state{jst}(:,r),sn.nservers(jst)), tu,'previous')/sn.nservers(jst);
226 else % if delay we use queue-length
227 occupancyAt_t = interp1(TranSysStateAggr{it}.t, TranSysStateAggr{it}.state{jst}(:,r), tu,'previous');
228 end
229 avgOccupancyAt_t = occupancyAt_t;
230 %avgOccupancyAt_t = cumsum(occupancyAt_t .*[0;diff(tu)])./tu;
231 avgOccupancyAt_t(isnan(avgOccupancyAt_t))=0;
232 UNt{jst,r}(:,1) = UNt{jst,r}(:,1) + (1/validReplications) * avgOccupancyAt_t;
233 end
234 % for it=1:options.iter_max
235 % departures = [0;diff(TranSysStateAggr{it}.state{j}(:,r))];
236 % departures(departures>0) = 0;
237 % departuresAt_t = abs(interp1(TranSysStateAggr{it}.t, cumsum(departures), tu, 'previous'));
238 % avgDeparturesAt_t = departuresAt_t./tu;
239 % avgDeparturesAt_t(isnan(avgDeparturesAt_t))=0;
240 % TNt{j,r}(:,1) = TNt{j,r}(:,1) + (1/options.iter_max) * avgDeparturesAt_t;
241 % end
242 if isfinite(sn.nservers(jst))
243 TNt{jst,r}(:,1) = UNt{jst,r}(:,1) * sn.nservers(jst) * sn.rates(jst,r);
244 else
245 TNt{jst,r}(:,1) = UNt{jst,r}(:,1) * sn.rates(jst,r);
246 end
247 end
248 end
249 runtime = toc(Tstart);
250 RNt = [];
251 CNt = [];
252 XNt = [];
253 self.setTranAvgResults(QNt,UNt,RNt,TNt,CNt,XNt,runtime);
254 self.result.Tran.Avg.U = UNt;
255 self.result.Tran.Avg.T = TNt;
256 self.result.Tran.Avg.Q = QNt;
257 end
258 self.options.seed = initSeed;
259 self.options.timespan = initTimeSpan;
260 self.result.('solver') = getName(self);
261 self.result.runtime = runtime;
262 %if options.verbose
263 % line_printf('\nJMT analysis (seed: %d) completed. Runtime: %f seconds.\n',options.seed,runtime);
264 %end
265 case {'jmva','jmva.amva','jmva.mva','jmva.recal','jmva.comom','jmva.chow','jmva.bs','jmva.aql','jmva.lin','jmva.dmlin','jmva.ls',...
266 'jmt.jmva','jmt.jmva.mva','jmt.jmva.amva','jmt.jmva.recal','jmt.jmva.comom','jmt.jmva.chow','jmt.jmva.bs','jmt.jmva.aql','jmt.jmva.lin','jmt.jmva.dmlin','jmt.jmva.ls'}
267 line_debug(options, 'JMT: using JMVA method: %s', options.method);
268 fname = self.writeJMVA(sn, getJMVATempPath(self), self.options);
269 cmd = ['java -cp "',getJMTJarPath(self),filesep,'JMT.jar" jmt.commandline.Jmt mva "',fname,'" -seed ',num2str(options.seed),' --illegal-access=permit'];
270 if options.verbose
271 line_printf('JMT model: %s\n',fname);
272 end
273 if options.verbose == VerboseLevel.DEBUG
274 line_printf('JMT command: %s\n',cmd);
275 end
276 [status, cmdoutput] = system(cmd);
277 runtime = toc(Tstart);
278 if status ~= 0 && options.verbose
279 line_printf('\nJMT command failed with status %d. Output:\n%s\n', status, cmdoutput);
280 end
281 self.getResults;
282 if ~options.keep && isfolder(getFilePath(self))
283 rmdir(getFilePath(self),'s');
284 end
285 %if options.verbose
286 % line_printf('\nJMT analysis (method: %d) completed. Runtime: %f seconds.\n',options.method,runtime);
287 %end
288 sn = self.model.getStruct();
289
290 % Compute average arrival rate at steady-state
291 TH = getAvgTputHandles(self);
292 AN = sn_get_arvr_from_tput(sn, self.result.Avg.T, TH);
293 self.setAvgResults(self.result.Avg.Q,self.result.Avg.U,self.result.Avg.R,self.result.Avg.T,AN,self.result.Avg.W,[],[],runtime,options.method,1);
294 otherwise
295 line_warning(mfilename,'This solver does not support the specified method. Setting to default.\n');
296 self.options.method = 'default';
297 runAnalyzer(self);
298 end
299end
300end