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.
10function sens = pfqn_sens(L,N,Z,mi)
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.
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.
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
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,
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).
68if nargin < 3 || isempty(Z)
72if nargin < 4 || isempty(mi)
77% see _kb/03-api-layer.md (pfqn/ family: scaling, log-domain switches, dispatch gates)
79useComom = (M == 1) && all(mi == 1) && any(populated) && ...
80 all(Z(populated) > GlobalConstants.FineTol) && ...
81 all(L(1, populated) > GlobalConstants.FineTol);
83 sens = sens_comom(L,N,Z);
85 sens = sens_mva(L,N,Z,mi);
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;
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}.
100for p = 1:numel(sens.params)
103 pL(pr.station,pr.class) = p;
106QCov = zeros(M,R,M,R);
112 QCov(i,r,j,s) = mom.QCov(i,r,s);
116 QCov(i,r,j,s) = L(j,s) * sens.dQ(i,r,p);
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
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>
146% parameter list: L(1,r) first, then Z(r) (mirrors sens_mva ordering)
150paramType = cell(1,P); paramStation = zeros(1,P); paramClass = zeros(1,P);
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;
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);
160 sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
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.
171 if ~any(nz), lg = 0; return; end
172 key = [num2str(m) '|' sprintf('%d,',n)];
174 lg = cache(key); return;
176 lg = pfqn_comomrm(D(nz),n(nz),Z(nz),m,GlobalConstants.FineTol);
180 e = zeros(1,R); e(s) = 1;
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));
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));
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));
200 if N(r) < 1, continue; end
208 if X(r) > 0, C(r) = Q(r) / X(r); end
213 if N(r) < 1, continue; end % empty class: X=Q=0, all derivatives 0
215 Xr = X(r); Qr = Q(r);
219 Vrs = Qr * ((r==s) + 2*qplus(Nr,s) - Q(s)); % Cov[n_r,n_s]
221 dX_L = Xr * (qmean(Nr,s) - Q(s)) / D(s);
222 dU_L = dX_L * D(r) + Xr * (r==s);
227 dC(1,r,p) = (dQ_L*Xr - Qr*dX_L) / Xr^2;
230 % Z(s) parameter: d log G_m(n)/dZ_s = G_m(n-1_s)/G_m(n) (exact, any Z_s>=0)
232 dX_Z = Xr * (xput(1,Nr,s) - X(s));
233 dQ_Z = Qr * (xput(2,Nr,s) - X(s));
239 dC(1,r,p) = (dQ_Z*Xr - Qr*dX_Z) / Xr^2;
244sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
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.
257% parameter list: all L(i,r), then all Z(r)
259paramType = cell(1,P);
260paramStation = zeros(1,P);
261paramClass = zeros(1,P);
268 paramType{p} = 'L'; paramStation(p) = i; paramClass(p) = r;
274 paramType{p} = 'Z'; paramStation(p) = 0; paramClass(p) = r;
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);
282 sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
286% population-lattice odometer, identical to pfqn_mva
289 prods(w) = prod(ones(1,R-(w+1)+1) + N(w+1:R));
292while N(firstnonempty) == 0
293 firstnonempty = firstnonempty - 1;
297Qtot = zeros(totpop,M);
298Qtotd = zeros(totpop,M,P);
313 pos = pos + n(w)*prods(w);
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;
327 CNtot = CNtot + C(i,s);
328 CNtotd = CNtotd + Cd;
332 Xd = -n(s) * CNtotd / den^2;
333 Xd(pZ(s)) = Xd(pZ(s)) - n(s) / den^2;
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);
346 while (s>0 && n(s)==N(s)) || s>firstnonempty
359 currentpop = currentpop + 1;
362% utilization and its derivatives
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);
372sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass);
375% =========================================================================
376function sens = pack(X,Q,U,C,dX,dQ,dU,dC,paramType,paramStation,paramClass)
378params = struct('type',cell(1,P),'station',cell(1,P),'class',cell(1,P));
380 params(p).type = paramType{p};
381 params(p).station = paramStation(p);
382 params(p).class = paramClass(p);
384sens.X = X; sens.Q = Q; sens.U = U; sens.R = C;
386sens.dX = dX; sens.dQ = dQ; sens.dU = dU; sens.dR = dC;