LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_nc.m
1% Computes the normalizing constant of a product-form queueing network.
2% [LG,X,Q,METHOD] = PFQN_NC(LAMBDA,L,N,Z,VARARGIN)
3function [lG,X,Q,method] = pfqn_nc(lambda,L,N,Z,varargin)
4
5options = Solver.parseOptions(varargin, SolverNC.defaultOptions);
6method = 'exact'; % if early return is triggered
7% backup initial parameters
8Rin = length(N);
9
10if any(N<0) || isempty(N)
11 lG = -Inf;
12 X = [];
13 Q = [];
14 return
15end
16
17if sum(N)==0
18 lG = 0;
19 X = [];
20 Q = [];
21 return
22end
23
24if isempty(lambda)
25 lambda=0*N;
26end
27
28X=[]; Q=[];
29
30% compute open class contributions
31Qopen = [];
32lGopen = 0;
33Ut = zeros(1, size(L,1));
34for i=1:size(L,1)
35 Ut(i) = (1-lambda*L(i,:)');
36 if isnan(Ut(i))
37 Ut(i) = 0;
38 end
39 L(i,:) = L(i,:)/Ut(i);
40 Qopen(i,:) = lambda.*L(i,:)/Ut(i);
41 %lGopen = lGopen + log(Ut(i));
42end
43Qopen(isnan(Qopen))=0;
44ocl = find(isinf(N));
45% then erase open classes
46N(isinf(N)) = 0;
47
48% first remove empty classes
49nnzClasses = find(N);
50lambda = lambda(:,nnzClasses);
51L = L(:,nnzClasses);
52N = N(:,nnzClasses);
53Z = Z(:,nnzClasses);
54
55% see _kb/03-api-layer.md (pfqn_nc dispatch notes) for rationale
56R = length(N);
57scalevec = ones(1,R);
58%switch options.method
59% case {'adaptive','comom','default'}
60% % no-op
61% otherwise
62%end
63for r=1:R
64 scalevec(r) = max([L(:,r);Z(:,r)]);
65end
66%end
67L = L ./ repmat(scalevec,size(L,1),1);
68Z = Z ./ scalevec;
69
70% remove stations with no demand
71Lsum = sum(L,2);
72Lmax = max(L,[],2);
73demStations = find((Lmax./Lsum)>GlobalConstants.FineTol);
74noDemStations = setdiff(1:size(L,1), demStations);
75L = L(demStations,:);
76if any(N((sum(L,1) + sum(Z,1)) == 0)>0) % if there is a class with jobs but L and Z all zero
77 if options.verbose
78 line_warning(mfilename,'The model has no positive demands in any class.\n');
79 end
80 if isempty(Z) || sum(Z(:))<options.tol
81 lG = 0;
82 else
83 lG = - sum(factln(N)) + sum(N.*log(sum(Z,1))) + N*log(scalevec)';
84 end
85 return
86end
87
88% update M and R
89[M,R]=size(L);
90
91% return immediately if degenerate case
92if isempty(L) || sum(L(:))<options.tol % all demands are zero
93 if isempty(Z) || sum(Z(:))<options.tol
94 lG = lGopen;
95 else
96 lG = lGopen - sum(factln(N)) + sum(N.*log(sum(Z,1))) + N*log(scalevec)';
97 end
98 return
99elseif M==1 && (isempty(Z) || sum(Z(:))<options.tol) % single node and no think time
100 lG = factln(sum(N)) - sum(factln(N)) + sum(N.*log(L(1,:))) + N*log(scalevec)';
101 return
102elseif size(unique(L,'rows'),1)==1 && (isempty(Z) || sum(Z(:))<options.tol) % M identical replicas
103 lG = factln(sum(N)+M-1) - sum(factln(N)) + sum(N.*log(L(1,:))) + N*log(scalevec)' - factln(M-1);
104 return
105end
106
107% determine contribution from jobs that permanently loop at delay
108zeroDemandClasses = find(sum(L,1)<options.tol); % all jobs in delay
109nonzeroDemandClasses = setdiff(1:R, zeroDemandClasses);
110
111if isempty(sum(Z,1)) || all(sum(Z(:,zeroDemandClasses),1)<options.tol)
112 lGzdem = 0;
113 Nz = 0;
114else
115 if isempty(zeroDemandClasses) % for old MATLAB release compatibility
116 lGzdem = 0;
117 Nz = 0;
118 else
119 Nz = N(zeroDemandClasses);
120 lGzdem = - sum(factln(Nz)) + sum(Nz.*log(sum(Z(:,zeroDemandClasses),1))) + Nz*log(scalevec(zeroDemandClasses))';
121 end
122end
123L = L(:,nonzeroDemandClasses);
124N = N(nonzeroDemandClasses);
125Zz = Z(:,zeroDemandClasses);
126Z = Z(:,nonzeroDemandClasses);
127scalevecz = scalevec(nonzeroDemandClasses);
128% compute G for classes No with non-zero demand
129[lGnzdem,Xnnzdem,Qnnzdem,method] = compute_norm_const(L, N, Z, options);
130
131if isempty(Xnnzdem) % in this case the NC method does not return metrics as a by-product
132 X = [];
133 Q = [];
134else
135 zClasses = setdiff(1:Rin, nnzClasses);
136 Xz = zeros(1,length(zClasses));
137 Xnnz = zeros(1,length(nnzClasses));
138 Xnnz(zeroDemandClasses) = Nz./ sum(Zz,1)./ scalevec(zeroDemandClasses);
139 Xnnz(nonzeroDemandClasses) = Xnnzdem./ scalevec(nonzeroDemandClasses);
140 X(1,[zClasses, nnzClasses]) = [Xz, Xnnz];
141 X(ocl) = lambda(ocl);
142 Qz = zeros(size(Qnnzdem,1),length(zClasses));
143 Qnnz = zeros(size(Qnnzdem,1),length(nnzClasses));
144 Qnnz(:,zeroDemandClasses) = 0; % they are all in the delay
145 Qnnz(:,nonzeroDemandClasses) = Qnnzdem; % Q does not require scaling
146 Q(noDemStations,:) = 0;
147 Q(demStations,[zClasses, nnzClasses]) = [Qz, Qnnz];
148 Q(:,ocl) = Qopen(:,ocl);
149end
150% scale back to original demands
151lG = lGopen + lGnzdem + lGzdem + N*log(scalevecz)';
152end
153
154function [lG,X,Q,method] = compute_norm_const(L,N,Z,options)
155% LG = COMPUTE_NORM_CONST(L,N,Z,OPTIONS)
156% Auxiliary script that computes LG after the initial filtering of L,N,Z
157
158% Note: methods that can handle more efficiently replicas need to do so
159% within the method function
160
161% L,N,Z
162[M,R] = size(L);
163X=[];Q=[];
164method = options.method;
165switch options.method
166 case {'ca'}
167 [~,lG] = pfqn_ca(L,N,sum(Z,1));
168 case {'clw'}
169 % Choudhury-Leung-Whitt generating function inversion: each
170 % single-server station is a multiplicity-1 queue, delay is the IS term
171 [~,lG] = pfqn_clw(L,N,sum(Z,1));
172 case {'adaptive','default'}
173 if M>1
174 if sum(N)<1e3
175 % see _kb/03-api-layer.md (pfqn_nc dispatch notes) for rationale
176 Cmax = M*R*(50)^3; % upper cost budget
177 maxorder = min(ceil((sum(N)-1)/2),16);
178
179 totCost = 0;
180 order = 0; % will be raised as far as possible
181 while order < maxorder
182 nextCost = R * nchoosek(M + 2*(order+1), M-1); % cost of order+1
183 if totCost + nextCost <= Cmax
184 order = order + 1;
185 totCost = totCost + nextCost;
186 else
187 break
188 end
189 end
190
191 [~,lG] = pfqn_cub(L,N,sum(Z,1),order,GlobalConstants.FineTol);
192 method = 'cub';
193 else
194 [~,lG] = pfqn_le(L,N,sum(Z,1));
195 method = 'le';
196 end
197 elseif sum(Z(:))==0 % single queue, no delay
198 lG = -N*log(L)';
199 method = 'exact';
200 else % repairman model
201 if N<10000
202 % gleint is a better method but there are JAR loading issues
203 % at times upon loading the txt files
204 %[~,lG] = pfqn_mmint2_gausslegendre(L,N,sum(Z,1));
205 %method = 'gleint';
206 [lG] = pfqn_comomrm(L,N,Z,1,options.tol);
207 method = 'comom';
208 else
209 [~,lG] = pfqn_le(L,N,sum(Z,1));
210 method = 'le';
211 end
212 end
213 case {'sampling'}
214 if M==1
215 [~,lG] = pfqn_mmsample2(L,N,sum(Z,1),options.samples);
216 method = 'sampling';
217 elseif M>R
218 [~,lG] = pfqn_mci(L,N,sum(Z,1),options.samples,'imci');
219 method = 'imci';
220 else
221 [~,lG] = pfqn_ls(L,N,sum(Z,1),options.samples);
222 method = 'ls';
223 end
224 case {'mmint2','gleint'}
225 if size(L,1)>1
226 if options.verbose
227 line_warning(mfilename,sprintf('The %s method requires a model with a delay and a single queueing station.',options.method));
228 end
229 lG = [];
230 return
231 end
232 [~,lG] = pfqn_mmint2_gausslegendre(L,N,sum(Z,1));
233 case {'cub','gm'} % Grundmann-Mueller cubatures
234 order = ceil((sum(N)-1)/2); % exact
235 [~,lG] = pfqn_cub(L,N,sum(Z,1),order,GlobalConstants.FineTol);
236 case 'kt'
237 [~,lG] = pfqn_kt(L,N,sum(Z,1));
238 case 'le'
239 [~,lG] = pfqn_le(L,N,sum(Z,1));
240 case 'ls'
241 [~,lG] = pfqn_ls(L,N,sum(Z,1),options.samples);
242 case {'is'}
243 % see _kb/03-api-layer.md (pfqn_nc dispatch notes) for rationale
244 [~,lG] = pfqn_is(L,N,sum(Z,1),options);
245 method = 'is';
246 case {'mci','imci'}
247 [~,lG] = pfqn_mci(L,N,sum(Z,1),options.samples,options.method);
248 case {'mva'}
249 [~,~,~,~,lG] = pfqn_mva(L,N,sum(Z,1));
250 %case 'mom'
251 % line_warning(mfilename,'mom supported only in SolverJMT (method ).');
252 case {'exact'}
253 if M>=R || sum(N)>10 || sum(Z(:))>0
254 [~,lG] = pfqn_ca(L,N,sum(Z,1));
255 method = 'exact/ca';
256 else
257 [~,lG] = pfqn_recal(L,N,sum(Z,1));% implemented with Z=0
258 method = 'exact/recal';
259 end
260 case {'comom'}
261 if R>1
262 % see _kb/03-api-layer.md (pfqn_nc dispatch notes) for rationale
263 if M>1
264 line_error(mfilename,'The ''comom'' method supports a single queueing station, but this model has %d. Use ''default'' or ''ca'' for an exact normalizing constant, or SolverJMT with method ''jmva.comom''.', M);
265 end
266 try
267 % comom has a bug in computing X, sometimes the
268 % order is switched
269 [lG] = pfqn_comomrm(L,N,Z,1,options.tol);
270 catch ME
271 getReport(ME,'basic')
272 % java exception, probably singular linear system
273 %if options.verbose
274 %line_warning(mfilename,'Numerical problems.');
275 %end
276 lG = [];
277 end
278 else
279 [~,lG] = pfqn_ca(L,N,sum(Z,1));
280 method = 'ca';
281 end
282 case {'panacea'}
283 [~,lG] = pfqn_panacea(L,N,sum(Z,1));
284 if isnan(lG)
285 if options.verbose
286 line_warning(mfilename,'Model is not in normal usage, panacea cannot continue.\n');
287 lG = [];
288 return
289 end
290 end
291 case 'propfair'
292 [~,lG] = pfqn_propfair(L,N,sum(Z,1));
293 case {'recal'}
294 if sum(Z(:))>0
295 if options.verbose
296 line_warning(mfilename,'RECAL is currently available only for models with non-zero think times.\n');
297 lG = [];
298 return
299 end
300 end
301 [~,lG] = pfqn_recal(L,N,sum(Z,1));
302 % case 'rgf'
303 % if sum(Z(:))>0
304 % if option.verbose
305 % line_warning(mfilename,'RGF is defined only for models with non-zero think times.\n');
306 % lG = [];
307 % return
308 % end
309 % end
310 % [~,lG] = pfqn_rgf(L,N);
311 otherwise
312 lG=[];
313 X=[];
314 Q=[];
315 if options.verbose
316 line_warning(mfilename,sprintf('Unrecognized method: %s',options.method));
317 lG = [];
318 return
319 end
320 return
321end
322end