LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
saveRetrialDistributions.m
1function [simDoc, section] = saveRetrialDistributions(self, simDoc, section, ind)
2% [SIMDOC, SECTION] = SAVERETRIALDISTRIBUTIONS(SIMDOC, SECTION, IND)
3%
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.
7%
8% Copyright (c) 2012-2026, Imperial College London
9% All rights reserved.
10
11retrialNode = simDoc.createElement('parameter');
12retrialNode.setAttribute('array', 'true');
13retrialNode.setAttribute('classPath', 'jmt.engine.NetStrategies.ServiceStrategy');
14retrialNode.setAttribute('name', 'retrialDistributions');
15
16sn = self.getStruct;
17numOfClasses = sn.nclasses;
18exportClasses = self.getExportableClasses();
19
20% Get the actual Queue node to access retrialDelays
21nodes = self.model.getNodes();
22currentNode = nodes{ind};
23
24for r=1:numOfClasses
25 if ~exportClasses(r)
26 continue;
27 end
28
29 refClassNode = simDoc.createElement('refClass');
30 refClassNode.appendChild(simDoc.createTextNode(sn.classnames{r}));
31 retrialNode.appendChild(refClassNode);
32
33 serviceTimeStrategyNode = simDoc.createElement('subParameter');
34 serviceTimeStrategyNode.setAttribute('classPath', 'jmt.engine.NetStrategies.ServiceStrategies.ServiceTimeStrategy');
35 serviceTimeStrategyNode.setAttribute('name', 'ServiceTimeStrategy');
36
37 % Check if retrial delay is defined for this class
38 hasRetrial = false;
39 retrialDist = [];
40 if isa(currentNode, 'Queue') && ~isempty(currentNode.retrialDelays)
41 if r <= size(currentNode.retrialDelays, 2) && ~isempty(currentNode.retrialDelays{1, r})
42 hasRetrial = true;
43 retrialDist = currentNode.retrialDelays{1, r};
44 end
45 end
46
47 if ~hasRetrial
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);
53
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);
65 else
66 % see _kb/06-solver-catalog.md (Wrappers: JMT phase-type distribution export is shared)
67 emitPhaseType = isa(retrialDist, 'PH') || isa(retrialDist, 'APH') || ...
68 isa(retrialDist, 'Coxian') || ...
69 (isa(retrialDist, 'HyperExp') && retrialDist.getNumberOfPhases() > 2);
70
71 if emitPhaseType
72 javaClass = 'jmt.engine.random.PhaseTypeDistr';
73 javaParClass = 'jmt.engine.random.PhaseTypePar';
74 distName = 'Phase-Type';
75 elseif isa(retrialDist, 'Exp')
76 javaClass = 'jmt.engine.random.Exponential';
77 javaParClass = 'jmt.engine.random.ExponentialPar';
78 distName = 'Exponential';
79 elseif isa(retrialDist, 'Erlang')
80 javaClass = 'jmt.engine.random.Erlang';
81 javaParClass = 'jmt.engine.random.ErlangPar';
82 distName = 'Erlang';
83 elseif isa(retrialDist, 'HyperExp')
84 javaClass = 'jmt.engine.random.HyperExp';
85 javaParClass = 'jmt.engine.random.HyperExpPar';
86 distName = 'Hyperexponential';
87 elseif isa(retrialDist, 'Det')
88 javaClass = 'jmt.engine.random.DeterministicDistr';
89 javaParClass = 'jmt.engine.random.DeterministicDistrPar';
90 distName = 'Deterministic';
91 elseif isa(retrialDist, 'Gamma')
92 javaClass = 'jmt.engine.random.GammaDistr';
93 javaParClass = 'jmt.engine.random.GammaDistrPar';
94 distName = 'Gamma';
95 elseif isa(retrialDist, 'Uniform')
96 javaClass = 'jmt.engine.random.Uniform';
97 javaParClass = 'jmt.engine.random.UniformPar';
98 distName = 'Uniform';
99 else
100 % Fallback: treat as exponential with the distribution's rate
101 javaClass = 'jmt.engine.random.Exponential';
102 javaParClass = 'jmt.engine.random.ExponentialPar';
103 distName = 'Exponential';
104 end
105
106 distributionNode = simDoc.createElement('subParameter');
107 distributionNode.setAttribute('classPath', javaClass);
108 distributionNode.setAttribute('name', distName);
109 serviceTimeStrategyNode.appendChild(distributionNode);
110
111 distrParNode = simDoc.createElement('subParameter');
112 distrParNode.setAttribute('classPath', javaParClass);
113 distrParNode.setAttribute('name', 'distrPar');
114
115 if emitPhaseType
116 [alpha, T, phases] = phaseTypeRepres(retrialDist);
117
118 % Alpha vector
119 subParNodeAlpha = simDoc.createElement('subParameter');
120 subParNodeAlpha.setAttribute('array', 'true');
121 subParNodeAlpha.setAttribute('classPath', 'java.lang.Object');
122 subParNodeAlpha.setAttribute('name', 'alpha');
123 subParNodeAlphaVec = simDoc.createElement('subParameter');
124 subParNodeAlphaVec.setAttribute('array', 'true');
125 subParNodeAlphaVec.setAttribute('classPath', 'java.lang.Object');
126 subParNodeAlphaVec.setAttribute('name', 'vector');
127 for k=1:phases
128 subParNodeAlphaElem = simDoc.createElement('subParameter');
129 subParNodeAlphaElem.setAttribute('classPath', 'java.lang.Double');
130 subParNodeAlphaElem.setAttribute('name', 'entry');
131 subParValue = simDoc.createElement('value');
132 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', alpha(k))));
133 subParNodeAlphaElem.appendChild(subParValue);
134 subParNodeAlphaVec.appendChild(subParNodeAlphaElem);
135 end
136
137 % T matrix
138 subParNodeT = simDoc.createElement('subParameter');
139 subParNodeT.setAttribute('array', 'true');
140 subParNodeT.setAttribute('classPath', 'java.lang.Object');
141 subParNodeT.setAttribute('name', 'T');
142 for k=1:phases
143 subParNodeTvec = simDoc.createElement('subParameter');
144 subParNodeTvec.setAttribute('array', 'true');
145 subParNodeTvec.setAttribute('classPath', 'java.lang.Object');
146 subParNodeTvec.setAttribute('name', 'vector');
147 for j=1:phases
148 subParNodeTElem = simDoc.createElement('subParameter');
149 subParNodeTElem.setAttribute('classPath', 'java.lang.Double');
150 subParNodeTElem.setAttribute('name', 'entry');
151 subParValue = simDoc.createElement('value');
152 if k==j
153 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', -abs(T(k,j)))));
154 else
155 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', abs(T(k,j)))));
156 end
157 subParNodeTElem.appendChild(subParValue);
158 subParNodeTvec.appendChild(subParNodeTElem);
159 end
160 subParNodeT.appendChild(subParNodeTvec);
161 end
162
163 subParNodeAlpha.appendChild(subParNodeAlphaVec);
164 distrParNode.appendChild(subParNodeAlpha);
165 distrParNode.appendChild(subParNodeT);
166
167 elseif isa(retrialDist, 'HyperExp')
168 % 2-phase only: HyperExpPar carries exactly (p, lambda1, lambda2).
169 % Emitting a bare `lambda` here, as the generic fallback below did,
170 % silently degraded the retrial delay to an exponential.
171 [alpha, T] = phaseTypeRepres(retrialDist);
172 subParNodeP = simDoc.createElement('subParameter');
173 subParNodeP.setAttribute('classPath', 'java.lang.Double');
174 subParNodeP.setAttribute('name', 'p');
175 subParValue = simDoc.createElement('value');
176 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', alpha(1))));
177 subParNodeP.appendChild(subParValue);
178 distrParNode.appendChild(subParNodeP);
179
180 subParNodeLambda1 = simDoc.createElement('subParameter');
181 subParNodeLambda1.setAttribute('classPath', 'java.lang.Double');
182 subParNodeLambda1.setAttribute('name', 'lambda1');
183 subParValue = simDoc.createElement('value');
184 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', -T(1,1))));
185 subParNodeLambda1.appendChild(subParValue);
186 distrParNode.appendChild(subParNodeLambda1);
187
188 subParNodeLambda2 = simDoc.createElement('subParameter');
189 subParNodeLambda2.setAttribute('classPath', 'java.lang.Double');
190 subParNodeLambda2.setAttribute('name', 'lambda2');
191 subParValue = simDoc.createElement('value');
192 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', -T(2,2))));
193 subParNodeLambda2.appendChild(subParValue);
194 distrParNode.appendChild(subParNodeLambda2);
195
196 elseif isa(retrialDist, 'Exp')
197 subParNodeLambda = simDoc.createElement('subParameter');
198 subParNodeLambda.setAttribute('classPath', 'java.lang.Double');
199 subParNodeLambda.setAttribute('name', 'lambda');
200 subParValue = simDoc.createElement('value');
201 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', retrialDist.getRate())));
202 subParNodeLambda.appendChild(subParValue);
203 distrParNode.appendChild(subParNodeLambda);
204
205 elseif isa(retrialDist, 'Det')
206 subParNodeT = simDoc.createElement('subParameter');
207 subParNodeT.setAttribute('classPath', 'java.lang.Double');
208 subParNodeT.setAttribute('name', 't');
209 subParValue = simDoc.createElement('value');
210 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', retrialDist.getMean())));
211 subParNodeT.appendChild(subParValue);
212 distrParNode.appendChild(subParNodeT);
213
214 elseif isa(retrialDist, 'Erlang')
215 subParNodeAlpha = simDoc.createElement('subParameter');
216 subParNodeAlpha.setAttribute('classPath', 'java.lang.Double');
217 subParNodeAlpha.setAttribute('name', 'alpha');
218 subParValue = simDoc.createElement('value');
219 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', retrialDist.getRate())));
220 subParNodeAlpha.appendChild(subParValue);
221 distrParNode.appendChild(subParNodeAlpha);
222
223 subParNodeR = simDoc.createElement('subParameter');
224 subParNodeR.setAttribute('classPath', 'java.lang.Long');
225 subParNodeR.setAttribute('name', 'r');
226 subParValue = simDoc.createElement('value');
227 subParValue.appendChild(simDoc.createTextNode(sprintf('%d', retrialDist.getNumberOfPhases())));
228 subParNodeR.appendChild(subParValue);
229 distrParNode.appendChild(subParNodeR);
230
231 else
232 % Generic fallback: use rate as exponential
233 subParNodeLambda = simDoc.createElement('subParameter');
234 subParNodeLambda.setAttribute('classPath', 'java.lang.Double');
235 subParNodeLambda.setAttribute('name', 'lambda');
236 subParValue = simDoc.createElement('value');
237 subParValue.appendChild(simDoc.createTextNode(sprintf('%.12f', 1/retrialDist.getMean())));
238 subParNodeLambda.appendChild(subParValue);
239 distrParNode.appendChild(subParNodeLambda);
240 end
241
242 serviceTimeStrategyNode.appendChild(distrParNode);
243 end
244
245 retrialNode.appendChild(serviceTimeStrategyNode);
246end
247
248section.appendChild(retrialNode);
249end
250
251function [alpha, T, phases] = phaseTypeRepres(dist)
252% [ALPHA, T, PHASES] = PHASETYPEREPRES(DIST)
253%
254% Extracts the (alpha, T) acyclic phase-type representation of a Markovian
255% distribution DIST from its own (D0,D1) process, so that the exported JMT
256% distribution is the SAME distribution rather than a moment-matched refit.
257% T = D0 and, since D1 = (-D0*e)*alpha for a PH renewal process, alpha is
258% recovered as the row of D1 rescaled by that phase's exit rate. This is the
259% same derivation used by MNetwork/refreshStruct.m for sn.impatiencePie.
260proc = dist.getProcess();
261if ~iscell(proc) || numel(proc) < 2
262 line_error(mfilename, sprintf(['Retrial distribution %s has no (D0,D1) ', ...
263 'representation, so it cannot be exported as a phase-type.'], class(dist)));
264end
265D0 = proc{1};
266D1 = proc{2};
267T = D0;
268phases = size(D0, 1);
269exitRates = -D0 * ones(phases, 1);
270idx = find(exitRates > 1e-10, 1);
271if ~isempty(idx)
272 alpha = abs(D1(idx, :) / exitRates(idx));
273else
274 alpha = ones(1, phases) / phases;
275end
276end
Definition fjtag.m:161