1function [runtime, tranSysState, tranSync] = runAnalyzer(self, options)
2% [RUNTIME, TRANSYSSTATE] = RUNANALYZER()
4% Run the LDES solver on the queueing network model.
5% For LayeredNetwork models, use SolverLDES from line-apps.
9 options = self.getOptions;
11self.runAnalyzerChecks(options);
12Solver.resetRandomGeneratorSeed(options.seed);
14line_debug(
'LDES solver starting: lang=%s, samples=%d, seed=%d', options.lang, options.samples, options.seed);
19% Check
if confidence intervals are requested
20[confintEnabled, confintLevel] = Solver.parseConfInt(options.confint);
24 line_debug(
'Default method: using Java LDES discrete-event simulation\n');
25 line_debug(
'Using Java LDES backend');
26 % Convert model to Java
27 jmodel = LINE2JLINE(self.model);
29 % Create Java solver with LDES-specific options via JLINE wrapper
30 jsolver = JLINE.SolverLDES(jmodel, options);
32 % Regular Network analysis
33 [runtime] = runNetworkAnalyzer(self, jmodel, jsolver, confintEnabled, options);
35 line_error(mfilename,
'SolverLDES currently only supports Java backend. Use options.lang = ''java''.');
41function [runtime] = runNetworkAnalyzer(self, jmodel, jsolver, confintEnabled, options)
42% RUNNETWORKANALYZER Run LDES analysis
for regular Network models
44M = jmodel.getNumberOfStations;
45R = jmodel.getNumberOfClasses;
47% Check
if this is transient analysis
48isTransient = isfield(options,
'timespan') && length(options.timespan) >= 2 && ...
49 isfinite(options.timespan(2));
52 % Run transient analysis directly
53 runTransientAnalyzer(self, jsolver, M, R, options);
54 runtime = 0; % Runtime tracked inside transient analyzer
58% Steady-state analysis
59[QN, UN, RN, WN, AN, TN] = JLINE.arrayListToResults(jsolver.getAvgTable(
true));
61CN = JLINE.from_jline_matrix(jsolver.getAvgSysRespT());
62XN = JLINE.from_jline_matrix(jsolver.getAvgSysTput());
63runtime = jsolver.result.runtime;
64QN = reshape(QN
', R, M)';
65UN = reshape(UN
', R, M)';
66RN = reshape(RN
', R, M)';
67TN = reshape(TN
', R, M)';
68WN = reshape(WN
', R, M)';
69AN = reshape(AN
', R, M)';
71% Extract FCR metrics and append to result matrices
72sn = self.model.getStruct;
75 result = jsolver.result;
76 isValidMatrix = @(x) ~isempty(x) && isa(x,
'jline.util.matrix.Matrix');
77 if isValidMatrix(result.QNfcr)
78 QNfcr = JLINE.from_jline_matrix(result.QNfcr);
79 UNfcr = JLINE.from_jline_matrix(result.UNfcr);
80 RNfcr = JLINE.from_jline_matrix(result.RNfcr);
81 TNfcr = JLINE.from_jline_matrix(result.TNfcr);
82 WNfcr = JLINE.from_jline_matrix(result.WNfcr);
83 % ANfcr
is NaN for FCR (not applicable)
85 % Append FCR rows to station metrics
95% Print samples like SSA does
97 line_printf('LDES samples: %8d\n', options.samples);
100self.setAvgResults(QN, UN, RN, TN, AN, WN, CN, XN, runtime, options.method, options.samples);
102% Marshal cache hit/miss/expected-latency results from the Java model back onto
103% the MATLAB Cache
nodes (LDES
is Java-backed; the native solvers set these
104% directly). For retrieval (delayed-hit) systems the miss ratio
is pi_{i,0} and
105% the expected latency
is the measured retrieval latency.
107 if sn.nodetype(ind) == NodeType.Cache
108 mcache = self.model.nodes{ind};
109 % Clear any cache hit/miss/delayed/latency split left on
this node by a
110 % previously run solver BEFORE marshalling. getRoutingMatrix bakes a
111 % populated actualHitProb into the cache routing, so a stale split from
112 % e.g. a prior SSA run would otherwise survive a failed or partial
113 % marshal below and make the reported cache metrics order-dependent
114 % (LDES would echo the prior solver). Cleared first so any marshalling
115 % failure leaves the node empty (reported as NaN) rather than stale.
116 mcache.setResultHitProb(sparse([]));
117 mcache.setResultMissProb(sparse([]));
118 mcache.setResultDelayedHitProb(sparse([]));
119 mcache.setResultResidT(sparse([]));
121 jcache = jmodel.getNodeByName(mcache.getName());
126 % Marshal each field independently: a single missing accessor (e.g.
127 % getResidT on an older jline.jar still on MATLAB
's static classpath)
128 % must NOT discard the hit/miss/delayed results that ARE available.
129 try, hp = JLINE.from_jline_matrix(jcache.getHitRatio()); if ~isempty(hp), mcache.setResultHitProb(hp); end; catch, end
130 try, mp = JLINE.from_jline_matrix(jcache.getMissRatio()); if ~isempty(mp), mcache.setResultMissProb(mp); end; catch, end
131 try, dhp = JLINE.from_jline_matrix(jcache.getDelayedHitRatio()); if ~isempty(dhp), mcache.setResultDelayedHitProb(dhp); end; catch, end
132 try, lp = JLINE.from_jline_matrix(jcache.getResidT()); if ~isempty(lp), mcache.setResultResidT(lp); end; catch, end
137% NB: the cache hit/miss/delayed split is stored on the Cache node above
138% (setResultHitProb etc.). The node-table reconstruction in @NetworkSolver/
139% getAvgNode resyncs that split from the node into a LOCAL struct copy, so the
140% reported throughputs reflect THIS simulation without persisting the split into
141% the shared model.sn (refreshStruct(true) would bake it into sn.rtnodes and
142% perturb a subsequently-run exact solver such as CTMC -- order dependence).
144% Extract confidence intervals from Java solver results
146 result = jsolver.result;
147 % Helper to check for non-null Java objects
148 isValidMatrix = @(x) ~isempty(x) && isa(x, 'jline.util.matrix.Matrix
');
149 if isValidMatrix(result.QNCI)
150 QNCI = JLINE.from_jline_matrix(result.QNCI);
151 QNCI = reshape(QNCI', R, M)
';
155 if isValidMatrix(result.UNCI)
156 UNCI = JLINE.from_jline_matrix(result.UNCI);
157 UNCI = reshape(UNCI', R, M)
';
161 if isValidMatrix(result.RNCI)
162 RNCI = JLINE.from_jline_matrix(result.RNCI);
163 RNCI = reshape(RNCI', R, M)
';
167 if isValidMatrix(result.TNCI)
168 TNCI = JLINE.from_jline_matrix(result.TNCI);
169 TNCI = reshape(TNCI', R, M)
';
173 if isValidMatrix(result.ANCI)
174 ANCI = JLINE.from_jline_matrix(result.ANCI);
175 ANCI = reshape(ANCI', R, M)
';
179 if isValidMatrix(result.WNCI)
180 WNCI = JLINE.from_jline_matrix(result.WNCI);
181 WNCI = reshape(WNCI', R, M)
';
186 self.setAvgResultsCI(QNCI, UNCI, RNCI, TNCI, ANCI, WNCI, [], []);
191function runTransientAnalyzer(self, jsolver, Mjava, R, options)
192% RUNTRANSIENTANALYZER Run transient analysis for LDES
196% Get MATLAB model structure for correct station count
197sn = self.model.getStruct;
198M = sn.nstations; % Use MATLAB station count for cell arrays
200% Run transient analysis on Java side
202result = jsolver.result;
205% Check if transient results are available (result.QNt is a Java 2D array)
206if ~isempty(result.QNt) && ~isempty(result.t)
207 % Get number of time points
208 Tmax = result.t.length();
210 % Convert Java transient results to MATLAB format
211 % result.QNt(ist,r) is a Matrix(numTimePoints, 2) with columns [value, time]
212 % Use MATLAB station count for cell arrays so getTranAvg can index correctly
215 RNt = cell(M, R); % Not computed by LDES, will be empty
220 % Initialize all cells to NaN
230 % Extract data from Java results (indexed by Java station count)
233 % Queue length transient
235 jmatrix = result.QNt(ist, r);
236 if ~isempty(jmatrix) && jmatrix.getNumRows() > 0
237 nRows = jmatrix.getNumRows();
238 matData = NaN(nRows, 2);
240 matData(p, 1) = jmatrix.get(p-1, 0); % value
241 matData(p, 2) = jmatrix.get(p-1, 1); % time
243 QNt{ist, r} = matData;
251 % Utilization transient
253 jmatrix = result.UNt(ist, r);
254 if ~isempty(jmatrix) && jmatrix.getNumRows() > 0
255 nRows = jmatrix.getNumRows();
256 matData = NaN(nRows, 2);
258 matData(p, 1) = jmatrix.get(p-1, 0); % value
259 matData(p, 2) = jmatrix.get(p-1, 1); % time
261 UNt{ist, r} = matData;
269 % Throughput transient
271 jmatrix = result.TNt(ist, r);
272 if ~isempty(jmatrix) && jmatrix.getNumRows() > 0
273 nRows = jmatrix.getNumRows();
274 matData = NaN(nRows, 2);
276 matData(p, 1) = jmatrix.get(p-1, 0); % value
277 matData(p, 2) = jmatrix.get(p-1, 1); % time
279 TNt{ist, r} = matData;
287 % Response time transient not computed by LDES
292 % CNt and XNt not computed for transient
298 % Store transient results
299 self.setTranAvgResults(QNt, UNt, RNt, TNt, CNt, XNt, runtime);
301 % Also compute and store steady-state estimates from final transient values
313 if ~isscalar(QNt{ist, r}) && ~isnan(QNt{ist, r}(end, 1))
314 QN(ist, r) = QNt{ist, r}(end, 1);
316 if ~isscalar(UNt{ist, r}) && ~isnan(UNt{ist, r}(end, 1))
317 UN(ist, r) = UNt{ist, r}(end, 1);
319 if ~isscalar(TNt{ist, r}) && ~isnan(TNt{ist, r}(end, 1))
320 TN(ist, r) = TNt{ist, r}(end, 1);
325 self.setAvgResults(QN, UN, RN, TN, AN, WN, CN, XN, runtime, options.method, 0);
329% Print verbose output if requested
331 line_printf('LDES transient analysis complete, timespan = [%g, %g]\n
', ...
332 options.timespan(1), options.timespan(2));