LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_nc_mem.m
1function [QN,UN,RN,TN,CN,XN,totiter] = solver_nc_mem(sn, options)
2% [QN,UN,RN,TN,CN,XN,TOTITER] = SOLVER_NC_MEM(SN, OPTIONS)
3%
4% Maximum Entropy Method (MEM) for Open and Closed Queueing Networks
5%
6% Implements the ME algorithms from Kouvatsos (1994) for analyzing
7% queueing networks with general arrival and service processes under
8% non-priority scheduling disciplines. Open models (Section 3.2) are
9% decomposed into GE/GE/1, GE/GE/c and GE/GE/inf building blocks; closed
10% models (Section 3.3) are solved by the two-stage pseudo-open network
11% plus convolution algorithm on G/G/1 and G/G/inf building blocks; mixed
12% models compose the two algorithms by product-form-style conditioning
13% (open classes reduce the server capacity seen by the closed classes,
14% closed occupancy inflates the open queue lengths).
15%
17% sn - Network structure from getStruct()
18% options - Solver options with MEM-specific fields:
19% - config.mem_tol: convergence tolerance (default 1e-6)
20% - config.mem_maxiter: maximum iterations (default 1000)
21% - config.mem_verbose: print iteration info (default false)
22%
23% Returns:
24% QN - Mean queue lengths [M x R matrix]
25% UN - Utilizations [M x R matrix]
26% RN - Mean response times [M x R matrix]
27% TN - Throughputs [M x R matrix]
28% CN - System response times per class [1 x R], by Little's law
29% XN - System throughputs per class [1 x R]
30% totiter - Number of iterations until convergence
31%
32% Reference:
33% D.D. Kouvatsos, "Entropy Maximisation and Queueing Network Models",
34% Annals of Operations Research, 48:63-126, 1994.
35%
36% Copyright (c) 2012-2026, Imperial College London
37% All rights reserved.
38
39% Validate the model against the MEM feature set
40[memok, memreason] = solver_nc_mem_supports(sn);
41if ~memok
42 line_error(mfilename, memreason);
43end
44
45M = sn.nstations;
46R = sn.nclasses;
47
48% Get MEM options from config
49if isfield(options, 'config') && isfield(options.config, 'mem_tol')
50 tol = options.config.mem_tol;
51else
52 tol = 1e-6;
53end
54
55if isfield(options, 'config') && isfield(options.config, 'mem_maxiter')
56 maxiter = options.config.mem_maxiter;
57else
58 maxiter = 1000;
59end
60
61if isfield(options, 'config') && isfield(options.config, 'mem_verbose')
62 verbose = options.config.mem_verbose;
63else
64 verbose = false;
65end
66
67% Create MEM options structure
68mem_options = struct();
69mem_options.tol = tol;
70mem_options.maxiter = maxiter;
71mem_options.verbose = verbose;
72
73% Closed models: two-stage pseudo-open + convolution algorithm (Section 3.3)
74if sn_is_closed_model(sn)
75 N = zeros(1, R);
76 refstat = zeros(1, R);
77 for r = 1:R
78 N(r) = sn.njobs(r);
79 refstat(r) = sn.refstat(r);
80 end
81 mu = zeros(M, R);
82 Cs = ones(M, R);
83 nservers = ones(M, 1);
84 for ist = 1:M
85 nservers(ist) = sn.nservers(ist);
86 for r = 1:R
87 if isfinite(sn.rates(ist, r)) && sn.rates(ist, r) > 0
88 mu(ist, r) = sn.rates(ist, r);
89 if isfinite(sn.scv(ist, r)) && sn.scv(ist, r) > 0
90 Cs(ist, r) = sn.scv(ist, r);
91 end
92 end
93 end
94 end
95 % Routing probabilities between stations
96 P = zeros(M, M, R);
97 for r = 1:R
98 for j = 1:M
99 jNode = sn.stationToNode(j);
100 for k = 1:M
101 iNode = sn.stationToNode(k);
102 P(j, k, r) = sn.rtnodes((jNode-1)*R + r, (iNode-1)*R + r);
103 end
104 end
105 end
106 insens = false(M, 1);
107 for ist = 1:M
108 insens(ist) = any(sn.sched(ist) == [SchedStrategy.PS, SchedStrategy.LCFSPR]);
109 end
110 [L, W, ~, ~, lam, rho, X, totiter] = me_cqn(M, R, N, mu, Cs, P, nservers, refstat, insens, mem_options);
111 QN = L;
112 UN = rho;
113 RN = W;
114 TN = lam;
115 XN = X;
116 CN = zeros(1, R);
117 for r = 1:R
118 if X(r) > 0
119 CN(r) = N(r) / X(r); % class cycle time by Little's law
120 end
121 end
122 return
123end
124
125% Mixed models: composition of the open (Section 3.2) and closed
126% (Section 3.3) algorithms with product-form-style conditioning
127if ~sn_is_open_model(sn)
128 openCls = false(1, R);
129 N = zeros(1, R);
130 for r = 1:R
131 N(r) = sn.njobs(r);
132 openCls(r) = isinf(sn.njobs(r));
133 end
134 sourceIdx = 0;
135 for ind = 1:sn.nnodes
136 if sn.nodetype(ind) == NodeType.Source
137 sourceIdx = sn.nodeToStation(ind);
138 break;
139 end
140 end
141 qs = setdiff(1:M, sourceIdx); % queueing/delay stations
142 Mq = length(qs);
143 mu = zeros(Mq, R);
144 Cs = ones(Mq, R);
145 nservers = ones(Mq, 1);
146 refstat = zeros(1, R);
147 for k = 1:Mq
148 ist = qs(k);
149 nservers(k) = sn.nservers(ist);
150 for r = 1:R
151 if isfinite(sn.rates(ist, r)) && sn.rates(ist, r) > 0
152 mu(k, r) = sn.rates(ist, r);
153 if isfinite(sn.scv(ist, r)) && sn.scv(ist, r) > 0
154 Cs(k, r) = sn.scv(ist, r);
155 end
156 end
157 end
158 end
159 for r = 1:R
160 if ~openCls(r)
161 refstat(r) = find(qs == sn.refstat(r), 1);
162 end
163 end
164 % External arrivals of the open classes along the source routing
165 lambda0 = zeros(Mq, R);
166 Ca0 = zeros(Mq, R);
167 sourceNode = sn.stationToNode(sourceIdx);
168 for r = 1:R
169 if openCls(r) && isfinite(sn.rates(sourceIdx, r)) && sn.rates(sourceIdx, r) > 0
170 extRate = sn.rates(sourceIdx, r);
171 Ca_ext = 1.0;
172 if isfinite(sn.scv(sourceIdx, r)) && sn.scv(sourceIdx, r) > 0
173 Ca_ext = sn.scv(sourceIdx, r);
174 end
175 for k = 1:Mq
176 destNode = sn.stationToNode(qs(k));
177 routeProb = sn.rtnodes((sourceNode-1)*R + r, (destNode-1)*R + r);
178 if routeProb > 0
179 lambda0(k, r) = extRate * routeProb;
180 Ca0(k, r) = Ca_ext;
181 end
182 end
183 end
184 end
185 % Routing probabilities between queueing stations
186 P = zeros(Mq, Mq, R);
187 for r = 1:R
188 for j = 1:Mq
189 jNode = sn.stationToNode(qs(j));
190 for k = 1:Mq
191 iNode = sn.stationToNode(qs(k));
192 P(j, k, r) = sn.rtnodes((jNode-1)*R + r, (iNode-1)*R + r);
193 end
194 end
195 end
196 insens = false(Mq, 1);
197 for k = 1:Mq
198 insens(k) = any(sn.sched(qs(k)) == [SchedStrategy.PS, SchedStrategy.LCFSPR]);
199 end
200 [L, W, ~, ~, lam, rho, X, totiter] = me_mqn(Mq, R, openCls, lambda0, Ca0, N, mu, Cs, P, nservers, refstat, insens, mem_options);
201 QN = zeros(M, R);
202 UN = zeros(M, R);
203 RN = zeros(M, R);
204 TN = zeros(M, R);
205 QN(qs, :) = L;
206 UN(qs, :) = rho;
207 RN(qs, :) = W;
208 TN(qs, :) = lam;
209 % Cap utilization of unstable stations at 1 (LINE convention)
210 for k = 1:Mq
211 ist = qs(k);
212 if isfinite(sn.nservers(ist))
213 Utot = sum(UN(ist, :));
214 if Utot > 1
215 UN(ist, :) = UN(ist, :) / Utot;
216 end
217 end
218 end
219 XN = zeros(1, R);
220 CN = zeros(1, R);
221 for r = 1:R
222 if openCls(r)
223 TN(sourceIdx, r) = X(r);
224 XN(r) = X(r);
225 if X(r) > 0
226 CN(r) = sum(QN(qs, r)) / X(r);
227 end
228 else
229 XN(r) = X(r);
230 if X(r) > 0
231 CN(r) = N(r) / X(r); % class cycle time
232 end
233 end
234 end
235 return
236end
237
238% Locate the source station (external arrivals)
239sourceIdx = 0;
240for ind = 1:sn.nnodes
241 if sn.nodetype(ind) == NodeType.Source
242 sourceIdx = sn.nodeToStation(ind);
243 break;
244 end
245end
246if sourceIdx <= 0
247 line_error(mfilename, 'MEM requires a Source node.');
248end
249
250qs = setdiff(1:M, sourceIdx); % queueing/delay stations
251Mq = length(qs);
252
253% Service rates, scvs and server counts (rates/scv are NaN for classes
254% not served at a station)
255mu = zeros(Mq, R);
256Cs = ones(Mq, R);
257nservers = ones(Mq, 1);
258for k = 1:Mq
259 ist = qs(k);
260 nservers(k) = sn.nservers(ist);
261 for r = 1:R
262 if isfinite(sn.rates(ist, r)) && sn.rates(ist, r) > 0
263 mu(k, r) = sn.rates(ist, r);
264 if isfinite(sn.scv(ist, r)) && sn.scv(ist, r) > 0
265 Cs(k, r) = sn.scv(ist, r);
266 end
267 end
268 end
269end
270
271% External arrivals: distribute the source output along its routing
272lambda0 = zeros(Mq, R);
273Ca0 = zeros(Mq, R);
274sourceNode = sn.stationToNode(sourceIdx);
275for r = 1:R
276 if isfinite(sn.rates(sourceIdx, r)) && sn.rates(sourceIdx, r) > 0
277 extRate = sn.rates(sourceIdx, r);
278 Ca_ext = 1.0;
279 if isfinite(sn.scv(sourceIdx, r)) && sn.scv(sourceIdx, r) > 0
280 Ca_ext = sn.scv(sourceIdx, r);
281 end
282 for k = 1:Mq
283 destNode = sn.stationToNode(qs(k));
284 routeProb = sn.rtnodes((sourceNode-1)*R + r, (destNode-1)*R + r);
285 if routeProb > 0
286 lambda0(k, r) = extRate * routeProb;
287 Ca0(k, r) = Ca_ext;
288 end
289 end
290 end
291end
292
293% Routing probabilities between queueing stations
294P = zeros(Mq, Mq, R);
295for r = 1:R
296 for j = 1:Mq
297 jNode = sn.stationToNode(qs(j));
298 for k = 1:Mq
299 iNode = sn.stationToNode(qs(k));
300 P(j, k, r) = sn.rtnodes((jNode-1)*R + r, (iNode-1)*R + r);
301 end
302 end
303end
304
305% Run the Maximum Entropy fixed-point algorithm
306insens = false(Mq, 1);
307for k = 1:Mq
308 insens(k) = any(sn.sched(qs(k)) == [SchedStrategy.PS, SchedStrategy.LCFSPR]);
309end
310[L, W, ~, ~, lam, rho, totiter] = me_oqn(Mq, R, lambda0, Ca0, mu, Cs, P, nservers, insens, mem_options);
311
312% Map results back to station-indexed LINE outputs
313QN = zeros(M, R);
314UN = zeros(M, R);
315RN = zeros(M, R);
316TN = zeros(M, R);
317QN(qs, :) = L;
318UN(qs, :) = rho;
319RN(qs, :) = W;
320TN(qs, :) = lam;
321
322% Cap utilization of unstable stations at 1 (LINE convention)
323for k = 1:Mq
324 ist = qs(k);
325 if isfinite(sn.nservers(ist))
326 Utot = sum(UN(ist, :));
327 if Utot > 1
328 UN(ist, :) = UN(ist, :) / Utot;
329 end
330 end
331end
332
333% Source station: report the external arrival rates as throughputs and
334% derive system metrics by Little's law
335XN = zeros(1, R);
336CN = zeros(1, R);
337for r = 1:R
338 if isfinite(sn.rates(sourceIdx, r)) && sn.rates(sourceIdx, r) > 0
339 TN(sourceIdx, r) = sn.rates(sourceIdx, r);
340 XN(r) = sn.rates(sourceIdx, r);
341 CN(r) = sum(QN(qs, r)) / XN(r);
342 end
343end
344
345end
Definition Station.m:245