1function [simDoc, section] = saveImpatience(self, simDoc, section, ind)
2% [SIMDOC, SECTION] = SAVEIMPATIENCE(SIMDOC, SECTION, IND)
4% Generates XML
for impatience (reneging) distributions
for JMT Queue sections
6% Copyright (c) 2012-2026, Imperial College London
9impatienceNode = simDoc.createElement(
'parameter');
10impatienceNode.setAttribute(
'array',
'true');
11impatienceNode.setAttribute(
'classPath',
'jmt.engine.NetStrategies.ImpatienceStrategies.Impatience');
12impatienceNode.setAttribute(
'name',
'Impatience');
15numOfClasses = sn.nclasses;
16exportClasses = self.getExportableClasses();
17i = sn.nodeToStation(ind);
20 % Skip classes that should not be exported to JMT
25 refClassNode = simDoc.createElement(
'refClass');
26 refClassNode.appendChild(simDoc.createTextNode(sn.classnames{r}));
27 impatienceNode.appendChild(refClassNode);
29 % Determine impatience kind
for this (station,
class). JMT models one
30 % impatience type per station-
class: BALKING (queue-length join refusal)
31 % or RENEGING (patience-driven abandonment). The array element must carry
32 %
the *concrete* strategy classPath (Balking / Reneging) so that
the JMT
33 % engine SimLoader instantiates it via
the matching constructor;
the
34 % Impatience *interface* classPath cannot be instantiated and
the strategy
35 % would be silently dropped.
37 if i > 0 && isfield(sn,
'balkingStrategy') && ~isempty(sn.balkingStrategy)
38 if sn.balkingStrategy(i, r) == BalkingStrategy.QUEUE_LENGTH
42 % Reneging
is flagged by impatienceClass == RENEGING, not by
43 % impatienceType (
the patience *distribution*): ProcessType.EXP == 0 in
44 % MATLAB, so an `impatienceType ~= 0` test silently misses exponential
45 % patience,
the most common case.
46 hasImpatience = false;
47 if i > 0 && isfield(sn, 'impatienceClass') && ~isempty(sn.impatienceClass)
48 if sn.impatienceClass(i, r) == ImpatienceType.RENEGING
53 impatienceStrategyNode = simDoc.createElement('subParameter');
56 % Balking: emit a Balking strategy wrapping a load-dependent range
57 % table {from -> probability} plus
the priorityActivated flag.
58 impatienceStrategyNode.setAttribute(
'classPath',
'jmt.engine.NetStrategies.ImpatienceStrategies.Balking');
59 impatienceStrategyNode.setAttribute(
'name',
'Balking');
60 [simDoc, impatienceStrategyNode] = saveBalkingStrategy(self, simDoc, impatienceStrategyNode, sn.balkingThresholds{i, r}, sn.nservers(i));
61 impatienceNode.appendChild(impatienceStrategyNode);
65 impatienceStrategyNode.setAttribute(
'classPath',
'jmt.engine.NetStrategies.ImpatienceStrategies.Reneging');
66 impatienceStrategyNode.setAttribute(
'name',
'Reneging');
69 % No impatience defined - use null
70 subParValue = simDoc.createElement(
'value');
71 subParValue.appendChild(simDoc.createTextNode(
'null'));
72 impatienceStrategyNode.appendChild(subParValue);
74 % Impatience
is defined - generate distribution XML
75 procType = sn.impatienceType(i, r);
76 impatiencePhases = sn.impatiencePhases(i, r);
78 % jmt.engine.random.HyperExpPar carries exactly (p, lambda1, lambda2),
79 % so it can only express a 2-phase hyper-exponential. An n>2 HyperExp
80 %
is exported as
the acyclic phase-type built from its OWN PH
81 % representation (alpha = p, T = -diag(lambda)), which
is the same
82 % distribution rather than a moment-matched refit. This mirrors
83 % saveServiceStrategy.m, so that patience and service agree.
84 emitPhaseType = (procType == ProcessType.PH) || ...
85 (procType == ProcessType.APH) || ...
86 (procType == ProcessType.COXIAN) || ...
87 (impatiencePhases > 2 && procType == ProcessType.HYPEREXP);
89 distributionNode = simDoc.createElement(
'subParameter');
92 javaClass =
'jmt.engine.random.PhaseTypeDistr';
93 javaParClass =
'jmt.engine.random.PhaseTypePar';
97 javaClass =
'jmt.engine.random.DeterministicDistr';
98 javaParClass =
'jmt.engine.random.DeterministicDistrPar';
99 case ProcessType.ERLANG
100 javaClass =
'jmt.engine.random.Erlang';
101 javaParClass =
'jmt.engine.random.ErlangPar';
103 javaClass =
'jmt.engine.random.Exponential';
104 javaParClass =
'jmt.engine.random.ExponentialPar';
105 case ProcessType.GAMMA
106 javaClass =
'jmt.engine.random.GammaDistr';
107 javaParClass =
'jmt.engine.random.GammaDistrPar';
108 case ProcessType.HYPEREXP
109 javaClass =
'jmt.engine.random.HyperExp';
110 javaParClass =
'jmt.engine.random.HyperExpPar';
111 case ProcessType.PARETO
112 javaClass =
'jmt.engine.random.Pareto';
113 javaParClass =
'jmt.engine.random.ParetoPar';
114 case ProcessType.WEIBULL
115 javaClass =
'jmt.engine.random.Weibull';
116 javaParClass =
'jmt.engine.random.WeibullPar';
117 case ProcessType.LOGNORMAL
118 javaClass =
'jmt.engine.random.Lognormal';
119 javaParClass =
'jmt.engine.random.LognormalPar';
120 case ProcessType.UNIFORM
121 javaClass =
'jmt.engine.random.Uniform';
122 javaParClass =
'jmt.engine.random.UniformPar';
124 line_error(mfilename, sprintf(
'Unsupported impatience distribution type: %s', ProcessType.toText(procType)));
128 distributionNode.setAttribute(
'classPath', javaClass);
130 distributionNode.setAttribute(
'name',
'Phase-Type');
134 distributionNode.setAttribute(
'name',
'Exponential');
135 case ProcessType.HYPEREXP
136 distributionNode.setAttribute(
'name',
'Hyperexponential');
138 distributionNode.setAttribute(
'name', ProcessType.toText(procType));
141 impatienceStrategyNode.appendChild(distributionNode);
143 % Create distribution parameters
144 distrParNode = simDoc.createElement(
'subParameter');
145 distrParNode.setAttribute(
'classPath', javaParClass);
146 distrParNode.setAttribute(
'name',
'distrPar');
148 % Get impatience parameters
149 impatienceMu = sn.impatienceMu(i, r);
150 impatiencePhi = sn.impatiencePhi(i, r);
153 % sn.impatienceProc/impatiencePie are CELL arrays: they must be
154 % dereferenced with braces. Reading them with () yielded a 1x1
155 % cell, so abs()/sprintf() below errored out on a cell input and
156 % no phase-type patience could be exported at all.
157 impatienceProc = sn.impatienceProc{i, r};
158 impatiencePie = sn.impatiencePie{i, r};
159 phases = impatiencePhases;
160 PH = impatienceProc{1};
161 alpha = abs(impatiencePie);
164 subParNodeAlpha = simDoc.createElement(
'subParameter');
165 subParNodeAlpha.setAttribute(
'array',
'true');
166 subParNodeAlpha.setAttribute(
'classPath',
'java.lang.Object');
167 subParNodeAlpha.setAttribute(
'name',
'alpha');
168 subParNodeAlphaVec = simDoc.createElement(
'subParameter');
169 subParNodeAlphaVec.setAttribute(
'array',
'true');
170 subParNodeAlphaVec.setAttribute(
'classPath',
'java.lang.Object');
171 subParNodeAlphaVec.setAttribute(
'name',
'vector');
173 subParNodeAlphaElem = simDoc.createElement(
'subParameter');
174 subParNodeAlphaElem.setAttribute(
'classPath',
'java.lang.Double');
175 subParNodeAlphaElem.setAttribute(
'name',
'entry');
176 subParValue = simDoc.createElement(
'value');
177 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', alpha(k))));
178 subParNodeAlphaElem.appendChild(subParValue);
179 subParNodeAlphaVec.appendChild(subParNodeAlphaElem);
183 subParNodeT = simDoc.createElement(
'subParameter');
184 subParNodeT.setAttribute(
'array',
'true');
185 subParNodeT.setAttribute(
'classPath',
'java.lang.Object');
186 subParNodeT.setAttribute(
'name',
'T');
188 subParNodeTvec = simDoc.createElement(
'subParameter');
189 subParNodeTvec.setAttribute(
'array',
'true');
190 subParNodeTvec.setAttribute(
'classPath',
'java.lang.Object');
191 subParNodeTvec.setAttribute(
'name',
'vector');
193 subParNodeTElem = simDoc.createElement(
'subParameter');
194 subParNodeTElem.setAttribute(
'classPath',
'java.lang.Double');
195 subParNodeTElem.setAttribute(
'name',
'entry');
196 subParValue = simDoc.createElement(
'value');
198 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', -abs(PH(k,j)))));
200 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', abs(PH(k,j)))));
202 subParNodeTElem.appendChild(subParValue);
203 subParNodeTvec.appendChild(subParNodeTElem);
205 subParNodeT.appendChild(subParNodeTvec);
208 subParNodeAlpha.appendChild(subParNodeAlphaVec);
209 distrParNode.appendChild(subParNodeAlpha);
210 distrParNode.appendChild(subParNodeT);
214 subParNodeAlpha = simDoc.createElement(
'subParameter');
215 subParNodeAlpha.setAttribute(
'classPath',
'java.lang.Double');
216 subParNodeAlpha.setAttribute(
'name',
't');
217 subParValue = simDoc.createElement(
'value');
218 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', 1/impatienceMu(1))));
219 subParNodeAlpha.appendChild(subParValue);
220 distrParNode.appendChild(subParNodeAlpha);
223 subParNodeLambda = simDoc.createElement(
'subParameter');
224 subParNodeLambda.setAttribute(
'classPath',
'java.lang.Double');
225 subParNodeLambda.setAttribute(
'name',
'lambda');
226 subParValue = simDoc.createElement(
'value');
227 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', impatienceMu(1))));
228 subParNodeLambda.appendChild(subParValue);
229 distrParNode.appendChild(subParNodeLambda);
231 case ProcessType.ERLANG
232 phases = sn.impatiencePhases(i, r);
233 subParNodeAlpha = simDoc.createElement(
'subParameter');
234 subParNodeAlpha.setAttribute(
'classPath',
'java.lang.Double');
235 subParNodeAlpha.setAttribute(
'name',
'alpha');
236 subParValue = simDoc.createElement(
'value');
237 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', impatienceMu(1) * phases)));
238 subParNodeAlpha.appendChild(subParValue);
239 distrParNode.appendChild(subParNodeAlpha);
240 subParNodeR = simDoc.createElement(
'subParameter');
241 subParNodeR.setAttribute(
'classPath',
'java.lang.Long');
242 subParNodeR.setAttribute(
'name',
'r');
243 subParValue = simDoc.createElement(
'value');
244 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%d', phases)));
245 subParNodeR.appendChild(subParValue);
246 distrParNode.appendChild(subParNodeR);
248 case ProcessType.HYPEREXP
249 % 2-phase only: HyperExpPar carries exactly (p,lambda1,lambda2).
250 % Braces, not (): these sn fields are cell arrays.
251 impatienceProc = sn.impatienceProc{i, r};
252 impatiencePie = sn.impatiencePie{i, r};
253 subParNodeP = simDoc.createElement(
'subParameter');
254 subParNodeP.setAttribute(
'classPath',
'java.lang.Double');
255 subParNodeP.setAttribute(
'name',
'p');
256 subParValue = simDoc.createElement(
'value');
257 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', impatiencePie(1))));
258 subParNodeP.appendChild(subParValue);
259 distrParNode.appendChild(subParNodeP);
260 subParNodeLambda1 = simDoc.createElement(
'subParameter');
261 subParNodeLambda1.setAttribute(
'classPath',
'java.lang.Double');
262 subParNodeLambda1.setAttribute(
'name',
'lambda1');
263 subParValue = simDoc.createElement(
'value');
264 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', -impatienceProc{1}(1,1))));
265 subParNodeLambda1.appendChild(subParValue);
266 distrParNode.appendChild(subParNodeLambda1);
267 subParNodeLambda2 = simDoc.createElement(
'subParameter');
268 subParNodeLambda2.setAttribute(
'classPath',
'java.lang.Double');
269 subParNodeLambda2.setAttribute(
'name',
'lambda2');
270 subParValue = simDoc.createElement(
'value');
271 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', -impatienceProc{1}(2,2))));
272 subParNodeLambda2.appendChild(subParValue);
273 distrParNode.appendChild(subParNodeLambda2);
275 case ProcessType.GAMMA
276 scv = impatiencePhi(1);
277 subParNodeAlpha = simDoc.createElement(
'subParameter');
278 subParNodeAlpha.setAttribute(
'classPath',
'java.lang.Double');
279 subParNodeAlpha.setAttribute(
'name',
'alpha');
280 subParValue = simDoc.createElement(
'value');
281 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', 1/scv)));
282 subParNodeAlpha.appendChild(subParValue);
283 distrParNode.appendChild(subParNodeAlpha);
284 subParNodeBeta = simDoc.createElement(
'subParameter');
285 subParNodeBeta.setAttribute(
'classPath',
'java.lang.Double');
286 subParNodeBeta.setAttribute(
'name',
'beta');
287 subParValue = simDoc.createElement(
'value');
288 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', scv/impatienceMu(1))));
289 subParNodeBeta.appendChild(subParValue);
290 distrParNode.appendChild(subParNodeBeta);
292 case ProcessType.PARETO
293 scv = impatiencePhi(1);
294 shape = sqrt(1 + 1/scv) + 1;
295 scale = 1/impatienceMu(1) * (shape - 1) / shape;
296 subParNodeAlpha = simDoc.createElement(
'subParameter');
297 subParNodeAlpha.setAttribute(
'classPath',
'java.lang.Double');
298 subParNodeAlpha.setAttribute(
'name',
'alpha');
299 subParValue = simDoc.createElement(
'value');
300 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', shape)));
301 subParNodeAlpha.appendChild(subParValue);
302 distrParNode.appendChild(subParNodeAlpha);
303 subParNodeK = simDoc.createElement(
'subParameter');
304 subParNodeK.setAttribute(
'classPath',
'java.lang.Double');
305 subParNodeK.setAttribute(
'name',
'k');
306 subParValue = simDoc.createElement(
'value');
307 subParValue.appendChild(simDoc.createTextNode(sprintf(
'%.12f', scale)));
308 subParNodeK.appendChild(subParValue);
309 distrParNode.appendChild(subParNodeK);
311 case ProcessType.WEIBULL
312 scv = impatiencePhi(1);
314 rval = c^(-1.086); % Justus approximation (1976)
315 alpha = 1/impatienceMu(1) / gamma(1+1/rval);
316 subParNodeAlpha = simDoc.createElement('subParameter');
317 subParNodeAlpha.setAttribute('classPath', 'java.lang.Double');
318 subParNodeAlpha.setAttribute('name', 'alpha');
319 subParValue = simDoc.createElement('value');
320 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', alpha)));
321 subParNodeAlpha.appendChild(subParValue);
322 distrParNode.appendChild(subParNodeAlpha);
323 subParNodeR = simDoc.createElement('subParameter');
324 subParNodeR.setAttribute('classPath', 'java.lang.Double');
325 subParNodeR.setAttribute('name', 'r');
326 subParValue = simDoc.createElement('value');
327 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', rval)));
328 subParNodeR.appendChild(subParValue);
329 distrParNode.appendChild(subParNodeR);
331 case ProcessType.LOGNORMAL
332 scv = impatiencePhi(1);
334 mu = log(1/impatienceMu(1) / sqrt(c*c + 1));
335 sigma = sqrt(log(c*c + 1));
336 subParNodeMu = simDoc.createElement('subParameter');
337 subParNodeMu.setAttribute('classPath', 'java.lang.Double');
338 subParNodeMu.setAttribute('name', 'mu');
339 subParValue = simDoc.createElement('value');
340 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', mu)));
341 subParNodeMu.appendChild(subParValue);
342 distrParNode.appendChild(subParNodeMu);
343 subParNodeSigma = simDoc.createElement('subParameter');
344 subParNodeSigma.setAttribute('classPath', 'java.lang.Double');
345 subParNodeSigma.setAttribute('name', 'sigma');
346 subParValue = simDoc.createElement('value');
347 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', sigma)));
348 subParNodeSigma.appendChild(subParValue);
349 distrParNode.appendChild(subParNodeSigma);
351 case ProcessType.UNIFORM
352 mean = 1/impatienceMu(1);
353 % For uniform [a,b]: mean = (a+b)/2, use a=0 for simplicity
355 subParNodeMin = simDoc.createElement('subParameter');
356 subParNodeMin.setAttribute('classPath', 'java.lang.Double');
357 subParNodeMin.setAttribute('name', 'min');
358 subParValue = simDoc.createElement('value');
359 subParValue.appendChild(simDoc.createTextNode('0.0'));
360 subParNodeMin.appendChild(subParValue);
361 distrParNode.appendChild(subParNodeMin);
362 subParNodeMax = simDoc.createElement('subParameter');
363 subParNodeMax.setAttribute('classPath', 'java.lang.Double');
364 subParNodeMax.setAttribute('name', 'max');
365 subParValue = simDoc.createElement('value');
366 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', b)));
367 subParNodeMax.appendChild(subParValue);
368 distrParNode.appendChild(subParNodeMax);
373 impatienceStrategyNode.appendChild(distrParNode);
376 impatienceNode.appendChild(impatienceStrategyNode);
379section.appendChild(impatienceNode);
382function [simDoc, balkingNode] = saveBalkingStrategy(self, simDoc, balkingNode, thresholds, nservers) %
#ok<INUSL>
383% Populate a JMT <Balking> impatience element from LINE queue-length balking
384% thresholds. LINE stores a list of {minJobs, maxJobs, probability} closed
385% intervals; JMT
's Balking reads a LoadDependentStrategy whose LDParameter
386% ranges are {from -> probability}, selecting the last range with from <=
387% queueLength (default 0). We translate the closed intervals into from-based
388% breakpoints, inserting explicit 0-probability breakpoints at gaps so that
389% queue lengths outside any interval do not inherit a neighbour's probability.
391% Queue-length convention: LINE evaluates balking against
the TOTAL station
392% population (in-service + waiting), as
do the CTMC/SSA/LDES
solvers. JMT
's
393% Balking evaluates against the number WAITING only (the buffer occupancy).
394% When the servers are busy (the regime where balking is meaningful) the two
395% differ by exactly the server count S, so we shift every `from` down by S:
396% JMT waiting w maps to LINE total n = w + S. This makes the exported JMT
397% model reproduce LINE's balking numerically.
399% Build sorted (from, prob) breakpoints
400lo = zeros(numel(thresholds),1);
401hi = zeros(numel(thresholds),1);
402pr = zeros(numel(thresholds),1);
403for ti = 1:numel(thresholds)
409[lo, order] = sort(lo);
413if isempty(nservers) || ~isfinite(nservers) || nservers < 1
421 froms(end+1) = max(0, lo(ti) - S); %#ok<AGROW>
422 probs(end+1) = pr(ti); %#ok<AGROW>
430 froms(end+1) = max(0, hi(ti)+1 - S); %#ok<AGROW>
431 probs(end+1) = 0.0; %#ok<AGROW>
436% LoadDependentStrategy wrapper (Parameter #1 of Balking)
437ldStrategyNode = simDoc.createElement(
'subParameter');
438ldStrategyNode.setAttribute(
'classPath',
'jmt.engine.NetStrategies.ServiceStrategies.LoadDependentStrategy');
439ldStrategyNode.setAttribute(
'name',
'LoadDependentStrategy');
441ldArrayNode = simDoc.createElement(
'subParameter');
442ldArrayNode.setAttribute(
'array',
'true');
443ldArrayNode.setAttribute(
'classPath',
'jmt.engine.NetStrategies.ServiceStrategies.LDParameter');
444ldArrayNode.setAttribute(
'name',
'LDParameter');
446for ti = 1:numel(froms)
447 rangeNode = simDoc.createElement(
'subParameter');
448 rangeNode.setAttribute(
'classPath',
'jmt.engine.NetStrategies.ServiceStrategies.LDParameter');
449 rangeNode.setAttribute(
'name',
'LDParameter');
452 fromNode = simDoc.createElement(
'subParameter');
453 fromNode.setAttribute(
'classPath',
'java.lang.Integer');
454 fromNode.setAttribute(
'name',
'from');
455 v = simDoc.createElement(
'value');
456 v.appendChild(simDoc.createTextNode(sprintf(
'%d', froms(ti))));
457 fromNode.appendChild(v);
458 rangeNode.appendChild(fromNode);
460 % dummy distribution (only
the function/probability
is read
for balking)
461 distrNode = simDoc.createElement(
'subParameter');
462 distrNode.setAttribute(
'classPath',
'jmt.engine.random.Exponential');
463 distrNode.setAttribute(
'name',
'Exponential');
464 rangeNode.appendChild(distrNode);
466 distrParNode = simDoc.createElement(
'subParameter');
467 distrParNode.setAttribute(
'classPath',
'jmt.engine.random.ExponentialPar');
468 distrParNode.setAttribute(
'name',
'distrPar');
469 lambdaNode = simDoc.createElement(
'subParameter');
470 lambdaNode.setAttribute(
'classPath',
'java.lang.Double');
471 lambdaNode.setAttribute(
'name',
'lambda');
472 v = simDoc.createElement(
'value');
473 v.appendChild(simDoc.createTextNode(
'1.0'));
474 lambdaNode.appendChild(v);
475 distrParNode.appendChild(lambdaNode);
476 rangeNode.appendChild(distrParNode);
478 % function (String) = balking probability
for this range
479 funcNode = simDoc.createElement(
'subParameter');
480 funcNode.setAttribute(
'classPath',
'java.lang.String');
481 funcNode.setAttribute(
'name',
'function');
482 v = simDoc.createElement(
'value');
483 v.appendChild(simDoc.createTextNode(sprintf(
'%.12f', probs(ti))));
484 funcNode.appendChild(v);
485 rangeNode.appendChild(funcNode);
487 ldArrayNode.appendChild(rangeNode);
490ldStrategyNode.appendChild(ldArrayNode);
491balkingNode.appendChild(ldStrategyNode);
493% priorityActivated flag (Parameter #2 of Balking)
494prioNode = simDoc.createElement(
'subParameter');
495prioNode.setAttribute(
'classPath',
'java.lang.Boolean');
496prioNode.setAttribute(
'name',
'priorityActivated');
497v = simDoc.createElement(
'value');
498v.appendChild(simDoc.createTextNode(
'false'));
499prioNode.appendChild(v);
500balkingNode.appendChild(prioNode);