3 % @file pfqn_sens_linearizer.m
4 % @brief Approximate higher moments of
the queue lengths of a closed
5 % product-form queueing network, by differentiating
the Linearizer
6 % fixed point. Polynomial in
the population, unlike
the exact
11function res = pfqn_sens_linearizer(L,N,Z,tol,maxiter)
14 % @brief Approximate moments E[Q_i], Var[Q_i], Cov[Q_i,Q_j], E[Q_i^2] and
15 % E[Q_i^3] of
the per-station total queue lengths of a closed
16 % product-form queueing network, by
the LINEARIZER-2 / LINEARIZER-3
17 % algorithms of
the reference (Section 5).
19 % Motivation. The exact moment analysis of pfqn_sens_mom evaluates
the
20 % MVA recursion on
the whole population lattice, so it costs
21 % O(prod(N+1)) and
is unusable once
the populations are large. The
22 % Linearizer replaces that lattice by a fixed point over a handful of
23 % populations, and
the reference observes that
the same trick applies to
24 %
the derivatives: differentiate
the Linearizer equations, append
the
25 % differentiated equations to
the originals, and iterate all of them
26 % together. This routine does that, carrying both
the first and
the
27 % second derivative, so it returns everything (3.2) needs, including
the
28 % third moment. Carrying only
the first derivative
is the reference's
29 % LINEARIZER-2; carrying
the second as well
is its LINEARIZER-3.
31 % The approximation. CORE (equations (5.1)-(5.2)) estimates
the queue
32 % lengths at population n - 1_l from those at n by
34 % v_i(l) = m_i^(n)(l) / n(l)
35 % m_i^(n-1_l')(l) = (n - 1_l')_l * ( v_i(l) + delta_i(l',l) )
37 % and substitutes them into
the exact MVA equations. Setting
the delta
38 % terms to zero gives Bard-Schweitzer; Linearizer instead estimates them
39 % from (5.3), delta_i^(N)(l',l) = v_i^(N-1_l')(l) - v_i^(N)(l), by
40 % running CORE at each of
the N - 1_l populations, and holds them fixed
41 % across populations (
the heuristic (5.4)). Differentiating (5.1)-(5.3)
42 % gives (5.5)-(5.8), which are carried alongside.
44 % Accuracy. The reference reports, over 51 networks including 34 stress
45 % cases, relative errors below 2.1% on E[Q], 4.1% on E[Q^2] and 6.2% on
46 % E[Q^3]. pfqn_sens_linearizer_validate measures
the error against
the
47 % exact pfqn_sens_mom on models small enough for both, and asserts
48 % bands of that order rather than machine precision: this routine
is an
49 % approximation and
is expected to disagree with
the exact answer.
51 % Reference: J. C. Strelen, "Moment Analysis for Closed Queuing Networks
52 % and its Linearizer", Performance Evaluation 11:127-142, 1990,
53 % Section 5, equations (5.1)-(5.8),
the CORE-2 and LINEARIZER-2
54 % algorithms. The delta definition follows
the standard Chandy-Neuse
55 % Linearizer, v_i^(N-1_l')(l) - v_i^(N)(l), which
is what LINE's
56 % pfqn_linearizer implements.
58 % @fn pfqn_sens_linearizer(L, N, Z, tol, maxiter)
59 % @param L Service demand matrix (M x R), L(i,r) = visits_ir / rate_ir.
60 % @param N Population vector (1 x R).
61 % @param Z Think time vector (1 x R). Default: zeros.
62 % @param tol (Optional) Convergence tolerance of
the CORE fixed point on
the
63 % mean queue lengths. Default:
the test of
the reference,
64 % 1/(4000+16*sum(n)), which
is also applied to
the variances at 1e-3.
65 % @param maxiter (Optional) Maximum CORE iterations. Default: 200.
66 % @return res A struct with:
67 % .X (1 x R), .Q (M x R), .U (M x R), .W (M x R) approximate base measures.
68 % .m (M x 1) approximate E[Q_i],
the total queue length at station i.
69 % .dm (M x M) dm(i,h) = x_h dm_i/dx_h,
the scaled first derivative.
70 % .d2m (M x 1) d2m(i) = x_i^2 d^2m_i/dx_i^2.
71 % .Var (M x 1), .Cov (M x M), .M2 (M x 1), .M3 (M x 1), .Skew (M x 1)
72 %
the moments of (3.2), formed exactly as in pfqn_sens_mom but from
the
73 % approximate derivatives.
74 % .CovAsym (scalar) raw asymmetry of Cov before symmetrization. Unlike
the
75 % exact routines, this
is NOT expected to sit at roundoff:
the Linearizer
76 % fixed point does not enforce
the symmetry that
the product form
77 % guarantees, so this
is a useful measure of
the approximation error.
78 % .iter (scalar) total CORE iterations performed.
81 % - Single-server stations plus an optional delay Z, matching pfqn_linearizer.
82 % - The exact counterpart
is pfqn_sens_mom;
the per-class exact second moments
83 % are in pfqn_sens_mva.
88if nargin < 3 || isempty(Z)
92if nargin < 4 || isempty(tol)
95if nargin < 5 || isempty(maxiter)
99 line_error(mfilename,'demand matrix and population vector have different number of classes');
102 line_error(mfilename,'pfqn_sens_linearizer requires a closed population');
106 res = pack(zeros(1,R),zeros(M,R),zeros(M,R),zeros(M,R),zeros(M,1), ...
107 zeros(M,M),zeros(M,1),0);
111% ---- Linearizer state ----------------------------------------------------
112% pops: index 1 = N, index 1+l = N - e_l
119 pv(1+l,l) = N(l) - 1;
123% m{p}(i,l) = estimate of m_i^{(pv(p,:))}(l), and its derivatives w.r.t. y_h
124mE = cell(npops,1); dmE = cell(npops,1); d2mE = cell(npops,1);
128 mE{p}(:,l) = pv(p,l)/M; % initialization of
the reference
130 dmE{p} = zeros(M,R,M);
131 d2mE{p} = zeros(M,R,M);
133% delta(i,l
',l), indexed by the removed class l' and
the class l
135ddelta = zeros(M,R,R,M);
136d2delta = zeros(M,R,R,M);
139Xf = zeros(1,R); Qf = zeros(M,R); Wf = zeros(M,R);
140dmf = zeros(M,M); d2mf = zeros(M,1);
143 % ---- Step 1: CORE at
the full population --------------------------
144 [mE{1}, dmE{1}, d2mE{1}, Xf, Wf, it] = core2(L,Z,N,delta,ddelta,d2delta, ...
145 mE{1}, dmE{1}, d2mE{1}, tol, maxiter);
146 totiter = totiter + it;
149 % ---- Step 2: CORE at each of
the N - e_l populations ------------
154 [mE{1+l}, dmE{1+l}, d2mE{1+l}, ~, ~, it] = core2(L,Z,pv(1+l,:), ...
155 delta,ddelta,d2delta, mE{1+l}, dmE{1+l}, d2mE{1+l}, tol, maxiter);
156 totiter = totiter + it;
159 % ---- Step 3: refresh delta from (5.1) and (5.3) ----------------
160 [vN, dvN, d2vN] = fractions(mE{1}, dmE{1}, d2mE{1}, N);
165 [vL, dvL, d2vL] = fractions(mE{1+lp}, dmE{1+lp}, d2mE{1+lp}, pv(1+lp,:));
168 delta(i,lp,l) = vL(i,l) - vN(i,l);
170 ddelta(i,lp,l,h) = dvL(i,l,h) - dvN(i,l,h);
171 d2delta(i,lp,l,h) = d2vL(i,l,h) - d2vN(i,l,h);
179% ----
final measures ------------------------------------------------------
184 dmf(i,h) = sum(dmE{1}(i,:,h));
186 d2mf(i) = sum(d2mE{1}(i,:,i));
191 U(i,r) = Xf(r) * L(i,r);
195res = pack(Xf,Qf,U,Wf,m,dmf,d2mf,totiter);
198% =========================================================================
199function [v, dv, d2v] = fractions(m, dm, d2m, n)
200% (5.1) and (5.5): v_i(l) = m_i(l)/n(l), and likewise
for the derivatives.
202v = zeros(M,R); dv = zeros(M,R,M); d2v = zeros(M,R,M);
207 v(:,l) = m(:,l) / n(l);
208 dv(:,l,:) = dm(:,l,:) / n(l);
209 d2v(:,l,:) = d2m(:,l,:) / n(l);
213% =========================================================================
214function [m, dm, d2m, lam, w, it] = core2(L,Z,n,delta,ddelta,d2delta,m,dm,d2m,tol,maxiter)
215% CORE-2 of
the reference, extended to second derivatives. Iterates (5.1),
216% (5.2) and
the MVA equations (1.4)-(1.5) together with their first and second
217% derivatives (5.5)-(5.6) and (3.4), until
the mean queue lengths and
the
218% variances both stop moving.
222 tolm = 1/(4000 + 16*nc); % termination test of
the reference
228lam = zeros(1,R); w = zeros(M,R);
235 % ---- (5.1)-(5.2) and (5.5)-(5.6): queue lengths one job down --------
236 % maux(i,l,l2) estimates m_i^{(n - e_l)}(l2)
237 [v, dv, d2v] = fractions(m, dm, d2m, n);
238 mtot = zeros(M,R); % mtot(i,l) = sum_l2 m_i^{(n-e_l)}(l2)
239 dmtot = zeros(M,R,M);
240 d2mtot = zeros(M,R,M);
246 acc = 0; dacc = zeros(1,M); d2acc = zeros(1,M);
248 cnt = n(l2) - (l2 == l); % (n - 1_l)_{l2}
252 acc = acc + cnt * (v(i,l2) + delta(i,l,l2));
254 dacc(h) = dacc(h) + cnt * (dv(i,l2,h) + ddelta(i,l,l2,h));
255 d2acc(h) = d2acc(h) + cnt * (d2v(i,l2,h) + d2delta(i,l,l2,h));
260 dmtot(i,l,h) = dacc(h);
261 d2mtot(i,l,h) = d2acc(h);
266 % ---- MVA (1.4)-(1.5) and its derivatives (3.4) ----------------------
267 % w_i(l) = y_i * L(i,l) * (1 + mtot(i,l))
268 w = zeros(M,R); dw = zeros(M,R,M); d2w = zeros(M,R,M);
280 dw(i,l,h) = L(i,l) * (A + dA);
281 d2w(i,l,h) = L(i,l) * (2*dA + d2A);
283 dw(i,l,h) = L(i,l) * dA;
284 d2w(i,l,h) = L(i,l) * d2A;
289 lam = zeros(1,R); dlam = zeros(R,M); d2lam = zeros(R,M);
294 den = Z(l) + sum(w(:,l));
297 dden = sum(dw(:,l,h));
298 d2den = sum(d2w(:,l,h));
299 dlam(l,h) = -n(l) * dden / den^2;
300 d2lam(l,h) = -n(l) * d2den / den^2 + 2*n(l) * dden^2 / den^3;
303 m = zeros(M,R); dm = zeros(M,R,M); d2m = zeros(M,R,M);
309 m(i,l) = lam(l) * w(i,l);
311 dm(i,l,h) = dlam(l,h)*w(i,l) + lam(l)*dw(i,l,h);
312 d2m(i,l,h) = d2lam(l,h)*w(i,l) + 2*dlam(l,h)*dw(i,l,h) ...
318 % ---- termination test of
the reference ------------------------------
324 dev = max(dev, max(abs(m(:,l) - mprev(:,l))) / n(l));
328 varnow(i) = sum(dm(i,:,i));
332 vdev = max(abs(varnow - varprev)) / sv;
337 if dev <= tolm && vdev <= tolv
343% =========================================================================
344function res = pack(X,Q,U,W,m,dm,d2m,it)
346res.X = X; res.Q = Q; res.U = U; res.W = W;
347res.m = m; res.dm = dm; res.d2m = d2m; res.iter = it;
349% Cov(i,j) = x_j dm_i/dx_j. The product form makes
this symmetric, but
the
350% Linearizer fixed point does not enforce that, so
the raw asymmetry
is a
351% genuine error indicator here rather than a roundoff residual.
353res.CovAsym = max(max(abs(Cov - Cov.
')));
354if isempty(res.CovAsym)
357Cov = (Cov + Cov.') / 2;
360Var = zeros(M,1); M2 = zeros(M,1); M3 = zeros(M,1); Skew = zeros(M,1);
363 M2(i) = dm(i,i) + m(i)^2;
364 M3(i) = d2m(i) + (1 + 3*m(i))*dm(i,i) + m(i)^3;
365 mu3 = M3(i) - 3*m(i)*M2(i) + 2*m(i)^3;
367 Skew(i) = mu3 / Var(i)^1.5;
372res.Var = Var; res.M2 = M2; res.M3 = M3; res.Skew = Skew;