LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
mapqn_bnd_lr_mva.m
1function result = mapqn_bnd_lr_mva(params, objective_queue, objective_level, sense)
2% MAPQN_BND_LR_MVA - MVA-based Linear Reduction Bounds for MAP Queueing Networks
3%
4% MATLAB port of bnd_lr_mva.py
5%
6% Usage:
7% result = mapqn_bnd_lr_mva(params)
8% result = mapqn_bnd_lr_mva(params, objective_queue)
9% result = mapqn_bnd_lr_mva(params, objective_queue, objective_level)
10% result = mapqn_bnd_lr_mva(params, objective_queue, objective_level, sense)
11%
12% Inputs:
13% params - Structure with model parameters:
14% .M - Number of queues
15% .N - Total population
16% .K - Number of levels (scalar integer)
17% .muM - [M-1 x 1] service rates for non-MAP queues 1..M-1
18% .muMAP - [K x K] service rate matrix for MAP queue (queue M)
19% .r - [M x M] routing probability matrix
20% .v - [K x K] level change rate matrix
21% .verbose - (optional) boolean
22%
23% objective_queue - (optional) 1-based queue index, default 1
24% objective_level - (optional) 1-based level index, default 1
25% sense - (optional) 'min' or 'max', default 'max'
26%
27% Outputs:
28% result - Structure with results:
29% .objective - Objective function value
30% .exitflag - Solver exit flag
31% .UN - [M x K] utilization matrix
32% .QN - [M x K] queue length matrix
33
34 if nargin < 2 || isempty(objective_queue)
35 objective_queue = 1;
36 end
37 if nargin < 3 || isempty(objective_level)
38 objective_level = 1;
39 end
40 if nargin < 4 || isempty(sense)
41 sense = 'max';
42 end
43
44 % Extract parameters
45 M = params.M;
46 N = params.N;
47 K = params.K;
48 muM = params.muM(:);
49 muMAP = params.muMAP;
50 r = params.r;
51 v = params.v;
52 if isfield(params, 'verbose')
53 verbose = params.verbose;
54 else
55 verbose = true;
56 end
57
58 %% q function (all indices 1-based)
59 % q(i,j,k,h) computes transition rate from queue i to queue j
60 % at level k going to level h
61 function val = q(i, j, k, h)
62 if i < M
63 % Non-MAP queue: scalar service rate
64 if k == h
65 val = r(i,j) * muM(i);
66 else
67 val = 0;
68 end
69 else
70 % MAP queue (i == M)
71 if j < M
72 val = r(M,j) * muMAP(k,h);
73 else
74 % j == M
75 if k ~= h
76 val = v(k,h) + r(M,M) * muMAP(k,h);
77 else
78 val = 0;
79 end
80 end
81 end
82 end
83
84 %% Build variable indexing
85 % Variables are laid out as:
86 % UN(i,k) for i=1..M, k=1..K -> nVarsUN = M*K
87 % QN(i,k) for i=1..M, k=1..K -> nVarsQN = M*K
88 % B(j,k,i) for j=1..M, k=1..K, i=1..M -> nVarsB = M*K*M
89
90 nVarsUN = M * K;
91 nVarsQN = M * K;
92 nVarsB = M * K * M;
93 nVars = nVarsUN + nVarsQN + nVarsB;
94
95 % Index functions (1-based)
96 getUNIdx = @(i, k) (i - 1) * K + k;
97 getQNIdx = @(i, k) nVarsUN + (i - 1) * K + k;
98 getBIdx = @(j, k, i) nVarsUN + nVarsQN + (j - 1) * K * M + (k - 1) * M + i;
99
100 if verbose; fprintf('Total variables: %d (UN=%d, QN=%d, B=%d)\n', nVars, nVarsUN, nVarsQN, nVarsB); end
101
102 %% Initialize bounds
103 lb = zeros(nVars, 1);
104 ub = zeros(nVars, 1);
105
106 % UN bounds: 0 <= UN(i,k) <= 1
107 for i = 1:M
108 for k = 1:K
109 ub(getUNIdx(i, k)) = 1;
110 end
111 end
112
113 % QN bounds: 0 <= QN(i,k) <= N
114 for i = 1:M
115 for k = 1:K
116 ub(getQNIdx(i, k)) = N;
117 end
118 end
119
120 % B bounds: 0 <= B(j,k,i) <= N
121 for j = 1:M
122 for k = 1:K
123 for i = 1:M
124 ub(getBIdx(j, k, i)) = N;
125 end
126 end
127 end
128
129 %% Build constraints using sparse triplets
130 % We collect row, col, val triplets for equality and inequality matrices
131 eq_rows = [];
132 eq_cols = [];
133 eq_vals = [];
134 beq = [];
135 nEq = 0;
136
137 ineq_rows = [];
138 ineq_cols = [];
139 ineq_vals = [];
140 bineq = [];
141 nIneq = 0;
142
143 if verbose; fprintf('Building constraints...\n'); end
144
145 %% 1. QNB: QN(i,k) - B(j,k,i) >= 0 for all (i,k,j)
146 % Written as: -QN(i,k) + B(j,k,i) <= 0
147 if verbose; fprintf(' QNB constraints...\n'); end
148 for i = 1:M
149 for k = 1:K
150 for j = 1:M
151 nIneq = nIneq + 1;
152 ineq_rows = [ineq_rows; nIneq; nIneq];
153 ineq_cols = [ineq_cols; getQNIdx(i, k); getBIdx(j, k, i)];
154 ineq_vals = [ineq_vals; -1; 1];
155 bineq = [bineq; 0];
156 end
157 end
158 end
159
160 %% 2. UMAX: sum_k UN(i,k) <= 1 for each i
161 if verbose; fprintf(' UMAX constraints...\n'); end
162 for i = 1:M
163 nIneq = nIneq + 1;
164 for k = 1:K
165 ineq_rows = [ineq_rows; nIneq];
166 ineq_cols = [ineq_cols; getUNIdx(i, k)];
167 ineq_vals = [ineq_vals; 1];
168 end
169 bineq = [bineq; 1];
170 end
171
172 %% 3. POPCONSTR: sum over i,k of QN(i,k) = N
173 if verbose; fprintf(' POPCONSTR constraint...\n'); end
174 nEq = nEq + 1;
175 for i = 1:M
176 for k = 1:K
177 eq_rows = [eq_rows; nEq];
178 eq_cols = [eq_cols; getQNIdx(i, k)];
179 eq_vals = [eq_vals; 1];
180 end
181 end
182 beq = [beq; N];
183
184 %% 4. FLOW: for each i: sum over k,m,w of (q(w,i,k,m)*UN(w,k) - q(i,w,m,k)*UN(i,m)) = 0
185 if verbose; fprintf(' FLOW constraints...\n'); end
186 for i = 1:M
187 nEq = nEq + 1;
188 row_coeffs = zeros(1, nVars);
189 for k = 1:K
190 for m = 1:K
191 for w = 1:M
192 q_in = q(w, i, k, m);
193 q_out = q(i, w, m, k);
194 row_coeffs(getUNIdx(w, k)) = row_coeffs(getUNIdx(w, k)) + q_in;
195 row_coeffs(getUNIdx(i, m)) = row_coeffs(getUNIdx(i, m)) - q_out;
196 end
197 end
198 end
199 idx_nz = find(row_coeffs);
200 eq_rows = [eq_rows; nEq * ones(length(idx_nz), 1)];
201 eq_cols = [eq_cols; idx_nz(:)];
202 eq_vals = [eq_vals; row_coeffs(idx_nz)'];
203 beq = [beq; 0];
204 end
205
206 %% 5. UBAL: for each k: sum over h~=k,w of (q(M,w,k,h)*UN(M,k) - q(M,w,h,k)*UN(M,h)) = 0
207 if verbose; fprintf(' UBAL constraints...\n'); end
208 for k = 1:K
209 nEq = nEq + 1;
210 row_coeffs = zeros(1, nVars);
211 for h = 1:K
212 if h ~= k
213 for w = 1:M
214 q_out = q(M, w, k, h);
215 q_in = q(M, w, h, k);
216 row_coeffs(getUNIdx(M, k)) = row_coeffs(getUNIdx(M, k)) + q_out;
217 row_coeffs(getUNIdx(M, h)) = row_coeffs(getUNIdx(M, h)) - q_in;
218 end
219 end
220 end
221 idx_nz = find(row_coeffs);
222 eq_rows = [eq_rows; nEq * ones(length(idx_nz), 1)];
223 eq_cols = [eq_cols; idx_nz(:)];
224 eq_vals = [eq_vals; row_coeffs(idx_nz)'];
225 beq = [beq; 0];
226 end
227
228 %% 6. QBAL: for each k
229 if verbose; fprintf(' QBAL constraints...\n'); end
230 for k = 1:K
231 nEq = nEq + 1;
232 row_coeffs = zeros(1, nVars);
233
234 % sum over h~=k,w of q(M,w,k,h)*QN(M,k)
235 for h = 1:K
236 if h ~= k
237 for w = 1:M
238 qval = q(M, w, k, h);
239 row_coeffs(getQNIdx(M, k)) = row_coeffs(getQNIdx(M, k)) + qval;
240 end
241 end
242 end
243
244 % + sum over m,j<M of q(M,j,m,k)*UN(M,m)
245 for m = 1:K
246 for j = 1:(M-1)
247 qval = q(M, j, m, k);
248 row_coeffs(getUNIdx(M, m)) = row_coeffs(getUNIdx(M, m)) + qval;
249 end
250 end
251
252 % - sum over j<M of q(j,M,k,k)*UN(j,k)
253 for j = 1:(M-1)
254 qval = q(j, M, k, k);
255 row_coeffs(getUNIdx(j, k)) = row_coeffs(getUNIdx(j, k)) - qval;
256 end
257
258 % - sum over h~=k,w of q(M,w,h,k)*QN(M,h)
259 for h = 1:K
260 if h ~= k
261 for w = 1:M
262 qval = q(M, w, h, k);
263 row_coeffs(getQNIdx(M, h)) = row_coeffs(getQNIdx(M, h)) - qval;
264 end
265 end
266 end
267
268 idx_nz = find(row_coeffs);
269 eq_rows = [eq_rows; nEq * ones(length(idx_nz), 1)];
270 eq_cols = [eq_cols; idx_nz(:)];
271 eq_vals = [eq_vals; row_coeffs(idx_nz)'];
272 beq = [beq; 0];
273 end
274
275 %% 7. MCC: for each i
276 if verbose; fprintf(' MCC constraints...\n'); end
277 for i = 1:M
278 nEq = nEq + 1;
279 row_coeffs = zeros(1, nVars);
280
281 % sum over k,m,w~=i of q(i,w,k,m)*QN(i,k)
282 for k = 1:K
283 for m = 1:K
284 for w = 1:M
285 if w ~= i
286 qval = q(i, w, k, m);
287 row_coeffs(getQNIdx(i, k)) = row_coeffs(getQNIdx(i, k)) + qval;
288 end
289 end
290 end
291 end
292
293 % + sum over k,m,j~=i of q(j,i,k,m)*QN(j,k)
294 for k = 1:K
295 for m = 1:K
296 for j = 1:M
297 if j ~= i
298 qval = q(j, i, k, m);
299 row_coeffs(getQNIdx(j, k)) = row_coeffs(getQNIdx(j, k)) + qval;
300 end
301 end
302 end
303 end
304
305 % + sum over k,m,j~=i,wp~=i,wp~=j of q(j,i,k,m)*B(j,k,wp)
306 for k = 1:K
307 for m = 1:K
308 for j = 1:M
309 if j ~= i
310 qval = q(j, i, k, m);
311 for wp = 1:M
312 if wp ~= i && wp ~= j
313 row_coeffs(getBIdx(j, k, wp)) = row_coeffs(getBIdx(j, k, wp)) + qval;
314 end
315 end
316 end
317 end
318 end
319 end
320
321 % - (N+1)*sum over k,m,j~=i of q(j,i,k,m)*UN(j,k)
322 for k = 1:K
323 for m = 1:K
324 for j = 1:M
325 if j ~= i
326 qval = q(j, i, k, m);
327 row_coeffs(getUNIdx(j, k)) = row_coeffs(getUNIdx(j, k)) - (N + 1) * qval;
328 end
329 end
330 end
331 end
332
333 idx_nz = find(row_coeffs);
334 eq_rows = [eq_rows; nEq * ones(length(idx_nz), 1)];
335 eq_cols = [eq_cols; idx_nz(:)];
336 eq_vals = [eq_vals; row_coeffs(idx_nz)'];
337 beq = [beq; 0];
338 end
339
340 %% 8. MCC2: for each i
341 if verbose; fprintf(' MCC2 constraints...\n'); end
342 for i = 1:M
343 nEq = nEq + 1;
344 row_coeffs = zeros(1, nVars);
345
346 % sum over k,m,w~=i of q(i,w,k,m)*QN(i,k)
347 for k = 1:K
348 for m = 1:K
349 for w = 1:M
350 if w ~= i
351 qval = q(i, w, k, m);
352 row_coeffs(getQNIdx(i, k)) = row_coeffs(getQNIdx(i, k)) + qval;
353 end
354 end
355 end
356 end
357
358 % - sum over k,m,j~=i of q(j,i,k,m)*(B(j,k,i) + UN(j,k))
359 for k = 1:K
360 for m = 1:K
361 for j = 1:M
362 if j ~= i
363 qval = q(j, i, k, m);
364 row_coeffs(getBIdx(j, k, i)) = row_coeffs(getBIdx(j, k, i)) - qval;
365 row_coeffs(getUNIdx(j, k)) = row_coeffs(getUNIdx(j, k)) - qval;
366 end
367 end
368 end
369 end
370
371 idx_nz = find(row_coeffs);
372 eq_rows = [eq_rows; nEq * ones(length(idx_nz), 1)];
373 eq_cols = [eq_cols; idx_nz(:)];
374 eq_vals = [eq_vals; row_coeffs(idx_nz)'];
375 beq = [beq; 0];
376 end
377
378 %% 9. QMAX: QN(w,k) - N*UN(w,k) <= 0 for all (w,k)
379 if verbose; fprintf(' QMAX constraints...\n'); end
380 for w = 1:M
381 for k = 1:K
382 nIneq = nIneq + 1;
383 ineq_rows = [ineq_rows; nIneq; nIneq];
384 ineq_cols = [ineq_cols; getQNIdx(w, k); getUNIdx(w, k)];
385 ineq_vals = [ineq_vals; 1; -N];
386 bineq = [bineq; 0];
387 end
388 end
389
390 %% 10. QMIN: sum_w QN(w,k) - N*UN(j,k) >= 0 for each (k,j)
391 % Written as: -sum_w QN(w,k) + N*UN(j,k) <= 0
392 if verbose; fprintf(' QMIN constraints...\n'); end
393 for k = 1:K
394 for j = 1:M
395 nIneq = nIneq + 1;
396 for w = 1:M
397 ineq_rows = [ineq_rows; nIneq];
398 ineq_cols = [ineq_cols; getQNIdx(w, k)];
399 ineq_vals = [ineq_vals; -1];
400 end
401 ineq_rows = [ineq_rows; nIneq];
402 ineq_cols = [ineq_cols; getUNIdx(j, k)];
403 ineq_vals = [ineq_vals; N];
404 bineq = [bineq; 0];
405 end
406 end
407
408 %% Assemble sparse constraint matrices
409 if verbose; fprintf('Assembling sparse matrices...\n'); end
410
411 if nEq > 0
412 Aeq = sparse(eq_rows, eq_cols, eq_vals, nEq, nVars);
413 else
414 Aeq = sparse(0, nVars);
415 beq = [];
416 end
417
418 if nIneq > 0
419 Aineq = sparse(ineq_rows, ineq_cols, ineq_vals, nIneq, nVars);
420 else
421 Aineq = sparse(0, nVars);
422 bineq = [];
423 end
424
425 %% Build objective function
426 if verbose; fprintf('Building objective function...\n'); end
427 c = zeros(nVars, 1);
428 c(getUNIdx(objective_queue, objective_level)) = 1;
429
430 if strcmp(sense, 'max')
431 c = -c;
432 end
433
434 %% Solve LP
435 if verbose
436 fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
437 nVars, nEq, nIneq);
438 end
439
440 if verbose
441 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
442 else
443 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
444 end
445
446 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
447
448 if strcmp(sense, 'max')
449 fval = -fval;
450 end
451
452 %% Extract results
453 result = struct();
454 result.objective = fval;
455 result.exitflag = exitflag;
456
457 if exitflag > 0
458 % Extract UN and QN matrices
459 result.UN = zeros(M, K);
460 result.QN = zeros(M, K);
461
462 for i = 1:M
463 for k = 1:K
464 result.UN(i, k) = x(getUNIdx(i, k));
465 result.QN(i, k) = x(getQNIdx(i, k));
466 end
467 end
468 else
469 result.UN = [];
470 result.QN = [];
471 end
472
473 if verbose; fprintf('\n=== Results ===\n'); end
474 if verbose; fprintf('Objective value: %f\n', fval); end
475 if verbose; fprintf('Exit flag: %d\n', exitflag); end
476 if exitflag > 0 && verbose
477 fprintf('\nUtilizations (UN):\n');
478 for i = 1:M
479 fprintf(' Queue %d: [', i);
480 for k = 1:K
481 fprintf('%.6f ', result.UN(i, k));
482 end
483 fprintf(']\n');
484 end
485 fprintf('\nQueue lengths (QN):\n');
486 for i = 1:M
487 fprintf(' Queue %d: [', i);
488 for k = 1:K
489 fprintf('%.6f ', result.QN(i, k));
490 end
491 fprintf(']\n');
492 end
493 end
494end