LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_sens.m
1%{
2%{
3 % @file pfqn_sens.m
4 % @brief Exact analytic performance sensitivities for closed product-form
5 % queueing networks. Dispatches to a CoMoM-backed kernel for the
6 % single-station repairman model and to differentiated MVA otherwise.
7%}
8%}
9
10function sens = pfqn_sens(L,N,Z,mi)
11%{
12%{
13 % @brief Exact derivatives of the mean performance measures {X,Q,U,R} of a
14 % closed product-form (BCMP) queueing network with respect to the
15 % service demands L(i,r) and the think times Z(r). The derivatives are
16 % analytic (exact to machine precision), not finite differences.
17 %
18 % Two exact kernels are dispatched transparently (local subfunctions):
19 % - sens_comom : Class-Oriented Method of Moments. Selected for the
20 % repairman model (a single single-server queue, M=1, plus a
21 % think-time delay with sum(Z)>0 and strictly positive demands).
22 % Polynomial in the number of classes R via normalizing-constant
23 % moment relations (queue-length covariances / replica moments).
24 % - sens_mva : forward-mode differentiation of the exact
25 % Reiser-Lavenberg MVA recursion. Used for every other model.
26 % Both kernels return the identical struct layout, so the choice is an
27 % internal optimization invisible to callers.
28 %
29 % Reference: Z. Liu and P. Nain, "Sensitivity Results in Open, Closed
30 % and Mixed Product-Form Queueing Networks", INRIA RR-1144, 1989;
31 % X.-R. Cao and D.-J. Ma, "Performance sensitivity formulae, algorithms
32 % and estimates for closed queueing networks with exponential servers",
33 % Performance Evaluation 26:181-199, 1996; G. Casale, "CoMoM: Efficient
34 % Class-Oriented Evaluation of Multiclass Performance Models", IEEE TSE
35 % 2011.
36 %
37 % @fn pfqn_sens(L, N, Z, mi)
38 % @param L Service demand matrix (M x R), L(i,r) = visits_ir / rate_ir.
39 % @param N Population vector (1 x R).
40 % @param Z Think time vector (1 x R). Default: zeros.
41 % @param mi (Optional) Station residence multiplicity (1 x M). Default: ones.
42 % @return sens A struct with base measures and their Jacobians:
43 % .X (1 x R), .Q (M x R), .U (M x R), .R (M x R) base MVA measures
44 % (X system throughput per class, Q mean queue length, U utilization,
45 % R residence time per visit-chain, i.e. CN of pfqn_mva).
46 % .params 1 x P struct array describing each differentiation parameter,
47 % fields .type ('L' or 'Z'), .station (i, 0 for Z), .class (r).
48 % .dX (R x P), .dQ (M x R x P), .dU (M x R x P), .dR (M x R x P)
49 % derivatives of each base measure w.r.t. parameter p. For a 'L'
50 % parameter at (i,r) the derivative is d(.)/dL(i,r); for a 'Z'
51 % parameter at class r it is d(.)/dZ(r).
52 % .QCov (M x R x M x R) QCov(i,r,j,s) = Cov[n(i,r),n(j,s)], the exact
53 % queue-length covariance, a by-product of the Jacobian.
54 % .QVar (M x R) QVar(i,r) = Var[n(i,r)].
55 % .QTotVar (M x 1) QTotVar(i) = Var[sum_r n(i,r)].
56 % .QCovAsym (scalar) roundoff-level residual of the moment recursion,
57 % see pfqn_sens_mva.
58 %
59 % Notes:
60 % - Mirrors pfqn_mva(L,N,Z,mi) exactly for the base measures (single-server
61 % or residence-multiplicity mi stations plus an infinite-server delay Z).
62 % - Derivatives w.r.t. a service rate mu(i,r) follow by the chain rule
63 % d(.)/dmu(i,r) = -(L(i,r)/mu(i,r)) * d(.)/dL(i,r).
64%}
65%}
66[M,R] = size(L);
67N = ceil(N(:)');
68if nargin < 3 || isempty(Z)
69 Z = zeros(1,R);
70end
71Z = Z(:)';
72if nargin < 4 || isempty(mi)
73 mi = ones(1,M);
74end
75mi = mi(:)';
76
77% Dispatch to the CoMoM-backed kernel when the model is the repairman model
78% supported by pfqn_comomrm: a single single-server queue where every populated
79% class has both a strictly positive think time and a strictly positive demand.
80% Every other model (M>1, a populated class with zero think time or zero demand,
81% or load-dependent residence multiplicity) falls back to the exact
82% differentiated-MVA kernel, which handles the general case.
83populated = N > 0;
84useComom = (M == 1) && all(mi == 1) && any(populated) && ...
85 all(Z(populated) > GlobalConstants.FineTol) && ...
86 all(L(1, populated) > GlobalConstants.FineTol);
87if useComom
88 sens = sens_comom(L,N,Z);
89else
90 sens = sens_mva(L,N,Z,mi);
91end
92
93% Exact queue-length second moments. Both sources below rest on the same
94% product-form identity Cov[n_{i,r},n_{j,s}] = D_{j,s} dQ_{i,r}/dD_{j,s}, which
95% follows from L_{j,s} d/dL_{j,s}(G Q_{i,r}) = G E[n_{i,r} n_{j,s}]:
96% - same-station blocks (i==j) come from pfqn_sens_mva, which evaluates them by
97% the exact MVA-like recursion of de Souza e Silva and Muntz (1988),
98% Corollary 1. That recursion is self-contained at a station and costs about
99% 1/(M+1) of the Jacobian pass above, so sourcing the variances from it is
100% essentially free and independent of the differentiated-MVA kernel.
101% - cross-station blocks (i~=j) are read off the Jacobian. They are not
102% self-contained in the recursion, which for them would couple every station
103% pair and cost as much as the Jacobian itself.
104mom = pfqn_sens_mva(L,N,Z,mi);
105[sens.QCov, sens.QVar] = queue_cov(L,sens,mom);
106sens.QTotVar = mom.QTotVar;
107sens.QCovAsym = mom.QCovAsym;
108end
109
110% =========================================================================
111function [QCov,QVar] = queue_cov(L,sens,mom)
112% QCov(i,r,j,s) = Cov[n_{i,r},n_{j,s}] = D_{j,s} dQ_{i,r}/dD_{j,s}.
113[M,R] = size(L);
114pL = zeros(M,R);
115for p = 1:numel(sens.params)
116 pr = sens.params(p);
117 if pr.type == 'L'
118 pL(pr.station,pr.class) = p;
119 end
120end
121QCov = zeros(M,R,M,R);
122for i = 1:M
123 for r = 1:R
124 for j = 1:M
125 for s = 1:R
126 if i == j
127 QCov(i,r,j,s) = mom.QCov(i,r,s);
128 else
129 p = pL(j,s);
130 if p > 0
131 QCov(i,r,j,s) = L(j,s) * sens.dQ(i,r,p);
132 end
133 end
134 end
135 end
136 end
137end
138QVar = mom.QVar;
139end
140
141% =========================================================================
142% CoMoM-backed kernel (M=1 repairman model)
143% =========================================================================
144function sens = sens_comom(L,N,Z)
145% Exact derivatives of {X,Q,U,R} for a single single-server queue plus a
146% think-time delay, using pfqn_comomrm to evaluate normalizing constants of the
147% base model and of its 2- and 3-replica extensions. The queue-length moment
148% relation
149% D_{1,s} dQ_{1,r}/dD_{1,s} = Cov[n_{1,r},n_{1,s}]
150% = Q_{1,r}(delta_{rs}+2 Q^{+1}_{1,s}(N-1_r)-Q_{1,s})
151% yields the demand Jacobian; think-time derivatives use the exact identity
152% d log G_m(n)/dZ_s = G_m(n-1_s)/G_m(n) (valid for any Z_s>=0). Q^{+1}_{1,s}(n)
153% is the class-s queue at one of two identical replicas of the station:
154% Q^{+1}_{1,s}(n) = D_{1,s} G_3(n-1_s)/G_2(n),
155% obtained from CoMoM's replication factor m.
156[M,R] = size(L); %#ok<ASGLU>
157N = ceil(N(:)');
158Z = Z(:)';
159D = L(1,:);
160
161% parameter list: L(1,r) first, then Z(r) (mirrors sens_mva ordering)
162P = 2*R;
163pL = 1:R;
164pZ = R + (1:R);
165paramType = cell(1,P); paramStation = zeros(1,P); paramClass = zeros(1,P);
166for r = 1:R
167 paramType{pL(r)} = 'L'; paramStation(pL(r)) = 1; paramClass(pL(r)) = r;
168 paramType{pZ(r)} = 'Z'; paramStation(pZ(r)) = 0; paramClass(pZ(r)) = r;
169end
170
171X = zeros(1,R); Q = zeros(1,R); U = zeros(1,R); C = zeros(1,R);
172dX = zeros(R,P); dQ = zeros(1,R,P); dU = zeros(1,R,P); dC = zeros(1,R,P);
173
174if ~any(N > 0)
175 sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
176 return;
177end
178
179cache = containers.Map('KeyType','char','ValueType','double');
180 function lg = lgm(m,n)
181 % memoized log normalizing constant of the m-replica model; zero
182 % population classes are stripped (they leave the NC unchanged) so
183 % pfqn_comomrm does not index a stale class count after its sanitize.
184 n = round(n);
185 nz = n>0;
186 if ~any(nz), lg = 0; return; end
187 key = [num2str(m) '|' sprintf('%d,',n)];
188 if isKey(cache,key)
189 lg = cache(key); return;
190 end
191 lg = pfqn_comomrm(D(nz),n(nz),Z(nz),m,GlobalConstants.FineTol);
192 cache(key) = lg;
193 end
194 function e = ei(s)
195 e = zeros(1,R); e(s) = 1;
196 end
197 function q = qmean(n,s)
198 % mean class-s queue at population n (single station, m=1 model)
199 if n(s) < 1, q = 0; return; end
200 q = D(s) * exp(lgm(2,n-ei(s)) - lgm(1,n));
201 end
202 function x = xput(m,n,s)
203 % class-s throughput X_s^{(m)}(n) = G_m(n-1_s)/G_m(n) in m-replica model
204 if n(s) < 1, x = 0; return; end
205 x = exp(lgm(m,n-ei(s)) - lgm(m,n));
206 end
207 function q = qplus(n,s)
208 % Q^{+1}_{1,s}(n): class-s queue at one replica of the doubled station
209 if n(s) < 1, q = 0; return; end
210 q = D(s) * exp(lgm(3,n-ei(s)) - lgm(2,n));
211 end
212
213% base measures
214for r = 1:R
215 if N(r) < 1, continue; end
216 X(r) = xput(1,N,r);
217end
218for s = 1:R
219 Q(s) = qmean(N,s);
220end
221for r = 1:R
222 U(r) = X(r) * D(r);
223 if X(r) > 0, C(r) = Q(r) / X(r); end
224end
225
226% Jacobian
227for r = 1:R
228 if N(r) < 1, continue; end % empty class: X=Q=0, all derivatives 0
229 Nr = N - ei(r);
230 Xr = X(r); Qr = Q(r);
231 for s = 1:R
232 % L(1,s) parameter
233 p = pL(s);
234 Vrs = Qr * ((r==s) + 2*qplus(Nr,s) - Q(s)); % Cov[n_r,n_s]
235 dQ_L = Vrs / D(s);
236 dX_L = Xr * (qmean(Nr,s) - Q(s)) / D(s);
237 dU_L = dX_L * D(r) + Xr * (r==s);
238 dQ(1,r,p) = dQ_L;
239 dX(r,p) = dX_L;
240 dU(1,r,p) = dU_L;
241 if Xr > 0
242 dC(1,r,p) = (dQ_L*Xr - Qr*dX_L) / Xr^2;
243 end
244
245 % Z(s) parameter: d log G_m(n)/dZ_s = G_m(n-1_s)/G_m(n) (exact, any Z_s>=0)
246 p = pZ(s);
247 dX_Z = Xr * (xput(1,Nr,s) - X(s));
248 dQ_Z = Qr * (xput(2,Nr,s) - X(s));
249 dU_Z = dX_Z * D(r);
250 dQ(1,r,p) = dQ_Z;
251 dX(r,p) = dX_Z;
252 dU(1,r,p) = dU_Z;
253 if Xr > 0
254 dC(1,r,p) = (dQ_Z*Xr - Qr*dX_Z) / Xr^2;
255 end
256 end
257end
258
259sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
260end
261
262% =========================================================================
263% Differentiated-MVA kernel (general model)
264% =========================================================================
265function sens = sens_mva(L,N,Z,mi)
266% Forward-mode differentiation of the exact Reiser-Lavenberg MVA recursion.
267[M,R] = size(L);
268N = ceil(N(:)');
269Z = Z(:)';
270mi = mi(:)';
271
272% parameter list: all L(i,r), then all Z(r)
273P = M*R + R;
274paramType = cell(1,P);
275paramStation = zeros(1,P);
276paramClass = zeros(1,P);
277pL = zeros(M,R);
278pZ = zeros(1,R);
279p = 0;
280for i = 1:M
281 for r = 1:R
282 p = p + 1;
283 paramType{p} = 'L'; paramStation(p) = i; paramClass(p) = r;
284 pL(i,r) = p;
285 end
286end
287for r = 1:R
288 p = p + 1;
289 paramType{p} = 'Z'; paramStation(p) = 0; paramClass(p) = r;
290 pZ(r) = p;
291end
292
293X = zeros(1,R); Q = zeros(M,R); U = zeros(M,R); C = zeros(M,R);
294dX = zeros(R,P); dQ = zeros(M,R,P); dU = zeros(M,R,P); dC = zeros(M,R,P);
295
296if ~any(N > 0)
297 sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
298 return;
299end
300
301% population-lattice odometer, identical to pfqn_mva
302prods = zeros(1,R-1);
303for w = 1:R-1
304 prods(w) = prod(ones(1,R-(w+1)+1) + N(w+1:R));
305end
306firstnonempty = R;
307while N(firstnonempty) == 0
308 firstnonempty = firstnonempty - 1;
309end
310totpop = prod(N+1);
311ctr = totpop;
312Qtot = zeros(totpop,M);
313Qtotd = zeros(totpop,M,P);
314currentpop = 2;
315n = zeros(1,R);
316n(firstnonempty) = 1;
317C_d_is = zeros(M,P);
318
319while ctr
320 s = 1;
321 while s <= R
322 pos = 0;
323 if n(s) > 0
324 n(s) = n(s) - 1;
325 pos = n(R);
326 w = 1;
327 while w <= R-1
328 pos = pos + n(w)*prods(w);
329 w = w + 1;
330 end
331 n(s) = n(s) + 1;
332 end
333 row = 1 + pos;
334 CNtot = 0;
335 CNtotd = zeros(1,P);
336 for i = 1:M
337 base = mi(i) + Qtot(row,i);
338 C(i,s) = L(i,s) * base;
339 Cd = L(i,s) * reshape(Qtotd(row,i,:),1,P);
340 Cd(pL(i,s)) = Cd(pL(i,s)) + base;
341 C_d_is(i,:) = Cd;
342 CNtot = CNtot + C(i,s);
343 CNtotd = CNtotd + Cd;
344 end
345 den = Z(s) + CNtot;
346 X(s) = n(s) / den;
347 Xd = -n(s) * CNtotd / den^2;
348 Xd(pZ(s)) = Xd(pZ(s)) - n(s) / den^2;
349 dX(s,:) = Xd;
350 for i = 1:M
351 Q(i,s) = X(s) * C(i,s);
352 Qd = Xd * C(i,s) + X(s) * C_d_is(i,:);
353 dQ(i,s,:) = reshape(Qd,1,1,P);
354 dC(i,s,:) = reshape(C_d_is(i,:),1,1,P);
355 Qtot(currentpop,i) = Qtot(currentpop,i) + Q(i,s);
356 Qtotd(currentpop,i,:) = Qtotd(currentpop,i,:) + reshape(Qd,1,1,P);
357 end
358 s = s + 1;
359 end
360 s = R;
361 while (s>0 && n(s)==N(s)) || s>firstnonempty
362 s = s - 1;
363 end
364 if s == 0
365 break;
366 end
367 n(s) = n(s) + 1;
368 s = s + 1;
369 while s <= R
370 n(s) = 0;
371 s = s + 1;
372 end
373 ctr = ctr - 1;
374 currentpop = currentpop + 1;
375end
376
377% utilization and its derivatives
378for i = 1:M
379 for r = 1:R
380 U(i,r) = X(r) * L(i,r);
381 Ud = dX(r,:) * L(i,r);
382 Ud(pL(i,r)) = Ud(pL(i,r)) + X(r);
383 dU(i,r,:) = reshape(Ud,1,1,P);
384 end
385end
386
387sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
388end
389
390% =========================================================================
391function sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass)
392P = numel(paramType);
393params = struct('type',cell(1,P),'station',cell(1,P),'class',cell(1,P));
394for p = 1:P
395 params(p).type = paramType{p};
396 params(p).station = paramStation(p);
397 params(p).class = paramClass(p);
398end
399sens.X = X; sens.Q = Q; sens.U = U; sens.R = C;
400sens.params = params;
401sens.dX = dX; sens.dQ = dQ; sens.dU = dU; sens.dR = dC;
402end
Definition Station.m:245