1function pfqn_sens_validate()
2% PFQN_SENS_VALIDATE Numerically validate
the queue-length moment / demand-
3% derivative identities
for closed product-form (BCMP) queueing networks.
5% Notation (single-server load-independent stations, plus optional delay Z):
6% Q_{i,r}(N) mean queue length of class r at station i, pop. N
7% -> pfqn_mva(L,N,Z), QN(i,r)
8% Q_{i,s}^{+k}(N) queue length of
class s at ORIGINAL station i in
the
9% network obtained by adding one replica of station k
10% (a station with identical demands L(k,:))
11% -> pfqn_mva([L;L(k,:)],N,Z), read row i
12% D_{i,r} = L(i,r) service demand
13% dQ_{k,r}/dD_{i,s} -> pfqn_sens(L,N,Z), dQ(k,r,pL(i,s))
14% N - 1_r population with one class-r job removed
16% The three
"moment" identities and
the three demand-derivative formulas from
17%
the prompt are each checked over all admissible index tuples (i,k,r,s) on a
18% set of random closed networks. Analytic derivatives (pfqn_sens) are also
19% cross-checked against central finite differences of pfqn_mva.
23% ---- test models: {L, N, Z} -------------------------------------------------
25models{end+1} =
struct(
'L',[0.6 0.3; 0.4 0.7; 0.5 0.2],
'N',[3 2],
'Z',[0 0]);
26models{end+1} =
struct(
'L',[1.0 0.5; 0.3 0.9; 0.7 0.6],
'N',[2 3],
'Z',[0.4 0.8]);
27models{end+1} =
struct(
'L',rand(4,3)+0.2,
'N',[2 2 2],
'Z',[0 0 0]);
28models{end+1} =
struct(
'L',rand(3,2)+0.1,
'N',[4 3],
'Z',[0.5 0]);
30tolIdent = 1e-8; % pure MVA identities: near machine precision
31tolDeriv = 1e-6; % vs analytic sens (both from MVA recursion)
32tolFD = 1e-4; % analytic sens vs finite difference
34maxErr = zeros(1,7); % [ident1 ident2 ident3 der_diag der_offclass der_offstat sens_vs_FD]
36for mm = 1:numel(models)
37 L = models{mm}.L; N = models{mm}.N; Z = models{mm}.Z;
40 [~,QN] = pfqn_mva(L,N,Z); % base Q_{i,r}(N)
41 sens = pfqn_sens(L,N,Z); % analytic derivatives
42 pL = @(i,s) (i-1)*R + s; % pfqn_sens L-parameter index
44 % cache Q^{+k}(N-1_r)
for every (k,r) with N_r>=1
45 Qp = cell(M,R); % Qp{k,r}
is M x R (rows=orig stat)
49 Qp{k,r} = Qplus(L,N,Z,k,r);
55 if N(r) < 1,
continue; end
59 % ---------- moment identity 1 ----------
60 % Q_{i,s}^{+k}(N-1_r) Q_{k,r} = Q_{k,r}^{+i}(N-1_s) Q_{i,s}
62 lhs = Qp{k,r}(i,s) * QN(k,r);
63 rhs = Qp{i,s}(k,r) * QN(i,s);
64 maxErr(1) = max(maxErr(1), relerr(lhs,rhs));
66 % ---------- moment identity 2 ----------
67 % Q_{i,s}^{+k}(N-1_r) Q_{k,r} D_{k,s} D_{i,r}
68 % = Q_{i,r}^{+k}(N-1_s) Q_{k,s} D_{i,s} D_{k,r}
69 % (prompt wrote
the superscript as +i;
the exact
70 % identity
requires +k, forced by identities 1 and 3)
71 lhs = Qp{k,r}(i,s)*QN(k,r)*L(k,s)*L(i,r);
72 rhs = Qp{k,s}(i,r)*QN(k,s)*L(i,s)*L(k,r);
73 maxErr(2) = max(maxErr(2), relerr(lhs,rhs));
75 % ---------- moment identity 3 ----------
76 % Q_{i,s}^{+k}(N-1_r) Q_{k,r} D_{k,s} D_{i,r}
77 % = Q_{k,s}^{+i}(N-1_r) Q_{i,r} D_{i,s} D_{k,r}
78 lhs = Qp{k,r}(i,s)*QN(k,r)*L(k,s)*L(i,r);
79 rhs = Qp{i,r}(k,s)*QN(i,r)*L(i,s)*L(k,r);
80 maxErr(3) = max(maxErr(3), relerr(lhs,rhs));
83 % ---------- demand-derivative formulas ----------
84 % analytic LHS = D_{i,s} dQ_{k,r}/dD_{i,s}
85 dAna = L(i,s) * sens.dQ(k,r,pL(i,s));
86 % finite-difference cross-check of
the analytic derivative
87 dFD = L(i,s) * fd_dQ(L,N,Z,k,r,i,s);
88 maxErr(7) = max(maxErr(7), relerr(dAna,dFD));
91 % D_{k,r} dQ_{k,r}/dD_{k,r}
92 % = Q_{k,r}(1 + 2 Q_{k,r}^{+k}(N-1_r) - Q_{k,r})
93 rhs = QN(k,r)*(1 + 2*Qp{k,r}(k,r) - QN(k,r));
94 maxErr(4) = max(maxErr(4), relerr(dAna,rhs));
96 % D_{k,s} dQ_{k,r}/dD_{k,s}
97 % = Q_{k,r}(2 Q_{k,s}^{+k}(N-1_r) - Q_{k,s})
98 rhs = QN(k,r)*(2*Qp{k,r}(k,s) - QN(k,s));
99 maxErr(5) = max(maxErr(5), relerr(dAna,rhs));
101 % D_{i,s} dQ_{k,r}/dD_{i,s}
102 % = Q_{k,r}(Q_{i,s}^{+k}(N-1_r) - Q_{i,s})
103 rhs = QN(k,r)*(Qp{k,r}(i,s) - QN(i,s));
104 maxErr(6) = max(maxErr(6), relerr(dAna,rhs));
112names = {
'moment identity 1 ', ...
113 'moment identity 2 ', ...
114 'moment identity 3 ', ...
115 'deriv i=k,r=s (diagonal) ', ...
116 'deriv i=k,r~=s (offclass)', ...
117 'deriv i~=k (offstat) ', ...
118 'sens vs finite-difference'};
119tols = [tolIdent tolIdent tolIdent tolDeriv tolDeriv tolDeriv tolFD];
121fprintf(
'\n=== pfqn_sens formula validation (max relative error) ===\n');
124 ok = maxErr(f) <= tols(f);
126 fprintf(
' %s : %10.3e (tol %7.1e) %s\n', names{f}, maxErr(f), tols(f), passfail(ok));
128fprintf(
'\n%s\n\n', ternary(allok,
'ALL FORMULAS VALIDATED',
'SOME FORMULAS FAILED'));
130 error(
'pfqn_sens_validate:mismatch',
'one or more formulas exceeded tolerance');
134% -------------------------------------------------------------------------
135function Q = Qplus(L,N,Z,k,r)
136% Q_{i,s}^{+k}(N-1_r): queue lengths at
the ORIGINAL M stations when a replica
137% of station k
is added, evaluated at population N with one class-r job removed.
138Np = N; Np(r) = Np(r) - 1;
141[~,QNp] = pfqn_mva(Lp,Np,Z);
145% -------------------------------------------------------------------------
146function d = fd_dQ(L,N,Z,k,r,i,s)
147% central finite difference of Q_{k,r}(N) w.r.t. L(i,s)
148h = 1e-6 * max(1,abs(L(i,s)));
149Lp = L; Lp(i,s) = Lp(i,s) + h;
150Lm = L; Lm(i,s) = Lm(i,s) - h;
151[~,Qp] = pfqn_mva(Lp,N,Z);
152[~,Qm] = pfqn_mva(Lm,N,Z);
153d = (Qp(k,r) - Qm(k,r)) / (2*h);
156% -------------------------------------------------------------------------
157function e = relerr(a,b)
158e = abs(a-b) / max([1, abs(a), abs(b)]);
161function s = passfail(ok),
if ok, s=
'PASS';
else, s=
'FAIL'; end, end
162function s = ternary(c,a,b),
if c, s=a;
else, s=b; end, end