3 % @file pfqn_egflinearizer.m
4 % @brief Extended generalized fixed-point Linearizer approximation.
10 % @brief Extended generalized fixed-point Linearizer approximation.
11 % @fn pfqn_egflinearizer(L, N, Z, type, tol, maxiter, alpha)
12 % @param L Service demand matrix.
13 % @param N Population vector.
14 % @param Z Think time vector.
15 % @param type Scheduling strategy type per station.
16 % @param tol Convergence tolerance (
default: 1e-8).
17 % @param maxiter Maximum number of iterations (default: 1000).
18 % @param alpha Per-class scaling exponent vector.
19 % @return Q Mean queue lengths.
20 % @return U Utilization.
21 % @return W Waiting times.
22 % @return C Cycle times.
23 % @return X System throughput.
24 % @return totiter Total iterations performed.
27function [Q,U,W,C,X,totiter] = pfqn_egflinearizer(L,N,Z,type,tol,maxiter,alpha,QN0)
28% Single-server version of linearizer
45if isempty(L) || all(max(L)==0)
66 [~,q] = pfqn_bs(L,N_1,Z);
68 % warm-start
the Bard-Schweitzer initialization from
the supplied Q
69 [~,q] = pfqn_bs(L,N_1,Z,tol,maxiter,QN0);
80 N_1 = oner(N,s); % for k=0 it just returns N
82 [Q(:,:,1+s),~,~,iter] = Core(L,M,R,N_1,Z,Q(:,:,1+s),Delta,type,tol,maxiter-totiter,alpha);
83 totiter = totiter + iter;
89 % At population N-e_r only class r itself vanishes;
the other
90 % classes keep
the queue lengths Core just computed.
96 Delta(i,r,s) = Q(i,r,1+s)/Ns(r)^alpha(r) - Q(i,r,1+0)/N(r)^alpha(r);
98 % (N-e_s)_r = 0, i.e. r==s and N(r)==1: class r
is absent at
99 % N-e_s, so F_ir(N-e_s) = 0 by
the 0/0 convention of Chandy
100 % and Neuse (1982) eq (10). Their worked trace confirms it:
101 % D222 = 0 - L22(8,1)/1 = -1.
102 Delta(i,r,s) = -Q(i,r,1+0)/N(r)^alpha(r);
111[Q,W,X,iter] = Core(L,M,R,N,Z,Q(:,:,1+0),Delta,type,tol,maxiter-totiter,alpha);
112totiter = totiter + iter;
113% Compute performance metrics
124function [Q,W,T,iter] = Core(L,M,R,N_1,Z,Q,Delta,type,tol,maxiter,alpha)
131 % Estimate population at
132 Q_1 = Estimate(L,M,R,N_1,Z,Q,Delta,W,alpha);
134 [Q,W,T] = ForwardMVA(L,M,R,type,N_1,Z,Q_1);
135 if enorm(Q-Qlast)<tol || iter > maxiter
142function [Q_1,T_1] = Estimate(~,M,R,N_1,~,Q,Delta,~,alpha)
149 % A class with no jobs at N_1 (or at N_1-e_s) has queue length 0
150 % there. Guarding this
is required, not cosmetic: without it
151 % N_1(r)=0 divides by zero and oner returns a negative population
152 % (e.g. oner([0 2],1) = [-1 2]), so Q_1 becomes NaN for
the empty
153 % class and poisons any Delta computed from this slice.
154 if N_1(r) <= 0 || Ns(r) <= 0
157 Q_1(i,r,1+s) = Ns(r)^alpha(r)*(Q(i,r,1+0)/N_1(r)^alpha(r) + Delta(i,r,s));
163% This part
is not used in Core so commented out
167% % initial guess based on balanced job bound
168% % helpful in case no stations with positive demand exists
169% T_1(s,1+r) = Nr(s) / (Z(s) + max(L(:,s))*(sum(Nr)-1));
172% T_1(s,1+r) = Nr(s)*(Q(i,s)/N_1(s) + Delta(i,r,s))/W(i,s,1+0);
180function [Q,W,T] = ForwardMVA(L,M,R,type,N_1,Z,Q_1)
185% Compute residence time
186% Note: The PS formula W = D*(1+sum(Q))
is used for all scheduling types.
187% The FCFS correction (W = D + sum_s D_s*Q_s) requires per-visit service
188% times S = D/V, but only demands D (= V*S) are available at
the chain level.
189% Using D in place of S produces incorrect results when visit ratios differ
190% across chains (e.g., self-looping classes). Since for exponential FCFS
the
191% per-visit response time equals
the PS formula, this
is the correct approach
192% for
the chain-level linearizer.
195 W(ist,r) = L(ist,r)*(1+sum(Q_1(ist,:,1+r)));
199% Compute throughputs and qlens
201 T(r) = N_1(r) / (Z(r)+sum(W(:,r)));
203 Q(ist,r) = T(r) * W(ist,r);