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)
5options = Solver.parseOptions(varargin, SolverNC.defaultOptions);
6method =
'exact'; %
if early
return is triggered
7% backup initial parameters
10if any(N<0) || isempty(N)
30% compute open
class contributions
33Ut = zeros(1, size(L,1));
35 Ut(i) = (1-lambda*L(i,:)');
39 L(i,:) = L(i,:)/Ut(i);
40 Qopen(i,:) = lambda.*L(i,:)/Ut(i);
41 %lGopen = lGopen + log(Ut(i));
45% then erase open classes
48% first remove empty classes
50lambda = lambda(:,nnzClasses);
55% see _kb/03-api-layer.md (pfqn_nc dispatch notes) for rationale
59% case {
'adaptive',
'comom',
'default'}
64 scalevec(r) = max([L(:,r);Z(:,r)]);
67L = L ./ repmat(scalevec,size(L,1),1);
70% remove stations with no demand
73demStations = find((Lmax./Lsum)>GlobalConstants.FineTol);
74noDemStations = setdiff(1:size(L,1), 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
78 line_warning(mfilename,'The model has no positive demands in any class.\n');
80 if isempty(Z) || sum(Z(:))<options.tol
83 lG = - sum(factln(N)) + sum(N.*log(sum(Z,1))) + N*log(scalevec)
';
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
96 lG = lGopen - sum(factln(N)) + sum(N.*log(sum(Z,1))) + N*log(scalevec)';
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)
';
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);
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);
111if isempty(sum(Z,1)) || all(sum(Z(:,zeroDemandClasses),1)<options.tol)
115 if isempty(zeroDemandClasses) %
for old MATLAB release compatibility
119 Nz = N(zeroDemandClasses);
120 lGzdem = - sum(factln(Nz)) + sum(Nz.*log(sum(Z(:,zeroDemandClasses),1))) + Nz*log(scalevec(zeroDemandClasses))
';
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);
131if isempty(Xnnzdem) % in this case the NC method does not return metrics as a by-product
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);
150% scale back to original demands
151lG = lGopen + lGnzdem + lGzdem + N*log(scalevecz)';
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
158% Note: methods that can handle more efficiently replicas need to
do so
159% within the method function
164method = options.method;
167 [~,lG] = pfqn_ca(L,N,sum(Z,1));
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'}
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);
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
185 totCost = totCost + nextCost;
191 [~,lG] = pfqn_cub(L,N,sum(Z,1),order,GlobalConstants.FineTol);
194 [~,lG] = pfqn_le(L,N,sum(Z,1));
197 elseif sum(Z(:))==0 % single queue, no delay
200 else % repairman model
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));
206 [lG] = pfqn_comomrm(L,N,Z,1,options.tol);
209 [~,lG] = pfqn_le(L,N,sum(Z,1));
215 [~,lG] = pfqn_mmsample2(L,N,sum(Z,1),options.samples);
218 [~,lG] = pfqn_mci(L,N,sum(Z,1),options.samples,'imci
');
221 [~,lG] = pfqn_ls(L,N,sum(Z,1),options.samples);
224 case {'mmint2
','gleint
'}
227 line_warning(mfilename,sprintf('The %s method requires a model with a delay and a single queueing station.
',options.method));
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);
237 [~,lG] = pfqn_kt(L,N,sum(Z,1));
239 [~,lG] = pfqn_le(L,N,sum(Z,1));
241 [~,lG] = pfqn_ls(L,N,sum(Z,1),options.samples);
243 % see _kb/03-api-layer.md (pfqn_nc dispatch notes) for rationale
244 [~,lG] = pfqn_is(L,N,sum(Z,1),options);
247 [~,lG] = pfqn_mci(L,N,sum(Z,1),options.samples,options.method);
249 [~,~,~,~,lG] = pfqn_mva(L,N,sum(Z,1));
251 % line_warning(mfilename,'mom supported only in SolverJMT (method ).
');
253 if M>=R || sum(N)>10 || sum(Z(:))>0
254 [~,lG] = pfqn_ca(L,N,sum(Z,1));
257 [~,lG] = pfqn_recal(L,N,sum(Z,1));% implemented with Z=0
258 method = 'exact/recal
';
262 % see _kb/03-api-layer.md (pfqn_nc dispatch notes) for rationale
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);
267 % comom has a bug in computing X, sometimes the
269 [lG] = pfqn_comomrm(L,N,Z,1,options.tol);
271 getReport(ME,'basic
')
272 % java exception, probably singular linear system
274 %line_warning(mfilename,'Numerical problems.
');
279 [~,lG] = pfqn_ca(L,N,sum(Z,1));
283 [~,lG] = pfqn_panacea(L,N,sum(Z,1));
286 line_warning(mfilename,'Model
is not in normal usage, panacea cannot continue.\n
');
292 [~,lG] = pfqn_propfair(L,N,sum(Z,1));
296 line_warning(mfilename,'RECAL
is currently available only for models with non-zero think times.\n
');
301 [~,lG] = pfqn_recal(L,N,sum(Z,1));
305 % line_warning(mfilename,'RGF
is defined only for models with non-zero think times.\n
');
310 % [~,lG] = pfqn_rgf(L,N);
316 line_warning(mfilename,sprintf('Unrecognized method: %s
',options.method));