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
4% MATLAB port of bnd_lr_mva.py
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)
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
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'
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
34 if nargin < 2 || isempty(objective_queue)
37 if nargin < 3 || isempty(objective_level)
40 if nargin < 4 || isempty(sense)
52 if isfield(params,
'verbose')
53 verbose = params.verbose;
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)
63 % Non-MAP queue: scalar service rate
65 val = r(i,j) * muM(i);
72 val = r(M,j) * muMAP(k,h);
76 val = v(k,h) + r(M,M) * muMAP(k,h);
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
93 nVars = nVarsUN + nVarsQN + nVarsB;
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;
100 if verbose; fprintf('Total variables: %d (UN=%d, QN=%d, B=%d)\n', nVars, nVarsUN, nVarsQN, nVarsB); end
103 lb = zeros(nVars, 1);
104 ub = zeros(nVars, 1);
106 % UN bounds: 0 <= UN(i,k) <= 1
109 ub(getUNIdx(i, k)) = 1;
113 % QN bounds: 0 <= QN(i,k) <= N
116 ub(getQNIdx(i, k)) = N;
120 % B bounds: 0 <= B(j,k,i) <= N
124 ub(getBIdx(j, k, i)) = N;
129 %% Build constraints using sparse triplets
130 % We collect row, col, val triplets for equality and inequality matrices
143 if verbose; fprintf('Building constraints...\n'); end
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
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];
160 %% 2. UMAX: sum_k UN(i,k) <= 1 for each i
161 if verbose; fprintf(' UMAX constraints...\n'); end
165 ineq_rows = [ineq_rows; nIneq];
166 ineq_cols = [ineq_cols; getUNIdx(i, k)];
167 ineq_vals = [ineq_vals; 1];
172 %% 3. POPCONSTR: sum over i,k of QN(i,k) = N
173 if verbose; fprintf(' POPCONSTR constraint...\n'); end
177 eq_rows = [eq_rows; nEq];
178 eq_cols = [eq_cols; getQNIdx(i, k)];
179 eq_vals = [eq_vals; 1];
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
188 row_coeffs = zeros(1, nVars);
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;
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)'];
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
210 row_coeffs = zeros(1, nVars);
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;
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)'];
228 %% 6. QBAL: for each k
229 if verbose; fprintf(' QBAL constraints...\n'); end
232 row_coeffs = zeros(1, nVars);
234 % sum over h~=k,w of q(M,w,k,h)*QN(M,k)
238 qval = q(M, w, k, h);
239 row_coeffs(getQNIdx(M, k)) = row_coeffs(getQNIdx(M, k)) + qval;
244 % + sum over m,j<M of q(M,j,m,k)*UN(M,m)
247 qval = q(M, j, m, k);
248 row_coeffs(getUNIdx(M, m)) = row_coeffs(getUNIdx(M, m)) + qval;
252 % - sum over j<M of q(j,M,k,k)*UN(j,k)
254 qval = q(j, M, k, k);
255 row_coeffs(getUNIdx(j, k)) = row_coeffs(getUNIdx(j, k)) - qval;
258 % - sum over h~=k,w of q(M,w,h,k)*QN(M,h)
262 qval = q(M, w, h, k);
263 row_coeffs(getQNIdx(M, h)) = row_coeffs(getQNIdx(M, h)) - qval;
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)'];
275 %% 7. MCC: for each i
276 if verbose; fprintf(' MCC constraints...\n'); end
279 row_coeffs = zeros(1, nVars);
281 % sum over k,m,w~=i of q(i,w,k,m)*QN(i,k)
286 qval = q(i, w, k, m);
287 row_coeffs(getQNIdx(i, k)) = row_coeffs(getQNIdx(i, k)) + qval;
293 % + sum over k,m,j~=i of q(j,i,k,m)*QN(j,k)
298 qval = q(j, i, k, m);
299 row_coeffs(getQNIdx(j, k)) = row_coeffs(getQNIdx(j, k)) + qval;
305 % + sum over k,m,j~=i,wp~=i,wp~=j of q(j,i,k,m)*B(j,k,wp)
310 qval = q(j, i, k, m);
312 if wp ~= i && wp ~= j
313 row_coeffs(getBIdx(j, k, wp)) = row_coeffs(getBIdx(j, k, wp)) + qval;
321 % - (N+1)*sum over k,m,j~=i of q(j,i,k,m)*UN(j,k)
326 qval = q(j, i, k, m);
327 row_coeffs(getUNIdx(j, k)) = row_coeffs(getUNIdx(j, k)) - (N + 1) * qval;
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)'];
340 %% 8. MCC2: for each i
341 if verbose; fprintf(' MCC2 constraints...\n'); end
344 row_coeffs = zeros(1, nVars);
346 % sum over k,m,w~=i of q(i,w,k,m)*QN(i,k)
351 qval = q(i, w, k, m);
352 row_coeffs(getQNIdx(i, k)) = row_coeffs(getQNIdx(i, k)) + qval;
358 % - sum over k,m,j~=i of q(j,i,k,m)*(B(j,k,i) + UN(j,k))
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;
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)'];
378 %% 9. QMAX: QN(w,k) - N*UN(w,k) <= 0 for all (w,k)
379 if verbose; fprintf(' QMAX constraints...\n'); end
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];
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
397 ineq_rows = [ineq_rows; nIneq];
398 ineq_cols = [ineq_cols; getQNIdx(w, k)];
399 ineq_vals = [ineq_vals; -1];
401 ineq_rows = [ineq_rows; nIneq];
402 ineq_cols = [ineq_cols; getUNIdx(j, k)];
403 ineq_vals = [ineq_vals; N];
408 %% Assemble sparse constraint matrices
409 if verbose; fprintf('Assembling sparse matrices...\n'); end
412 Aeq = sparse(eq_rows, eq_cols, eq_vals, nEq, nVars);
414 Aeq = sparse(0, nVars);
419 Aineq = sparse(ineq_rows, ineq_cols, ineq_vals, nIneq, nVars);
421 Aineq = sparse(0, nVars);
425 %% Build objective function
426 if verbose; fprintf('Building objective function...\n'); end
428 c(getUNIdx(objective_queue, objective_level)) = 1;
430 if strcmp(sense, 'max')
436 fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
441 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
443 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
446 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
448 if strcmp(sense, 'max')
454 result.objective = fval;
455 result.exitflag = exitflag;
458 % Extract UN and QN matrices
459 result.UN = zeros(M, K);
460 result.QN = zeros(M, K);
464 result.UN(i, k) = x(getUNIdx(i, k));
465 result.QN(i, k) = x(getQNIdx(i, k));
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');
479 fprintf(' Queue %d: [', i);
481 fprintf('%.6f ', result.UN(i, k));
485 fprintf('\nQueue lengths (QN):\n');
487 fprintf(' Queue %d: [', i);
489 fprintf('%.6f ', result.QN(i, k));