LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_ncld.m
1function [Q,U,R,T,C,X,lG,runtime,iter,method] = solver_ncld(sn, options)
2% [Q,U,R,T,C,X,LG,RUNTIME,ITER.METHOD] = SOLVER_NCLD(QN, OPTIONS)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6M = sn.nstations; %number of stations
7K = sn.nclasses;
8nservers = sn.nservers;
9if nservers(isfinite(nservers))>1
10 if isempty(sn.lldscaling) && M==2 && all(isfinite(sn.njobs))
11 for ist=1:M
12 Nt = sum(sn.njobs);
13 sn.lldscaling(ist,1:Nt) = min(1:Nt,sn.nservers(ist));
14 end
15 else
16 line_error(mfilename,'The load-dependent solver does not support multi-server stations yet. Specify multi-server stations via limited load-dependence.');
17 end
18end
19
20if ~isempty(sn.cdscaling) && strcmpi(options.method, 'exact')
21 line_error(mfilename,'Exact class-dependent solver not yet available in NC.');
22end
23
24if sn_has_joint_dependence(sn) && strcmpi(options.method, 'exact')
25 line_error(mfilename,'Exact joint-dependent solver not yet available in NC.');
26end
27
28NK = sn.njobs'; % initial population per class
29
30if isinf(max(NK))
31 line_error(mfilename,'The load-dependent solver does not support open classes yet.');
32end
33
34sched = sn.sched;
35%chains = sn.chains;
36C = sn.nchains;
37SCV = sn.scv;
38gamma = zeros(M,1);
39V = cellsum(sn.visits);
40ST = 1 ./ sn.rates;
41ST(isnan(ST))=0;
42ST0=ST;
43lldscaling = sn.lldscaling;
44Nt = sum(NK(isfinite(NK)));
45if isempty(lldscaling)
46 lldscaling = ones(M,ceil(Nt));
47end
48
49[~,~,Vchain,alpha] = sn_get_demands_chain(sn);
50
51eta_1 = zeros(1,M);
52eta = ones(1,M);
53if all(sched~=SchedStrategy.FCFS) options.iter_max=1; end
54iter = 0;
55while max(abs(1-eta./eta_1)) > options.iter_tol & iter < options.iter_max
56 iter = iter + 1;
57 eta_1 = eta;
58 M = sn.nstations; %number of stations
59 K = sn.nclasses; %number of classes
60 C = sn.nchains;
61 Lchain = zeros(M,C);
62 STchain = zeros(M,C);
63
64 SCVchain = zeros(M,C);
65 Nchain = zeros(1,C);
66 refstatchain = zeros(C,1);
67 for c=1:C
68 inchain = sn.inchain{c};
69 isOpenChain = any(isinf(sn.njobs(inchain)));
70 for ist=1:M
71 % we assume that the visits in L(i,inchain) are equal to 1
72 Lchain(ist,c) = Vchain(ist,c) * ST(ist,inchain) * alpha(ist,inchain)';
73 STchain(ist,c) = ST(ist,inchain) * alpha(ist,inchain)';
74 if isOpenChain && ist == sn.refstat(inchain(1)) % if this is a source ST = 1 / arrival rates
75 STchain(ist,c) = sumfinite(ST(ist,inchain)); % ignore degenerate classes with zero arrival rates
76 else
77 STchain(ist,c) = ST(ist,inchain) * alpha(ist,inchain)';
78 end
79 SCVchain(ist,c) = SCV(ist,inchain) * alpha(ist,inchain)';
80 end
81 Nchain(c) = sum(NK(inchain));
82 refstatchain(c) = sn.refstat(inchain(1));
83 if any((sn.refstat(inchain(1))-refstatchain(c))~=0)
84 line_error(mfilename,sprintf('Classes in chain %d have different reference station.',c));
85 end
86 end
87 STchain(~isfinite(STchain))=0;
88 Lchain(~isfinite(Lchain))=0;
89 Tstart = tic;
90 Nt = sum(Nchain(isfinite(Nchain)));
91
92 L = zeros(M,C);
93 mu = zeros(M,ceil(Nt));
94 infServers = [];
95 Z = zeros(M,C);
96 for ist=1:M
97 if isinf(nservers(ist)) % infinite server
98 %mu_chain(i,1:sum(Nchain)) = 1:sum(Nchain);
99 infServers(end+1) = ist;
100 L(ist,:) = Lchain(ist,:);
101 Z(ist,:) = Lchain(ist,:);
102 mu(ist,1:Nt) = 1:Nt;
103 else
104 if strcmpi(options.method,'exact') && nservers(ist)>1
105 %options.method = 'default';
106 line_warning(mfilename,sprintf('%s does not support exact multiserver yet. Switching to approximate method.\n', 'SolverNC'));
107 end
108 L(ist,:) = Lchain(ist,:);
109 mu(ist,1:Nt) = lldscaling(ist,1:Nt);
110 end
111 end
112 Qchain = zeros(M,C);
113 % Solve original system
114 [lG,~,method] = pfqn_ncld(L, Nchain, 0*Nchain, mu, options);
115 lG = real(lG);
116 Xchain=[];
117
118 % Solve systems with a job less
119 if isempty(Xchain)
120 for r=1:C
121 Nchain_r =oner(Nchain,r);
122 lGr(r) = pfqn_ncld(L,Nchain_r,0*Nchain,mu,options);
123 lGr = real(lGr);
124 Xchain(r) = exp(lGr(r) - lG);
125 for ist=1:M
126 Qchain(ist,r)=0;
127 end
128 CQchain_r = zeros(M,1);
129
130 if M==2 && any(isinf(sn.nservers)) % repairmen model
131 firstDelay = find(isinf(sn.nservers),1);
132 Qchain(firstDelay,r) = real(Lchain(firstDelay,r) * Xchain(r));
133 Qchain(setdiff(1:M,firstDelay),r) = Nchain(r) - real(Lchain(firstDelay,r) * Xchain(r));
134 else
135 % Add queue replicas for queue-length
136 for ist=1:M
137 Lms_i = L; Lms_i(ist,:) = [];
138 mu_i = mu; mu_i(ist,:) = [];
139 muhati = mu; muhati = pfqn_mushift(mu,ist); %#ok<NASGU>
140 [muhati_f,c] = pfqn_fnc(muhati(ist,:));
141 if Lchain(ist,r)>0
142 if isinf(nservers(ist)) % infinite server
143 Qchain(ist,r) = real(Lchain(ist,r) * Xchain(r));
144 else
145 if ist==M && sum(isfinite(nservers))==1 % normalize queue-lengths to Nchain(r)
146 Qchain(ist,r) = max(0,real(Nchain(r) - sum(Lchain(isinf(nservers),r)) * Xchain(r)) - sum(Qchain(setdiff(1:(M-1),find(isinf(nservers))),r)));
147 else
148 [lGhat_fnci(r)] = pfqn_ncld([L;L(ist,:)],Nchain_r, 0*Nchain, [muhati;muhati_f], options);
149 [lGhatir(r)] = pfqn_ncld(L,Nchain_r, 0*Nchain, muhati, options);
150 [lGr_i(r)] = pfqn_ncld(Lms_i,Nchain_r, 0*Nchain, mu_i, options);
151 [lGhati(r)] = pfqn_ncld(L,Nchain_r, 0*Nchain, muhati, options);
152 dlGa = real(lGhat_fnci(r)) - real(lGhatir(r));
153 dlG_i = real(lGr_i(r)) - real(lGhatir(r));
154 CQchain(ist) = (exp(dlGa) - 1) + c*(exp(dlG_i)-1); % conditional qlen
155 ldDemand(ist,r) = log(L(ist,r)) + real(lGhati(r)) - log(mu(ist,1)) - real(lGr(r));
156 Qchain(ist,r) = exp(ldDemand(ist,r)) * Xchain(r) * (1+CQchain(ist)); % conditional MVA formula
157 end
158 end
159 end
160 end
161 end
162 end
163
164 else
165 % just fill the delay servers
166 for r=1:C
167 for ist=1:M
168 if Lchain(ist,r)>0
169 if isinf(nservers(ist)) % infinite server
170 Qchain(ist,r) = Lchain(ist,r) * Xchain(r);
171 end
172 end
173 end
174 end
175 end
176
177 if isnan(Xchain)
178 line_warning(mfilename,'Normalizing constant computations produced a floating-point range exception. Model is likely too large.\n');
179 end
180
181 Z = sum(Z(1:M,:),1);
182
183 Rchain = Qchain ./ repmat(Xchain,M,1) ./ Vchain;
184 Rchain(infServers,:) = Lchain(infServers,:) ./ Vchain(infServers,:);
185 Tchain = repmat(Xchain,M,1) .* Vchain;
186 Uchain = Tchain .* Lchain;
187 Cchain = Nchain ./ Xchain - Z;
188
189 Xchain=real(Xchain);
190 Uchain=real(Uchain);
191 Qchain=real(Qchain);
192 Rchain=real(Rchain);
193
194 Xchain(~isfinite(Xchain))=0;
195 Uchain(~isfinite(Uchain))=0;
196 Qchain(~isfinite(Qchain))=0;
197 Rchain(~isfinite(Rchain))=0;
198
199 Xchain(Nchain==0)=0;
200 Uchain(:,Nchain==0)=0;
201 Qchain(:,Nchain==0)=0;
202 Rchain(:,Nchain==0)=0;
203 Tchain(:,Nchain==0)=0;
204
205 [Q,U,R,T,C,X] = sn_deaggregate_chain_results(sn, Lchain, ST, STchain, Vchain, alpha, [], [], Rchain, Tchain, [], Xchain);
206
207 [ST,gamma,~,~,~,~,eta] = npfqn_nonexp_approx(options.config.highvar,sn,ST0,V,SCV,T,U,gamma,nservers);
208end
209
210
211[lambda,L]= sn_get_product_form_params(sn);
212runtime = toc(Tstart);
213Q=abs(Q); R=abs(R); X=abs(X); U=abs(U);
214for ist=1:M
215 if sn.nservers(ist)>1 && sn.nservers(ist)<Inf
216 openClasses = find(isinf(NK));
217 closedClasses = setdiff(1:K, openClasses);
218 for r=closedClasses
219 c = find(sn.chains(:,r));
220 if X(r) > 0
221 U(ist,r) = X(r) * sn.visits{c}(ist,r) / sn.visits{c}(sn.refstat(r),r) * ST(ist,r)/sn.nservers(ist);
222 end
223 end
224 for r=openClasses
225 c = find(sn.chains(:,r));
226 if lambda(r)>0
227 U(ist,r) = lambda(r) * sn.visits{c}(ist,r) / sn.visits{c}(sn.refstat(r),r) * ST(ist,r)/sn.nservers(ist);
228 end
229 end
230 elseif isinf(sn.nservers(ist))
231 openClasses = find(isinf(NK));
232 closedClasses = setdiff(1:K, openClasses);
233 for r=closedClasses
234 if X(r) > 0
235 c = find(sn.chains(:,r));
236 U(ist,r) = X(r) * sn.visits{c}(ist,r) / sn.visits{c}(sn.refstat(r),r) * ST(ist,r);
237 end
238 end
239 for r=openClasses
240 if lambda(r)>0
241 c = find(sn.chains(:,r));
242 U(ist,r) = lambda(r) * sn.visits{c}(ist,r) / sn.visits{c}(sn.refstat(r),r) * ST(ist,r);
243 end
244 end
245 else
246 U(ist,:) = U(ist,:) / max(lldscaling(ist,:));
247 if sum(U(ist,:)) > 1
248 U(ist,:) = U(ist,:) / sum(U(ist,:),"omitnan");
249 end
250 end
251end
252
253X(~isfinite(X))=0; U(~isfinite(U))=0; Q(~isfinite(Q))=0; R(~isfinite(R))=0;
254
255% renormalize qlen and tput to correct for unforeseen population constraint deviations
256for c=1:sn.nchains
257 inchain = sn.inchain{c};
258 Nchain(c) = sum(NK(inchain)); %#ok<FNDSB>
259 if isfinite(Nchain(c))
260 q_den = sum(sum(Q(:,inchain)));
261 if q_den > 0
262 ratio = Nchain(c)/ q_den;
263 else
264 ratio = 0;
265 end
266 Q(:,inchain) = ratio * Q(:,inchain);
267 X(inchain) = ratio * X(inchain);
268 T(:,inchain) = ratio * T(:,inchain);
269 U(:,inchain) = ratio * U(:,inchain);
270 R(:,inchain) = Q(:,inchain) ./ T(:,inchain);
271 end
272end
273end