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