LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_ca.m
1%{
2%{
3 % @file pfqn_ca.m
4 % @brief Convolution Algorithm for exact normalizing constant computation.
5%}
6%}
7
8function [Gn,lGn]=pfqn_ca(L,N,Z)
9%{
10%{
11 % @brief Convolution Algorithm for exact normalizing constant computation.
12 % @fn pfqn_ca(L, N, Z)
13 % @param L Service demand matrix.
14 % @param N Population vector.
15 % @param Z Think time vector.
16 % @return Gn Normalizing constant.
17 % @return lGn Logarithm of the normalizing constant.
18%}
19%}
20[M,R]=size(L);
21if nargin<3 || isempty(Z)
22 Z=zeros(1,R);
23end
24if M==0
25 lGn = - sum(factln(N)) + sum(N.*log(sum(Z,1)));
26 Gn = exp(lGn);
27 return
28end
29
30if min(N)<0
31 Gn=0;
32 lGn=-Inf;
33 return;
34end
35
36if sum(N)==0
37 Gn=1;
38 lGn=0;
39 return;
40end
41
42% Demand scaling, so that lGn stays computable once G(N) leaves the
43% double-precision range. The recursion below runs in linear space, so it
44% overflows to Inf as soon as G(N) > realmax, i.e. log G > 709.78 -- and lGn
45% was then returned as Inf even though log G is perfectly representable. This
46% is the floating-point range problem Reiser and Lavenberg (1980, JACM 27(2),
47% p.319) report for the convolution algorithm, and the scaling remedy is Lam
48% (1982), "Dynamic scaling and growth behavior of queuing network normalization
49% constants".
50%
51% Every state in G(N) carries the same total population sum(N), so dividing all
52% demands and think times by a constant c divides G(N) by exactly c^sum(N):
53% G(N; L/c, Z/c) = G(N; L, Z) / c^sum(N)
54% hence log G = log G_scaled + sum(N) log c, which is exact, not an
55% approximation.
56%
57% c must be chosen to CENTRE log G_scaled near 0, not merely to shrink the
58% demands. G(N) can leave the double range in EITHER direction: Reiser (1981,
59% Perf. Eval. 1:7-18, Sec. 6.1) reports the unnormalized convolution
60% UNDERFLOWING (< 1e-75, losing all significant digits) on his Fig. 5
61% central-server model for K > 160, and OVERFLOWING (> 1e75) on the same model
62% if the think time is raised. Scaling by max(L), the textbook choice, only
63% shrinks G and so makes the underflow strictly worse -- on Reiser's model it
64% turns a representable G(200) ~ 1e-87 into an underflow to 0.
65%
66% log G is estimated first, from the largest SINGLE-STATE term, which is a
67% lower bound on G(N) and in practice within O(log #states) of log G:
68% all jobs at one queueing station i : sum_r N_r log L_ir
69% all jobs at the delay : sum_r [N_r log Z_r - log N_r!]
70% On Reiser's model at K=200 this gives max(-460.5, -264.1) = -264.1 against a
71% true log G of about -263, i.e. accurate enough to place the scaled value well
72% inside the exponent range.
73%
74% c is then the power of two nearest exp(lGest/sum(N)), so that
75% log G_scaled = log G - sum(N) log c is near 0. A power of two matters: it
76% shifts exponents only, so L/c and Z/c stay exactly representable and the
77% recursion is bit-for-bit the unscaled one with shifted exponents.
78Nt = sum(N);
79lGest = -Inf;
80for i = 1:M
81 t = 0; ok = true;
82 for r = 1:R
83 if N(r) > 0
84 if L(i,r) > 0
85 t = t + N(r)*log(L(i,r));
86 else
87 ok = false; break
88 end
89 end
90 end
91 if ok, lGest = max(lGest, t); end
92end
93if any(Z > 0) % all jobs at the delay
94 t = 0; ok = true;
95 for r = 1:R
96 if N(r) > 0
97 if sum(Z(:,r)) > 0
98 t = t + N(r)*log(sum(Z(:,r))) - factln(N(r));
99 else
100 ok = false; break
101 end
102 end
103 end
104 if ok, lGest = max(lGest, t); end
105end
106if ~isfinite(lGest)
107 kscale = 0;
108else
109 kscale = round(lGest/(Nt*log(2)));
110end
111cscale = pow2(kscale);
112L = L / cscale;
113Z = Z / cscale;
114
115G = ones(M+1,prod(N+1)); % stores G across recursion
116n = pprod(N);
117while sum(n)~=-1
118 idxn = hashpop(n,N);
119 G(1,idxn) = Fz(Z,n);
120 for m=2:M+1
121 G(m,idxn) = G(m-1,idxn); % norm constant with m-1 queues
122 for r=1:R
123 if n(r)>=1
124 n(r) = n(r)-1;
125 idxn_1r = hashpop(n,N);
126 n(r) = n(r)+1;
127 G(m,idxn) = G(m,idxn) + L(m-1,r)*G(m,idxn_1r);
128 end
129 end
130 end
131 n=pprod(n,N);
132end
133% Undo the scaling in log space: log G = log G_scaled + sum(N) log c. lGn is
134% therefore finite whenever log G itself is, even though Gn may legitimately
135% overflow to Inf (the true constant really is outside double range).
136lGn = log(G(M+1,end)) + sum(N)*kscale*log(2);
137% Gn is recovered by scaling the mantissa back with pow2 (an exact exponent
138% adjustment), not as exp(lGn): on models that never overflowed this returns
139% bit-for-bit the value the unscaled recursion used to return, and it still
140% goes to Inf when the constant genuinely leaves double range -- in which case
141% lGn above remains finite and usable.
142Gn = pow2(G(M+1,end), sum(N)*kscale);
143end
144
145function idx=hashpop(n,N,R,prods)
146% IDX=HASHPOP(N,N,R,PRODS)
147
148% hash a population vector in n: 0<=n<=N
149idx=1;
150if nargin==2
151 R=length(N);
152 for r=1:R
153 idx= idx + prod(N(1:r-1)+1)*n(r);
154 end
155 return
156else
157 for r=1:R
158 idx= idx + prods(r)*n(r);
159 end
160end
161end
162
163function [n]=pprod(n,N)
164% [N]=PPROD(N,N)
165
166% sequentially generate all vectors n: 0<=n<=N
167% n=pprod(N) - init
168% n=pprod(n,N) - next state
169if nargin==1
170 N=n;
171 n=zeros(size(N));
172 return;
173end
174
175R=length(N);
176if sum(n==N)==R
177 n=-1;
178 return
179end
180
181s=R;
182while s>0 && n(s)==N(s)
183 n(s)=0;
184 s=s-1;
185end
186if s==0
187 %n=-1*ones(1,R);
188 return
189end
190n(s)=n(s)+1;
191return;
192end
193
194function f=Fz(Z,n)
195% F=FZ(Z,N)
196
197R=length(n);
198if sum(n)==0
199 f=1;
200 return
201end
202f=0;
203for r=1:R
204 if Z(r)>0
205 f=f+log(Z(r))*n(r);
206 f=f-gammaln(1+n(r));
207 elseif n(r)>0
208 f = 0;
209 return
210 end
211end
212f=exp(f);
213end
Definition Station.m:245