1function [result, x, fval, exitflag] = mapqn_bnd_lr(params, objective_queue, objective_phase, sense)
2% MAPQN_BND_LR - General Linear Reduction Bounds
for MAP Queueing Networks
4% MATLAB port of the Python file bnd_lr.py
7% [result, x, fval, exitflag] = mapqn_bnd_lr(params)
8% [result, x, fval, exitflag] = mapqn_bnd_lr(params, objective_queue)
9% [result, x, fval, exitflag] = mapqn_bnd_lr(params, objective_queue, objective_phase)
10% [result, x, fval, exitflag] = mapqn_bnd_lr(params, objective_queue, objective_phase, sense)
13% params - Structure with model parameters:
14% .M - Number of queues
15% .N - Total population
16% .K - [M x 1] Number of phases
for each queue
17% .mu - {M x 1} cell, each mu{i}
is K(i) x K(i) completion rates
18% .v - {M x 1} cell, each v{i}
is K(i) x K(i) background rates
19% .r - [M x M] Routing probabilities
20% .verbose - (optional)
boolean,
default true
22% objective_queue - (optional) Queue index to optimize (1-based),
default 1
23% objective_phase - (optional) Phase index to optimize (1-based),
default 1
24% sense - (optional)
'min' or
'max',
default 'max'
27% result - Structure with results:
28% .objective - Objective function value
29% .exitflag - Solver exit flag
30% .U - [M x max(K)] Utilization matrix
31% .IT - [M x max(K)] Idle time matrix
32% .Q - [M x max(K)] Queue length matrix
33% x - Raw solution vector
34% fval - Objective function value
35% exitflag - Solver exit flag
37 if nargin < 2 || isempty(objective_queue)
40 if nargin < 3 || isempty(objective_phase)
43 if nargin < 4 || isempty(sense)
54 if isfield(params,
'verbose')
55 verbose = params.verbose;
62 % Compute transition rates q{i,j}(k,h)
66 q{i,j} = zeros(K(i), K(i));
70 q{i,j}(ki, hi) = r(i,j) * mu{i}(ki, hi);
72 q{i,j}(ki, hi) = v{i}(ki, hi) + r(i,i) * mu{i}(ki, hi);
79 %% Build variable indexing
80 if verbose; fprintf(
'Building variable index map...\n'); end
83 % U(i,k) variables: utilization at queue i, phase k
84 Uidx = zeros(M, maxK);
87 varCount = varCount + 1;
88 Uidx(i, k) = varCount;
92 % IT(i,k) variables: idle time at queue i, phase k
93 ITidx = zeros(M, maxK);
96 varCount = varCount + 1;
97 ITidx(i, k) = varCount;
101 % Q(i,k) variables: mean queue length at queue i, phase k
102 Qidx = zeros(M, maxK);
105 varCount = varCount + 1;
106 Qidx(i, k) = varCount;
110 % UP(j,kj,i,hi) variables: utilization products
111 UPidx = zeros(M, maxK, M, maxK);
116 varCount = varCount + 1;
117 UPidx(j, kj, i, hi) = varCount;
123 % QP(j,kj,i,hi) variables: queue-length products
124 QPidx = zeros(M, maxK, M, maxK);
129 varCount = varCount + 1;
130 QPidx(j, kj, i, hi) = varCount;
136 % C(j,kj,i) variables: conditional queue lengths
137 Cidx = zeros(M, maxK, M);
141 varCount = varCount + 1;
142 Cidx(j, kj, i) = varCount;
147 % I_var(j,kj,i) variables: conditional idle lengths
148 Iidx = zeros(M, maxK, M);
152 varCount = varCount + 1;
153 Iidx(j, kj, i) = varCount;
158 % p1(j,kj,i,ni,hi) variables: marginal probabilities, ni from 0 to N
159 % Stored as p1idx(j, kj, i, ni+1, hi)
160 p1idx = zeros(M, maxK, M, N+1, maxK);
166 varCount = varCount + 1;
167 p1idx(j, kj, i, ni+1, hi) = varCount;
174 % p1c(j,kj,i,ni,hi) variables: complementary marginal probabilities, ni from 0 to N
175 % Stored as p1cidx(j, kj, i, ni+1, hi)
176 p1cidx = zeros(M, maxK, M, N+1, maxK);
182 varCount = varCount + 1;
183 p1cidx(j, kj, i, ni+1, hi) = varCount;
191 if verbose; fprintf(
'Total variables: %d\n', nVars); end
194 lb = zeros(nVars, 1);
197 % Set upper bounds
for each variable type
209 ub(UPidx(j, kj, i, hi)) = 1;
210 ub(QPidx(j, kj, i, hi)) = N;
218 ub(Cidx(j, kj, i)) = N;
219 ub(Iidx(j, kj, i)) = N;
228 ub(p1idx(j, kj, i, ni+1, hi)) = 1;
229 ub(p1cidx(j, kj, i, ni+1, hi)) = 1;
242 if verbose; fprintf(
'Building constraints...\n'); end
244 %% ZER1: p1(j,k,j,0,k) = 0
for all j,k
245 % Implemented via upper bounds
246 if verbose; fprintf(
' ZER1 constraints...\n'); end
249 idx = p1idx(j, k, j, 0+1, k);
254 %% ZER2: p1(j,k,j,nj,h) = 0
for h ~= k, all j,k,nj
255 if verbose; fprintf(
' ZER2 constraints...\n'); end
261 idx = p1idx(j, k, j, nj+1, h);
269 %% ZER3: p1(j,k,i,N,h) = 0
for j ~= i, all j,k,i,h
270 if verbose; fprintf(
' ZER3 constraints...\n'); end
276 idx = p1idx(j, k, i, N+1, h);
284 %% ZER4: p1c(j,k,j,nj,h) = 0
for nj >= 1, all j,k,nj,h
285 if verbose; fprintf(
' ZER4 constraints...\n'); end
290 idx = p1cidx(j, k, j, nj+1, h);
297 %% CEQU: C(j,k,j) = Q(j,k)
for all j,k
298 if verbose; fprintf(
' CEQU constraints...\n'); end
301 row = zeros(1, nVars);
302 row(Cidx(j, k, j)) = 1;
303 row(Qidx(j, k)) = -1;
309 %% ONE1: sum over kj,hi,ni of (p1 + p1c) = 1
for each (j,i)
310 if verbose; fprintf(
' ONE1 constraints...\n'); end
313 row = zeros(1, nVars);
317 row(p1idx(j, kj, i, ni+1, hi)) = 1;
318 row(p1cidx(j, kj, i, ni+1, hi)) = 1;
327 %% UTLB: U(i,k) = sum over t,nt,h of p1(i,k,t,nt,h)
for each (i,k,t)
328 if verbose; fprintf(
' UTLB constraints...\n'); end
332 row = zeros(1, nVars);
336 row(p1idx(i, k, t, nt+1, h)) = -1;
345 %% UTLC: IT(i,k) = sum over t,nt,h of p1c(i,k,t,nt,h)
for each (i,k,t)
346 if verbose; fprintf(
' UTLC constraints...\n'); end
350 row = zeros(1, nVars);
351 row(ITidx(i, k)) = 1;
354 row(p1cidx(i, k, t, nt+1, h)) = -1;
363 %% QLEN: Q(i,k) = sum over ni of ni*p1(i,k,i,ni,k)
for each (i,k)
364 if verbose; fprintf(
' QLEN constraints...\n'); end
367 row = zeros(1, nVars);
370 row(p1idx(i, k, i, ni+1, k)) = row(p1idx(i, k, i, ni+1, k)) - ni;
377 %% CLEN: C(j,k,i) = sum over ni,h of ni*p1(j,k,i,ni,h)
for each (j,k,i)
378 if verbose; fprintf(
' CLEN constraints...\n'); end
382 row = zeros(1, nVars);
383 row(Cidx(j, k, i)) = 1;
386 row(p1idx(j, k, i, ni+1, h)) = row(p1idx(j, k, i, ni+1, h)) - ni;
395 %% ONE: sum over k of (U(j,k) + IT(j,k)) = 1
for each j
396 if verbose; fprintf(
' ONE constraints...\n'); end
398 row = zeros(1, nVars);
401 row(ITidx(j, k)) = 1;
407 %% POPC: sum over i,k of Q(i,k) = N
408 if verbose; fprintf(
' POPC constraint...\n'); end
409 row = zeros(1, nVars);
418 %% MPCB: sum over i of C(j,k,i) = N*U(j,k)
for each (j,k)
419 if verbose; fprintf(
' MPCB constraints...\n'); end
422 row = zeros(1, nVars);
424 row(Cidx(j, k, i)) = 1;
426 row(Uidx(j, k)) = -N;
432 %% UUB1: sum over k of U(i,k) <= 1
for each i (inequality)
433 if verbose; fprintf(
' UUB1 constraints...\n'); end
435 row = zeros(1, nVars);
439 Aineq = [Aineq; row];
443 %% QUB1: Q(j,k) <= N*U(j,k)
for each (j,k) (inequality)
444 if verbose; fprintf(
' QUB1 constraints...\n'); end
447 row = zeros(1, nVars);
449 row(Uidx(j, k)) = -N;
450 Aineq = [Aineq; row];
455 %% Build objective function
456 if verbose; fprintf(
'Building objective function...\n'); end
458 c(Uidx(objective_queue, objective_phase)) = 1;
460 if strcmp(sense,
'max')
466 fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
467 nVars, size(Aeq, 1), size(Aineq, 1));
471 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
473 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
476 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
478 if strcmp(sense, 'max')
484 result.objective = fval;
485 result.exitflag = exitflag;
488 % Compute utilizations
489 result.U = zeros(M, maxK);
490 result.IT = zeros(M, maxK);
491 result.Q = zeros(M, maxK);
495 result.U(i, k) = x(Uidx(i, k));
496 result.IT(i, k) = x(ITidx(i, k));
497 result.Q(i, k) = x(Qidx(i, k));
502 if verbose; fprintf('\n=== Results ===\n'); end
503 if verbose; fprintf('Objective value: %f\n', fval); end
504 if verbose; fprintf('Exit flag: %d\n', exitflag); end
505 if exitflag > 0 && verbose
506 fprintf('\nUtilizations:\n');
508 fprintf(' Queue %d: U = [', i);
510 fprintf('%.6f ', result.U(i, k));
514 fprintf('\nIdle times:\n');
516 fprintf(' Queue %d: IT = [', i);
518 fprintf('%.6f ', result.IT(i, k));
522 fprintf('\nQueue lengths:\n');
524 fprintf(' Queue %d: Q = [', i);
526 fprintf('%.6f ', result.Q(i, k));