1function [simDoc, section] = saveRetrialDistributions(self, simDoc, section, ind)
2% [SIMDOC, SECTION] = SAVERETRIALDISTRIBUTIONS(SIMDOC, SECTION, IND)
4% Generates XML
for retrial delay distributions
for JMT Queue sections.
5% The retrial distribution tells JMT how
long a blocked customer waits
6% in
the orbit before retrying.
8% Copyright (c) 2012-2026, Imperial College London
11retrialNode = simDoc.createElement(
'parameter');
12retrialNode.setAttribute(
'array',
'true');
13retrialNode.setAttribute(
'classPath',
'jmt.engine.NetStrategies.ServiceStrategy');
14retrialNode.setAttribute(
'name',
'retrialDistributions');
17numOfClasses = sn.nclasses;
18exportClasses = self.getExportableClasses();
20% Get
the actual Queue node to access retrialDelays
21nodes = self.model.getNodes();
22currentNode =
nodes{ind};
29 refClassNode = simDoc.createElement(
'refClass');
30 refClassNode.appendChild(simDoc.createTextNode(sn.classnames{r}));
31 retrialNode.appendChild(refClassNode);
33 serviceTimeStrategyNode = simDoc.createElement(
'subParameter');
34 serviceTimeStrategyNode.setAttribute(
'classPath',
'jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy');
35 serviceTimeStrategyNode.setAttribute(
'name',
'ServiceTimeStrategy');
37 % Check
if retrial delay
is defined
for this class
40 if isa(currentNode,
'Queue') && ~isempty(currentNode.retrialDelays)
41 if r <= size(currentNode.retrialDelays, 2) && ~isempty(currentNode.retrialDelays{1, r})
43 retrialDist = currentNode.retrialDelays{1, r};
48 % Default: Exp(1) placeholder
49 distributionNode = simDoc.createElement('subParameter');
50 distributionNode.setAttribute('classPath', 'jmt.engine.random.Exponential');
51 distributionNode.setAttribute('name', 'Exponential');
52 serviceTimeStrategyNode.appendChild(distributionNode);
54 distrParNode = simDoc.createElement('subParameter');
55 distrParNode.setAttribute('classPath', 'jmt.engine.random.ExponentialPar');
56 distrParNode.setAttribute('name', 'distrPar');
57 subParNodeLambda = simDoc.createElement('subParameter');
58 subParNodeLambda.setAttribute('classPath', 'java.lang.Double');
59 subParNodeLambda.setAttribute('name', 'lambda');
60 subParValue = simDoc.createElement('value');
61 subParValue.appendChild(simDoc.createTextNode('1.000000000000'));
62 subParNodeLambda.appendChild(subParValue);
63 distrParNode.appendChild(subParNodeLambda);
64 serviceTimeStrategyNode.appendChild(distrParNode);
66 % Write
the actual retrial distribution
67 % Determine distribution type from
the distribution
object
69 % jmt.engine.random.HyperExpPar carries exactly (p, lambda1, lambda2)
70 % and so can only express a 2-phase hyper-exponential. An n>2 HyperExp,
71 % and any other phase-type retrial delay,
is exported as
the acyclic
72 % phase-type built from its OWN PH representation (alpha, T), i.e.
the
73 % same distribution rather than a moment-matched refit. This mirrors
74 % saveServiceStrategy.m and saveImpatience.m.
75 emitPhaseType = isa(retrialDist, 'PH') || isa(retrialDist, 'APH') || ...
76 isa(retrialDist, 'Coxian') || ...
77 (isa(retrialDist, 'HyperExp') && retrialDist.getNumberOfPhases() > 2);
80 javaClass = 'jmt.engine.random.PhaseTypeDistr';
81 javaParClass = 'jmt.engine.random.PhaseTypePar';
82 distName = 'Phase-Type';
83 elseif isa(retrialDist, 'Exp')
84 javaClass = 'jmt.engine.random.Exponential';
85 javaParClass = 'jmt.engine.random.ExponentialPar';
86 distName = 'Exponential';
87 elseif isa(retrialDist, 'Erlang')
88 javaClass = 'jmt.engine.random.Erlang';
89 javaParClass = 'jmt.engine.random.ErlangPar';
91 elseif isa(retrialDist, 'HyperExp')
92 javaClass = 'jmt.engine.random.HyperExp';
93 javaParClass = 'jmt.engine.random.HyperExpPar';
94 distName = 'Hyperexponential';
95 elseif isa(retrialDist, 'Det')
96 javaClass = 'jmt.engine.random.DeterministicDistr';
97 javaParClass = 'jmt.engine.random.DeterministicDistrPar';
98 distName = 'Deterministic';
99 elseif isa(retrialDist, 'Gamma')
100 javaClass = 'jmt.engine.random.GammaDistr';
101 javaParClass = 'jmt.engine.random.GammaDistrPar';
103 elseif isa(retrialDist, 'Uniform')
104 javaClass = 'jmt.engine.random.Uniform';
105 javaParClass = 'jmt.engine.random.UniformPar';
106 distName = 'Uniform';
108 % Fallback: treat as exponential with
the distribution's rate
109 javaClass = 'jmt.engine.random.Exponential';
110 javaParClass = 'jmt.engine.random.ExponentialPar';
111 distName = 'Exponential';
114 distributionNode = simDoc.createElement('subParameter');
115 distributionNode.setAttribute('classPath', javaClass);
116 distributionNode.setAttribute('name', distName);
117 serviceTimeStrategyNode.appendChild(distributionNode);
119 distrParNode = simDoc.createElement('subParameter');
120 distrParNode.setAttribute('classPath', javaParClass);
121 distrParNode.setAttribute('name', 'distrPar');
124 [alpha, T, phases] = phaseTypeRepres(retrialDist);
127 subParNodeAlpha = simDoc.createElement('subParameter');
128 subParNodeAlpha.setAttribute('array', 'true');
129 subParNodeAlpha.setAttribute('classPath', 'java.lang.Object');
130 subParNodeAlpha.setAttribute('name', 'alpha');
131 subParNodeAlphaVec = simDoc.createElement('subParameter');
132 subParNodeAlphaVec.setAttribute('array', 'true');
133 subParNodeAlphaVec.setAttribute('classPath', 'java.lang.Object');
134 subParNodeAlphaVec.setAttribute('name', 'vector');
136 subParNodeAlphaElem = simDoc.createElement('subParameter');
137 subParNodeAlphaElem.setAttribute('classPath', 'java.lang.Double');
138 subParNodeAlphaElem.setAttribute('name', 'entry');
139 subParValue = simDoc.createElement('value');
140 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', alpha(k))));
141 subParNodeAlphaElem.appendChild(subParValue);
142 subParNodeAlphaVec.appendChild(subParNodeAlphaElem);
146 subParNodeT = simDoc.createElement('subParameter');
147 subParNodeT.setAttribute('array', 'true');
148 subParNodeT.setAttribute('classPath', 'java.lang.Object');
149 subParNodeT.setAttribute('name', 'T');
151 subParNodeTvec = simDoc.createElement('subParameter');
152 subParNodeTvec.setAttribute('array', 'true');
153 subParNodeTvec.setAttribute('classPath', 'java.lang.Object');
154 subParNodeTvec.setAttribute('name', 'vector');
156 subParNodeTElem = simDoc.createElement('subParameter');
157 subParNodeTElem.setAttribute('classPath', 'java.lang.Double');
158 subParNodeTElem.setAttribute('name', 'entry');
159 subParValue = simDoc.createElement('value');
161 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', -abs(T(k,j)))));
163 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', abs(T(k,j)))));
165 subParNodeTElem.appendChild(subParValue);
166 subParNodeTvec.appendChild(subParNodeTElem);
168 subParNodeT.appendChild(subParNodeTvec);
171 subParNodeAlpha.appendChild(subParNodeAlphaVec);
172 distrParNode.appendChild(subParNodeAlpha);
173 distrParNode.appendChild(subParNodeT);
175 elseif isa(retrialDist, 'HyperExp')
176 % 2-phase only: HyperExpPar carries exactly (p, lambda1, lambda2).
177 % Emitting a bare `lambda` here, as
the generic fallback below did,
178 % silently degraded
the retrial delay to an exponential.
179 [alpha, T] = phaseTypeRepres(retrialDist);
180 subParNodeP = simDoc.createElement('subParameter');
181 subParNodeP.setAttribute('classPath', 'java.lang.Double');
182 subParNodeP.setAttribute('name', 'p');
183 subParValue = simDoc.createElement('value');
184 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', alpha(1))));
185 subParNodeP.appendChild(subParValue);
186 distrParNode.appendChild(subParNodeP);
188 subParNodeLambda1 = simDoc.createElement('subParameter');
189 subParNodeLambda1.setAttribute('classPath', 'java.lang.Double');
190 subParNodeLambda1.setAttribute('name', 'lambda1');
191 subParValue = simDoc.createElement('value');
192 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', -T(1,1))));
193 subParNodeLambda1.appendChild(subParValue);
194 distrParNode.appendChild(subParNodeLambda1);
196 subParNodeLambda2 = simDoc.createElement('subParameter');
197 subParNodeLambda2.setAttribute('classPath', 'java.lang.Double');
198 subParNodeLambda2.setAttribute('name', 'lambda2');
199 subParValue = simDoc.createElement('value');
200 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', -T(2,2))));
201 subParNodeLambda2.appendChild(subParValue);
202 distrParNode.appendChild(subParNodeLambda2);
204 elseif isa(retrialDist, 'Exp')
205 subParNodeLambda = simDoc.createElement('subParameter');
206 subParNodeLambda.setAttribute('classPath', 'java.lang.Double');
207 subParNodeLambda.setAttribute('name', 'lambda');
208 subParValue = simDoc.createElement('value');
209 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', retrialDist.getRate())));
210 subParNodeLambda.appendChild(subParValue);
211 distrParNode.appendChild(subParNodeLambda);
213 elseif isa(retrialDist, 'Det')
214 subParNodeT = simDoc.createElement('subParameter');
215 subParNodeT.setAttribute('classPath', 'java.lang.Double');
216 subParNodeT.setAttribute('name', 't');
217 subParValue = simDoc.createElement('value');
218 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', retrialDist.getMean())));
219 subParNodeT.appendChild(subParValue);
220 distrParNode.appendChild(subParNodeT);
222 elseif isa(retrialDist, 'Erlang')
223 subParNodeAlpha = simDoc.createElement('subParameter');
224 subParNodeAlpha.setAttribute('classPath', 'java.lang.Double');
225 subParNodeAlpha.setAttribute('name', 'alpha');
226 subParValue = simDoc.createElement('value');
227 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', retrialDist.getRate())));
228 subParNodeAlpha.appendChild(subParValue);
229 distrParNode.appendChild(subParNodeAlpha);
231 subParNodeR = simDoc.createElement('subParameter');
232 subParNodeR.setAttribute('classPath', 'java.lang.Long');
233 subParNodeR.setAttribute('name', 'r');
234 subParValue = simDoc.createElement('value');
235 subParValue.appendChild(simDoc.createTextNode(sprintf('%d', retrialDist.getNumberOfPhases())));
236 subParNodeR.appendChild(subParValue);
237 distrParNode.appendChild(subParNodeR);
240 % Generic fallback: use rate as exponential
241 subParNodeLambda = simDoc.createElement('subParameter');
242 subParNodeLambda.setAttribute('classPath', 'java.lang.Double');
243 subParNodeLambda.setAttribute('name', 'lambda');
244 subParValue = simDoc.createElement('value');
245 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', 1/retrialDist.getMean())));
246 subParNodeLambda.appendChild(subParValue);
247 distrParNode.appendChild(subParNodeLambda);
250 serviceTimeStrategyNode.appendChild(distrParNode);
253 retrialNode.appendChild(serviceTimeStrategyNode);
256section.appendChild(retrialNode);
259function [alpha, T, phases] = phaseTypeRepres(dist)
260% [ALPHA, T, PHASES] = PHASETYPEREPRES(DIST)
262% Extracts
the (alpha, T) acyclic phase-type representation of a Markovian
263% distribution DIST from its own (D0,D1) process, so that
the exported JMT
264% distribution
is the SAME distribution rather than a moment-matched refit.
265% T = D0 and, since D1 = (-D0*e)*alpha for a PH renewal process, alpha
is
266% recovered as
the row of D1 rescaled by that phase's exit rate. This
is the
267% same derivation used by MNetwork/refreshStruct.m for sn.impatiencePie.
268proc = dist.getProcess();
269if ~iscell(proc) || numel(proc) < 2
270 line_error(mfilename, sprintf(['Retrial distribution %s has no (D0,D1) ', ...
271 'representation, so it cannot be exported as a phase-type.'], class(dist)));
277exitRates = -D0 * ones(phases, 1);
278idx = find(exitRates > 1e-10, 1);
280 alpha = abs(D1(idx, :) / exitRates(idx));
282 alpha = ones(1, phases) / phases;