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% see _kb/03-api-layer.md (pfqn/ family: scaling, log-domain switches, dispatch gates)
78populated = N > 0;
79useComom = (M == 1) && all(mi == 1) && any(populated) && ...
80 all(Z(populated) > GlobalConstants.FineTol) && ...
81 all(L(1, populated) > GlobalConstants.FineTol);
82if useComom
83 sens = sens_comom(L,N,Z);
84else
85 sens = sens_mva(L,N,Z,mi);
86end
87
88% see _kb/03-api-layer.md (pfqn/ family: scaling, log-domain switches, dispatch gates)
89mom = pfqn_sens_mva(L,N,Z,mi);
90[sens.QCov, sens.QVar] = queue_cov(L,sens,mom);
91sens.QTotVar = mom.QTotVar;
92sens.QCovAsym = mom.QCovAsym;
93end
94
95% =========================================================================
96function [QCov,QVar] = queue_cov(L,sens,mom)
97% QCov(i,r,j,s) = Cov[n_{i,r},n_{j,s}] = D_{j,s} dQ_{i,r}/dD_{j,s}.
98[M,R] = size(L);
99pL = zeros(M,R);
100for p = 1:numel(sens.params)
101 pr = sens.params(p);
102 if pr.type == 'L'
103 pL(pr.station,pr.class) = p;
104 end
105end
106QCov = zeros(M,R,M,R);
107for i = 1:M
108 for r = 1:R
109 for j = 1:M
110 for s = 1:R
111 if i == j
112 QCov(i,r,j,s) = mom.QCov(i,r,s);
113 else
114 p = pL(j,s);
115 if p > 0
116 QCov(i,r,j,s) = L(j,s) * sens.dQ(i,r,p);
117 end
118 end
119 end
120 end
121 end
122end
123QVar = mom.QVar;
124end
125
126% =========================================================================
127% CoMoM-backed kernel (M=1 repairman model)
128% =========================================================================
129function sens = sens_comom(L,N,Z)
130% Exact derivatives of {X,Q,U,R} for a single single-server queue plus a
131% think-time delay, using pfqn_comomrm to evaluate normalizing constants of the
132% base model and of its 2- and 3-replica extensions. The queue-length moment
133% relation
134% D_{1,s} dQ_{1,r}/dD_{1,s} = Cov[n_{1,r},n_{1,s}]
135% = Q_{1,r}(delta_{rs}+2 Q^{+1}_{1,s}(N-1_r)-Q_{1,s})
136% yields the demand Jacobian; think-time derivatives use the exact identity
137% 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)
138% is the class-s queue at one of two identical replicas of the station:
139% Q^{+1}_{1,s}(n) = D_{1,s} G_3(n-1_s)/G_2(n),
140% obtained from CoMoM's replication factor m.
141[M,R] = size(L); %#ok<ASGLU>
142N = ceil(N(:)');
143Z = Z(:)';
144D = L(1,:);
145
146% parameter list: L(1,r) first, then Z(r) (mirrors sens_mva ordering)
147P = 2*R;
148pL = 1:R;
149pZ = R + (1:R);
150paramType = cell(1,P); paramStation = zeros(1,P); paramClass = zeros(1,P);
151for r = 1:R
152 paramType{pL(r)} = 'L'; paramStation(pL(r)) = 1; paramClass(pL(r)) = r;
153 paramType{pZ(r)} = 'Z'; paramStation(pZ(r)) = 0; paramClass(pZ(r)) = r;
154end
155
156X = zeros(1,R); Q = zeros(1,R); U = zeros(1,R); C = zeros(1,R);
157dX = zeros(R,P); dQ = zeros(1,R,P); dU = zeros(1,R,P); dC = zeros(1,R,P);
158
159if ~any(N > 0)
160 sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
161 return;
162end
163
164cache = containers.Map('KeyType','char','ValueType','double');
165 function lg = lgm(m,n)
166 % memoized log normalizing constant of the m-replica model; zero
167 % population classes are stripped (they leave the NC unchanged) so
168 % pfqn_comomrm does not index a stale class count after its sanitize.
169 n = round(n);
170 nz = n>0;
171 if ~any(nz), lg = 0; return; end
172 key = [num2str(m) '|' sprintf('%d,',n)];
173 if isKey(cache,key)
174 lg = cache(key); return;
175 end
176 lg = pfqn_comomrm(D(nz),n(nz),Z(nz),m,GlobalConstants.FineTol);
177 cache(key) = lg;
178 end
179 function e = ei(s)
180 e = zeros(1,R); e(s) = 1;
181 end
182 function q = qmean(n,s)
183 % mean class-s queue at population n (single station, m=1 model)
184 if n(s) < 1, q = 0; return; end
185 q = D(s) * exp(lgm(2,n-ei(s)) - lgm(1,n));
186 end
187 function x = xput(m,n,s)
188 % class-s throughput X_s^{(m)}(n) = G_m(n-1_s)/G_m(n) in m-replica model
189 if n(s) < 1, x = 0; return; end
190 x = exp(lgm(m,n-ei(s)) - lgm(m,n));
191 end
192 function q = qplus(n,s)
193 % Q^{+1}_{1,s}(n): class-s queue at one replica of the doubled station
194 if n(s) < 1, q = 0; return; end
195 q = D(s) * exp(lgm(3,n-ei(s)) - lgm(2,n));
196 end
197
198% base measures
199for r = 1:R
200 if N(r) < 1, continue; end
201 X(r) = xput(1,N,r);
202end
203for s = 1:R
204 Q(s) = qmean(N,s);
205end
206for r = 1:R
207 U(r) = X(r) * D(r);
208 if X(r) > 0, C(r) = Q(r) / X(r); end
209end
210
211% Jacobian
212for r = 1:R
213 if N(r) < 1, continue; end % empty class: X=Q=0, all derivatives 0
214 Nr = N - ei(r);
215 Xr = X(r); Qr = Q(r);
216 for s = 1:R
217 % L(1,s) parameter
218 p = pL(s);
219 Vrs = Qr * ((r==s) + 2*qplus(Nr,s) - Q(s)); % Cov[n_r,n_s]
220 dQ_L = Vrs / D(s);
221 dX_L = Xr * (qmean(Nr,s) - Q(s)) / D(s);
222 dU_L = dX_L * D(r) + Xr * (r==s);
223 dQ(1,r,p) = dQ_L;
224 dX(r,p) = dX_L;
225 dU(1,r,p) = dU_L;
226 if Xr > 0
227 dC(1,r,p) = (dQ_L*Xr - Qr*dX_L) / Xr^2;
228 end
229
230 % Z(s) parameter: d log G_m(n)/dZ_s = G_m(n-1_s)/G_m(n) (exact, any Z_s>=0)
231 p = pZ(s);
232 dX_Z = Xr * (xput(1,Nr,s) - X(s));
233 dQ_Z = Qr * (xput(2,Nr,s) - X(s));
234 dU_Z = dX_Z * D(r);
235 dQ(1,r,p) = dQ_Z;
236 dX(r,p) = dX_Z;
237 dU(1,r,p) = dU_Z;
238 if Xr > 0
239 dC(1,r,p) = (dQ_Z*Xr - Qr*dX_Z) / Xr^2;
240 end
241 end
242end
243
244sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
245end
246
247% =========================================================================
248% Differentiated-MVA kernel (general model)
249% =========================================================================
250function sens = sens_mva(L,N,Z,mi)
251% Forward-mode differentiation of the exact Reiser-Lavenberg MVA recursion.
252[M,R] = size(L);
253N = ceil(N(:)');
254Z = Z(:)';
255mi = mi(:)';
256
257% parameter list: all L(i,r), then all Z(r)
258P = M*R + R;
259paramType = cell(1,P);
260paramStation = zeros(1,P);
261paramClass = zeros(1,P);
262pL = zeros(M,R);
263pZ = zeros(1,R);
264p = 0;
265for i = 1:M
266 for r = 1:R
267 p = p + 1;
268 paramType{p} = 'L'; paramStation(p) = i; paramClass(p) = r;
269 pL(i,r) = p;
270 end
271end
272for r = 1:R
273 p = p + 1;
274 paramType{p} = 'Z'; paramStation(p) = 0; paramClass(p) = r;
275 pZ(r) = p;
276end
277
278X = zeros(1,R); Q = zeros(M,R); U = zeros(M,R); C = zeros(M,R);
279dX = zeros(R,P); dQ = zeros(M,R,P); dU = zeros(M,R,P); dC = zeros(M,R,P);
280
281if ~any(N > 0)
282 sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
283 return;
284end
285
286% population-lattice odometer, identical to pfqn_mva
287prods = zeros(1,R-1);
288for w = 1:R-1
289 prods(w) = prod(ones(1,R-(w+1)+1) + N(w+1:R));
290end
291firstnonempty = R;
292while N(firstnonempty) == 0
293 firstnonempty = firstnonempty - 1;
294end
295totpop = prod(N+1);
296ctr = totpop;
297Qtot = zeros(totpop,M);
298Qtotd = zeros(totpop,M,P);
299currentpop = 2;
300n = zeros(1,R);
301n(firstnonempty) = 1;
302C_d_is = zeros(M,P);
303
304while ctr
305 s = 1;
306 while s <= R
307 pos = 0;
308 if n(s) > 0
309 n(s) = n(s) - 1;
310 pos = n(R);
311 w = 1;
312 while w <= R-1
313 pos = pos + n(w)*prods(w);
314 w = w + 1;
315 end
316 n(s) = n(s) + 1;
317 end
318 row = 1 + pos;
319 CNtot = 0;
320 CNtotd = zeros(1,P);
321 for i = 1:M
322 base = mi(i) + Qtot(row,i);
323 C(i,s) = L(i,s) * base;
324 Cd = L(i,s) * reshape(Qtotd(row,i,:),1,P);
325 Cd(pL(i,s)) = Cd(pL(i,s)) + base;
326 C_d_is(i,:) = Cd;
327 CNtot = CNtot + C(i,s);
328 CNtotd = CNtotd + Cd;
329 end
330 den = Z(s) + CNtot;
331 X(s) = n(s) / den;
332 Xd = -n(s) * CNtotd / den^2;
333 Xd(pZ(s)) = Xd(pZ(s)) - n(s) / den^2;
334 dX(s,:) = Xd;
335 for i = 1:M
336 Q(i,s) = X(s) * C(i,s);
337 Qd = Xd * C(i,s) + X(s) * C_d_is(i,:);
338 dQ(i,s,:) = reshape(Qd,1,1,P);
339 dC(i,s,:) = reshape(C_d_is(i,:),1,1,P);
340 Qtot(currentpop,i) = Qtot(currentpop,i) + Q(i,s);
341 Qtotd(currentpop,i,:) = Qtotd(currentpop,i,:) + reshape(Qd,1,1,P);
342 end
343 s = s + 1;
344 end
345 s = R;
346 while (s>0 && n(s)==N(s)) || s>firstnonempty
347 s = s - 1;
348 end
349 if s == 0
350 break;
351 end
352 n(s) = n(s) + 1;
353 s = s + 1;
354 while s <= R
355 n(s) = 0;
356 s = s + 1;
357 end
358 ctr = ctr - 1;
359 currentpop = currentpop + 1;
360end
361
362% utilization and its derivatives
363for i = 1:M
364 for r = 1:R
365 U(i,r) = X(r) * L(i,r);
366 Ud = dX(r,:) * L(i,r);
367 Ud(pL(i,r)) = Ud(pL(i,r)) + X(r);
368 dU(i,r,:) = reshape(Ud,1,1,P);
369 end
370end
371
372sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
373end
374
375% =========================================================================
376function sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass)
377P = numel(paramType);
378params = struct('type',cell(1,P),'station',cell(1,P),'class',cell(1,P));
379for p = 1:P
380 params(p).type = paramType{p};
381 params(p).station = paramStation(p);
382 params(p).class = paramClass(p);
383end
384sens.X = X; sens.Q = Q; sens.U = U; sens.R = C;
385sens.params = params;
386sens.dX = dX; sens.dQ = dQ; sens.dU = dU; sens.dR = dC;
387end