LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_sens_mom_validate.m
1function pfqn_sens_mom_validate()
2%{
3%{
4 % @file pfqn_sens_mom_validate.m
5 % @brief Validation harness for pfqn_sens_mom, the higher-moment analysis of
6 % Strelen (1990).
7 %
8 % Five references:
9 % A. brute-force enumeration of the closed product-form distribution,
10 % which is ground truth for m, Var, Cov, E[Q^2] and E[Q^3].
11 % B. pfqn_sens_mva. Summing its per-class covariance matrix at station
12 % i over all class pairs must give Var[Q_i], since
13 % Var[sum_r n(i,r)] = sum_{r,s} Cov[n(i,r),n(i,s)]. This ties the
14 % per-station-total moments of Strelen to the finer per-class
15 % moments of de Souza e Silva and Muntz.
16 % C. pfqn_mva for the base measures.
17 % D. Cov symmetry: x_j dm_i/dx_j and x_i dm_j/dx_i are computed by
18 % different derivative tracks and must agree.
19 % E. the published table of Example 3.4 of the reference (the
20 % Kobayashi central-server model), which pins the second
21 % derivative against numbers the author printed rather than
22 % against our own code.
23 %
24 % Reference: J. C. Strelen, "Moment Analysis for Closed Queuing Networks
25 % and its Linearizer", Performance Evaluation 11:127-142, 1990.
26%}
27%}
28rng(3);
29tolBrute = 1e-9;
30tolMva = 1e-10;
31tolTot = 1e-9;
32tolSym = 1e-9;
33tolPaper = 5e-5; % the paper prints 5 significant digits
34
35errBrute = 0; errMva = 0; errTot = 0; errSym = 0; errGrp = 0;
36nBrute = 0; nGrp = 0;
37
38for trial = 1:40
39 M = randi([1 3]);
40 R = randi([1 2]);
41 L = 0.2 + rand(M,R);
42 N = randi([0 3],1,R);
43 if ~any(N > 0)
44 N(1) = 2;
45 end
46 if mod(trial,2) == 0
47 Z = 0.3 + rand(1,R);
48 else
49 Z = zeros(1,R);
50 end
51 if mod(trial,3) == 0
52 mi = randi([1 3],1,M);
53 else
54 mi = ones(1,M);
55 end
56
57 mom = pfqn_sens_mom(L,N,Z,mi);
58
59 % ---- C. base measures ------------------------------------------
60 [XN,QN,UN,CN] = pfqn_mva(L,N,Z,mi);
61 errMva = max([errMva, relerr(mom.X,XN), relerr(mom.Q,QN), ...
62 relerr(mom.U,UN), relerr(mom.R,CN)]);
63
64 % ---- D. symmetry -----------------------------------------------
65 errSym = max(errSym, mom.CovAsym);
66
67 % ---- B. total variance against the per-class covariances -------
68 ref = pfqn_sens_mva(L,N,Z,mi);
69 errTot = max(errTot, relerr(mom.Var, ref.QTotVar));
70
71 % ---- A. brute force --------------------------------------------
72 if prod(N+1) <= 32 && M <= 3 && all(mi == 1)
73 [mb,Varb,Covb,M2b,M3b] = brute_totals(L,N,Z);
74 errBrute = max([errBrute, relerr(mom.m,mb), relerr(mom.Var,Varb), ...
75 relerr(mom.Cov,Covb), relerr(mom.M2,M2b), ...
76 relerr(mom.M3,M3b)]);
77 nBrute = nBrute + 1;
78
79 % ---- F. the per-class grouping ------------------------------
80 % groups = 1:R scales one class at a time, which is Akyildiz and
81 % Strelen's Theorem 1 with T = {r}. It must reproduce the per-class
82 % moments of the brute-force distribution, INCLUDING the third, and its
83 % second moments must equal pfqn_sens_mva's exactly.
84 momc = pfqn_sens_mom(L,N,Z,mi,1:R);
85 [mc,Varc,M2c,M3c] = brute_perclass(L,N,Z);
86 errGrp = max([errGrp, relerr(momc.m,mc), relerr(momc.Var,Varc), ...
87 relerr(momc.M2,M2c), relerr(momc.M3,M3c)]);
88 errGrp = max(errGrp, relerr(momc.Var, ref.QVar));
89 nGrp = nGrp + 1;
90
91 % ---- G. an intermediate grouping ----------------------------
92 % Two classes in one group must give the moments of their sum, and a
93 % grouping that puts every class in one group must reproduce the
94 % default (station totals). Both are checked against brute force.
95 if R == 2
96 momg = pfqn_sens_mom(L,N,Z,mi,[1 1]);
97 errGrp = max([errGrp, relerr(momg.m, mom.m), ...
98 relerr(momg.Var, mom.Var), relerr(momg.M3, mom.M3)]);
99 end
100 end
101end
102
103% =====================================================================
104% E. Example 3.4 of the reference: Kobayashi central-server model.
105% 12 type-1 queues, one class. Queues 1-9: x=0.0215, e=9.333;
106% queues 10,11: x=0.104, e=10.5; queue 12: x=0.019, e=105.
107% The paper prints E(Q_i) and sigma^2_{Q_i} for n=3, 2, 1.
108% =====================================================================
109xs = [repmat(0.0215,1,9), 0.104, 0.104, 0.019];
110es = [repmat(9.333,1,9), 10.5, 10.5, 105];
111Lk = (xs .* es)'; % demands, 12 x 1
112paperM = [0.07606, 0.05835, 0.03353; % rows: queues 1-9, 10-11, 12
113 0.53316, 0.36327, 0.18246;
114 1.24917, 0.74835, 0.33334];
115paperVar = [0.07893, 0.05873, 0.03240;
116 0.57689, 0.34341, 0.14917;
117 1.02546, 0.56250, 0.22222];
118errPaper = 0;
119for col = 1:3
120 nJobs = 4 - col; % col 1 -> n=3, col 2 -> n=2, col 3 -> n=1
121 mk = pfqn_sens_mom(Lk, nJobs, 0);
122 got = [mk.m(1), mk.m(10), mk.m(12)];
123 gotV = [mk.Var(1), mk.Var(10), mk.Var(12)];
124 errPaper = max([errPaper, relerr(got(:), paperM(:,col)), ...
125 relerr(gotV(:), paperVar(:,col))]);
126 % the nine identical queues must be identical, and so must 10 and 11
127 errPaper = max([errPaper, relerr(mk.m(1:9), repmat(mk.m(1),9,1)), ...
128 relerr(mk.m(10), mk.m(11))]);
129end
130
131fprintf('\n=== pfqn_sens_mom validation (max relative error) ===\n');
132fprintf(' A. brute-force product form (%d models) : %.3e (tol %.1e)\n', nBrute, errBrute, tolBrute);
133fprintf(' B. Var vs pfqn_sens_mva QTotVar : %.3e (tol %.1e)\n', errTot, tolTot);
134fprintf(' C. pfqn_mva base measures : %.3e (tol %.1e)\n', errMva, tolMva);
135fprintf(' D. Cov symmetry (raw, pre-symmetrize) : %.3e (tol %.1e)\n', errSym, tolSym);
136fprintf(' E. Strelen Example 3.4 published table : %.3e (tol %.1e)\n', errPaper, tolPaper);
137fprintf(' F. per-class grouping vs brute force + pfqn_sens_mva (%d models): %.3e (tol %.1e)\n', nGrp, errGrp, tolBrute);
138
139ok = errBrute <= tolBrute && errTot <= tolTot && errMva <= tolMva && ...
140 errSym <= tolSym && errPaper <= tolPaper && errGrp <= tolBrute;
141if ~ok
142 error('pfqn_sens_mom_validate:mismatch','one or more checks exceeded tolerance');
143end
144fprintf(' ALL CHECKS PASSED\n');
145end
146
147% =========================================================================
148function e = relerr(a,b)
149a = a(:); b = b(:);
150d = abs(a-b);
151scale = max(1, max(abs(a),abs(b)));
152e = max(d ./ scale);
153if isempty(e)
154 e = 0;
155end
156end
157
158% =========================================================================
159function [m,Var,Cov,M2,M3] = brute_totals(L,N,Z)
160% Moments of the per-station total queue lengths by enumerating the closed
161% product-form equilibrium distribution.
162[M,R] = size(L);
163states = enumerate_states(N,M);
164K = size(states,1);
165w = zeros(K,1);
166for k = 1:K
167 nir = reshape(states(k,:),R,M)';
168 lw = 0;
169 ok = true;
170 for i = 1:M
171 ni = sum(nir(i,:));
172 lw = lw + gammaln(ni+1);
173 for r = 1:R
174 if nir(i,r) > 0
175 if L(i,r) <= 0
176 ok = false; break;
177 end
178 lw = lw + nir(i,r)*log(L(i,r)) - gammaln(nir(i,r)+1);
179 end
180 end
181 if ~ok, break; end
182 end
183 if ok
184 for r = 1:R
185 n0r = N(r) - sum(nir(:,r));
186 if n0r > 0
187 if Z(r) <= 0
188 ok = false; break;
189 end
190 lw = lw + n0r*log(Z(r)) - gammaln(n0r+1);
191 end
192 end
193 end
194 if ok
195 w(k) = exp(lw);
196 end
197end
198w = w / sum(w);
199
200tot = zeros(K,M);
201for k = 1:K
202 nir = reshape(states(k,:),R,M)';
203 tot(k,:) = sum(nir,2)';
204end
205m = (w' * tot)';
206M2 = (w' * (tot.^2))';
207M3 = (w' * (tot.^3))';
208Var = M2 - m.^2;
209Cov = zeros(M,M);
210for i = 1:M
211 for j = 1:M
212 Cov(i,j) = sum(w .* tot(:,i) .* tot(:,j)) - m(i)*m(j);
213 end
214end
215end
216
217% =========================================================================
218function [m,Var,M2,M3] = brute_perclass(L,N,Z)
219% Per-class moments of n(i,r) by enumerating the closed product form.
220[M,R] = size(L);
221states = enumerate_states(N,M);
222K = size(states,1);
223w = zeros(K,1);
224for k = 1:K
225 nir = reshape(states(k,:),R,M)';
226 lw = 0;
227 ok = true;
228 for i = 1:M
229 ni = sum(nir(i,:));
230 lw = lw + gammaln(ni+1);
231 for r = 1:R
232 if nir(i,r) > 0
233 if L(i,r) <= 0
234 ok = false; break;
235 end
236 lw = lw + nir(i,r)*log(L(i,r)) - gammaln(nir(i,r)+1);
237 end
238 end
239 if ~ok, break; end
240 end
241 if ok
242 for r = 1:R
243 n0r = N(r) - sum(nir(:,r));
244 if n0r > 0
245 if Z(r) <= 0
246 ok = false; break;
247 end
248 lw = lw + n0r*log(Z(r)) - gammaln(n0r+1);
249 end
250 end
251 end
252 if ok
253 w(k) = exp(lw);
254 end
255end
256w = w / sum(w);
257m = zeros(M,R); M2 = zeros(M,R); M3 = zeros(M,R);
258for k = 1:K
259 nir = reshape(states(k,:),R,M)';
260 m = m + w(k)*nir;
261 M2 = M2 + w(k)*nir.^2;
262 M3 = M3 + w(k)*nir.^3;
263end
264Var = M2 - m.^2;
265end
266
267% =========================================================================
268function states = enumerate_states(N,M)
269R = numel(N);
270per = cell(1,R);
271for r = 1:R
272 per{r} = compositions_leq(N(r),M);
273end
274states = zeros(0,R*M);
275idx = ones(1,R);
276while true
277 row = zeros(M,R);
278 for r = 1:R
279 row(:,r) = per{r}(idx(r),:)';
280 end
281 states(end+1,:) = reshape(row',1,[]); %#ok<AGROW>
282 r = R;
283 while r >= 1
284 idx(r) = idx(r) + 1;
285 if idx(r) <= size(per{r},1)
286 break;
287 end
288 idx(r) = 1;
289 r = r - 1;
290 end
291 if r == 0
292 break;
293 end
294end
295end
296
297% =========================================================================
298function C = compositions_leq(n,M)
299if M == 1
300 C = (0:n)';
301 return;
302end
303C = zeros(0,M);
304for first = 0:n
305 sub = compositions_leq(n-first,M-1);
306 C = [C; [repmat(first,size(sub,1),1), sub]]; %#ok<AGROW>
307end
308end
Definition Station.m:245