LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_sens_mvaldmx_validate.m
1function pfqn_sens_mvaldmx_validate()
2%{
3%{
4 % @file pfqn_sens_mvaldmx_validate.m
5 % @brief Validation harness for pfqn_sens_mvaldmx, the mixed load-dependent
6 % moment analysis of Akyildiz and Strelen (1991).
7 %
8 % Five independent references, chosen so that every channel of the
9 % derivation is exercised by something that does not share its code:
10 %
11 % A. pfqn_mvaldmx for the base measures X, Q, U, R. The primal must be
12 % reproduced entry by entry, otherwise the derivative is of the
13 % wrong function.
14 % B. central finite differences of pfqn_mvaldmx with respect to the
15 % demand-scaling parameter y(j,s). This checks the differentiated
16 % recursion itself, including the load-dependent channel
17 % dEC/dLo and the open-class channel dLo/dy of eq. (21), but does
18 % not check the identity Cov = d nbar / dy.
19 % C. pfqn_sens_mva in the closed load-independent limit. This checks
20 % the identity against the independently validated de Souza e Silva
21 % and Muntz recursion.
22 % D. brute-force enumeration of the product-form equilibrium
23 % distribution. Closed load-dependent models are enumerated exactly;
24 % mixed models are enumerated with the open populations truncated,
25 % which converges geometrically and is therefore checked at a
26 % looser tolerance. This is the only check that closes the loop on
27 % the identity in the mixed load-dependent case.
28 % E. symmetry of QCovFull. Cov[n(i,r),n(j,s)] and Cov[n(j,s),n(i,r)]
29 % are computed by differentiating two different classes'
30 % equations, so their agreement is a nontrivial structural check.
31%}
32%}
33rng(1);
34tolMva = 1e-12;
35tolFd = 1e-6;
36tolMom = 1e-9;
37tolBrt = 1e-8; % exact enumeration, closed load-dependent
38tolBrtT = 5e-5; % truncated enumeration, mixed
39tolSym = 1e-8;
40
41errMva = 0; errFd = 0; errMom = 0; errBrt = 0; errBrtT = 0; errSym = 0;
42nFd = 0; nMom = 0; nBrt = 0; nBrtT = 0;
43
44% =====================================================================
45% A/B/E on random mixed load-dependent models
46% =====================================================================
47for trial = 1:12
48 M = randi([1 2]);
49 Ropen = randi([0 1]);
50 D = 0.2 + 0.6*rand(M,1+Ropen);
51 R = 1 + Ropen;
52 N = zeros(1,R);
53 N(1) = randi([1 3]); % closed class
54 lambda = zeros(1,R);
55 if Ropen == 1
56 N(2) = Inf; % open class
57 lambda(2) = 0.05 + 0.15*rand; % keep the station loads modest
58 end
59 Z = zeros(1,R);
60 Z(1) = 0.5*rand;
61 NCtot = sum(N(isfinite(N)));
62 % limited load dependence: rates grow up to level b then saturate
63 b = randi([1 3]);
64 mu = zeros(M,max(NCtot,1));
65 for i = 1:M
66 for n = 1:size(mu,2)
67 mu(i,n) = min(n,b) * (0.8 + 0.4*rand);
68 end
69 end
70 % keep the geometric tail of the limited load dependence stable
71 Lo = zeros(M,1);
72 for i = 1:M
73 Lo(i) = lambda*D(i,:)';
74 end
75 if any(Lo ./ mu(:,end) > 0.6)
76 continue;
77 end
78
79 mom = pfqn_sens_mvaldmx(lambda,D,N,Z,mu,ones(M,1));
80
81 % ---- A. base measures ------------------------------------------
82 [XN,QN,UN,CN] = pfqn_mvaldmx(lambda,D,N,Z,mu,ones(M,1));
83 errMva = max([errMva, relerr(mom.X,XN), relerr(mom.Q,QN), ...
84 relerr(mom.U,UN), relerr(mom.R,CN)]);
85
86 % ---- E. symmetry -----------------------------------------------
87 errSym = max(errSym, mom.QCovAsym);
88
89 % ---- B. finite differences -------------------------------------
90 h = 1e-6;
91 for j = 1:M
92 for s = 1:R
93 if D(j,s) <= 0, continue; end
94 Dp = D; Dp(j,s) = D(j,s)*(1+h);
95 Dm = D; Dm(j,s) = D(j,s)*(1-h);
96 [~,QNp] = pfqn_mvaldmx(lambda,Dp,N,Z,mu,ones(M,1));
97 [~,QNm] = pfqn_mvaldmx(lambda,Dm,N,Z,mu,ones(M,1));
98 fd = (QNp - QNm) / (2*h); % d nbar / dy at y=1
99 an = zeros(M,R);
100 for i = 1:M
101 for r = 1:R
102 an(i,r) = mom.QCovFull(i,r,j,s);
103 end
104 end
105 errFd = max(errFd, relerr(an,fd));
106 nFd = nFd + 1;
107 end
108 end
109end
110
111% =====================================================================
112% C. closed load-independent limit against pfqn_sens_mva
113% =====================================================================
114for trial = 1:12
115 M = randi([1 3]);
116 R = randi([1 2]);
117 D = 0.2 + rand(M,R);
118 N = randi([1 3],1,R);
119 Z = 0.4*rand(1,R);
120 lambda = zeros(1,R);
121 mu = ones(M,sum(N));
122 mom = pfqn_sens_mvaldmx(lambda,D,N,Z,mu,ones(M,1));
123 ref = pfqn_sens_mva(D,N,Z);
124 errMom = max([errMom, relerr(mom.Q,ref.Q), relerr(mom.QCov,ref.QCov), ...
125 relerr(mom.QVar,ref.QVar), relerr(mom.QTotVar,ref.QTotVar)]);
126 nMom = nMom + 1;
127end
128
129% =====================================================================
130% D. brute-force product form
131% =====================================================================
132% D1. closed load-dependent, exact enumeration
133for trial = 1:10
134 M = 2; R = 1;
135 D = 0.3 + 0.5*rand(M,R);
136 N = randi([2 4],1,R);
137 Z = 0.3*rand(1,R);
138 lambda = 0;
139 b = randi([2 3]);
140 mu = zeros(M,sum(N));
141 for i = 1:M
142 for n = 1:size(mu,2)
143 mu(i,n) = min(n,b) * (0.8 + 0.4*rand);
144 end
145 end
146 mom = pfqn_sens_mvaldmx(lambda,D,N,Z,mu,ones(M,1));
147 [Qb,QCovb] = brute_ldmx(lambda,D,N,Z,mu,0);
148 errBrt = max([errBrt, relerr(mom.Q,Qb), relerr(mom.QCov,QCovb)]);
149 nBrt = nBrt + 1;
150end
151
152% D2. mixed load-dependent, truncated enumeration
153for trial = 1:6
154 M = 2; R = 2;
155 D = 0.3 + 0.4*rand(M,R);
156 N = [randi([1 2]), Inf];
157 Z = [0.3*rand, 0];
158 lambda = [0, 0.05 + 0.1*rand];
159 b = randi([1 2]);
160 mu = zeros(M,sum(N(isfinite(N))));
161 for i = 1:M
162 for n = 1:size(mu,2)
163 mu(i,n) = min(n,b) * (1.0 + 0.3*rand);
164 end
165 end
166 Lo = zeros(M,1);
167 for i = 1:M
168 Lo(i) = lambda*D(i,:)';
169 end
170 if any(Lo ./ mu(:,end) > 0.4)
171 continue;
172 end
173 mom = pfqn_sens_mvaldmx(lambda,D,N,Z,mu,ones(M,1));
174 [Qb,QCovb] = brute_ldmx(lambda,D,N,Z,mu,60);
175 errBrtT = max([errBrtT, relerr(mom.Q,Qb), relerr(mom.QCov,QCovb)]);
176 nBrtT = nBrtT + 1;
177end
178
179fprintf('\n=== pfqn_sens_mvaldmx validation (max relative error) ===\n');
180fprintf(' A. pfqn_mvaldmx base measures : %.3e (tol %.1e)\n', errMva, tolMva);
181fprintf(' B. finite differences (%3d params) : %.3e (tol %.1e)\n', nFd, errFd, tolFd);
182fprintf(' C. pfqn_sens_mva closed LI (%3d models) : %.3e (tol %.1e)\n', nMom, errMom, tolMom);
183fprintf(' D1. brute force closed LD (%3d models) : %.3e (tol %.1e)\n', nBrt, errBrt, tolBrt);
184fprintf(' D2. brute force mixed LD (%3d models) : %.3e (tol %.1e)\n', nBrtT, errBrtT, tolBrtT);
185fprintf(' E. QCovFull symmetry (raw) : %.3e (tol %.1e)\n', errSym, tolSym);
186
187ok = errMva <= tolMva && errFd <= tolFd && errMom <= tolMom && ...
188 errBrt <= tolBrt && errBrtT <= tolBrtT && errSym <= tolSym;
189if ~ok
190 error('pfqn_sens_mvaldmx_validate:mismatch','one or more checks exceeded tolerance');
191end
192fprintf(' ALL CHECKS PASSED\n');
193end
194
195% =========================================================================
196function e = relerr(a,b)
197a = a(:); b = b(:);
198d = abs(a-b);
199scale = max(1, max(abs(a),abs(b)));
200e = max(d ./ scale);
201if isempty(e)
202 e = 0;
203end
204end
205
206% =========================================================================
207function [Q,QCov] = brute_ldmx(lambda,D,N,Z,mu,Kopen)
208% Exact moments by enumerating the mixed load-dependent product form
209% p(n) ~ prod_i [ n_i! prod_r a(i,r)^n(i,r)/n(i,r)! prod_{j=1}^{n_i} 1/mu(i,j) ]
210% * prod_{closed c} Z(c)^n(0,c)/n(0,c)!
211% with a(i,r) = D(i,r) for a closed class and a(i,r) = lambda(r)*D(i,r) for an
212% open class, n_i the total population at station i, and the closed classes
213% constrained to sum to N. Open classes are truncated at Kopen jobs per station.
214[M,R] = size(D);
215openClasses = find(isinf(N));
216closedClasses = setdiff(1:R, openClasses);
217
218a = zeros(M,R);
219for r = 1:R
220 if isinf(N(r))
221 a(:,r) = lambda(r) * D(:,r);
222 else
223 a(:,r) = D(:,r);
224 end
225end
226
227% per-class allocation lists
228alloc = cell(1,R);
229for r = 1:R
230 if isinf(N(r))
231 alloc{r} = compositions_leq(Kopen,M); % open: 0..Kopen total, free
232 else
233 alloc{r} = compositions_leq(N(r),M); % closed: remainder in the delay
234 end
235end
236
237idx = ones(1,R);
238tot = 1;
239for r = 1:R
240 tot = tot * size(alloc{r},1);
241end
242W = zeros(tot,1);
243NIR = zeros(tot,M*R);
244k = 0;
245while true
246 k = k + 1;
247 nir = zeros(M,R);
248 for r = 1:R
249 nir(:,r) = alloc{r}(idx(r),:)';
250 end
251 lw = 0;
252 ok = true;
253 for i = 1:M
254 ni = sum(nir(i,:));
255 lw = lw + gammaln(ni+1);
256 for j = 1:ni
257 lw = lw - log(mu_at(mu,i,j));
258 end
259 for r = 1:R
260 if nir(i,r) > 0
261 if a(i,r) <= 0
262 ok = false; break;
263 end
264 lw = lw + nir(i,r)*log(a(i,r)) - gammaln(nir(i,r)+1);
265 end
266 end
267 if ~ok, break; end
268 end
269 if ok
270 for ci = 1:numel(closedClasses)
271 c = closedClasses(ci);
272 n0c = N(c) - sum(nir(:,c));
273 if n0c > 0
274 if Z(c) <= 0
275 ok = false; break;
276 end
277 lw = lw + n0c*log(Z(c)) - gammaln(n0c+1);
278 end
279 end
280 end
281 if ok
282 W(k) = exp(lw);
283 end
284 NIR(k,:) = reshape(nir',1,[]);
285 r = R;
286 while r >= 1
287 idx(r) = idx(r) + 1;
288 if idx(r) <= size(alloc{r},1)
289 break;
290 end
291 idx(r) = 1;
292 r = r - 1;
293 end
294 if r == 0
295 break;
296 end
297end
298W = W / sum(W);
299
300Q = zeros(M,R);
301for k = 1:size(NIR,1)
302 Q = Q + W(k)*reshape(NIR(k,:),R,M)';
303end
304QCov = zeros(M,R,R);
305for i = 1:M
306 for r = 1:R
307 for s = 1:R
308 m2 = 0;
309 for k = 1:size(NIR,1)
310 nir = reshape(NIR(k,:),R,M)';
311 m2 = m2 + W(k)*nir(i,r)*nir(i,s);
312 end
313 QCov(i,r,s) = m2 - Q(i,r)*Q(i,s);
314 end
315 end
316end
317end
318
319% =========================================================================
320function v = mu_at(mu,i,j)
321% Limited load dependence: the rate saturates at its last tabulated value.
322if j <= size(mu,2)
323 v = mu(i,j);
324else
325 v = mu(i,end);
326end
327end
328
329% =========================================================================
330function C = compositions_leq(n,M)
331% All nonnegative integer vectors of length M summing to at most n.
332if M == 1
333 C = (0:n)';
334 return;
335end
336C = zeros(0,M);
337for first = 0:n
338 sub = compositions_leq(n-first,M-1);
339 C = [C; [repmat(first,size(sub,1),1), sub]]; %#ok<AGROW>
340end
341end
Definition Station.m:245