1function [result, x, fval, exitflag] = mapqn_bnd_lr_pf(params, objective_queue, sense)
2% MAPQN_BND_LR_PF - Product-Form Linear Reduction Bounds
4% MATLAB port of bnd_lr_pf.py
6% Computes lower/upper bounds on utilization
for closed queueing networks
7%
using a linear programming formulation that exploits product-form
8% structure (no phases, scalar service rates per queue).
11% [result, x, fval, exitflag] = mapqn_bnd_lr_pf(params)
12% [result, x, fval, exitflag] = mapqn_bnd_lr_pf(params, objective_queue)
13% [result, x, fval, exitflag] = mapqn_bnd_lr_pf(params, objective_queue, sense)
16% params - Structure with model parameters:
17% .M - Number of queues
18% .N - Total population
19% .mu - [M x 1] Service rates (scalar per queue)
20% .r - [M x M] Routing probability matrix
21% .verbose - (optional)
boolean,
default true
23% objective_queue - (optional) Queue index
for objective (1-based),
default 1
24% sense - (optional)
'min' (
default) or
'max'
27% result - Structure with results:
28% .objective - Objective function value
29% .exitflag - Solver exit flag
30% .U - [M x 1] Utilizations
31% .Q - [M x 1] Mean queue lengths
32% x - Raw solution vector
33% fval - Objective function value
34% exitflag - Solver exit flag
36 if nargin < 2 || isempty(objective_queue)
39 if nargin < 3 || isempty(sense)
48 if isfield(params,
'verbose')
49 verbose = params.verbose;
54 % Compute transition rates q(i,j) = r(i,j) * mu(i)
58 q(i,j) = r(i,j) * mu(i);
62 %% Build variable indexing
63 % Variables (all 1-based):
66 % C(j,i) for j in 1:M, i in 1:M
67 % p1(j,i,ni) for j in 1:M, i in 1:M, ni in 0:N
68 % p1c(j,i,ni) for j in 1:M, i in 1:M, ni in 0:N
70 if verbose; fprintf('Building variable index
map...\n'); end
73 % U variables: utilization
76 varCount = varCount + 1;
80 % Q variables: mean queue length
83 varCount = varCount + 1;
87 % C variables: conditional queue lengths C(j,i)
91 varCount = varCount + 1;
96 % p1 variables: marginal probabilities p1(j,i,ni), ni in 0:N
97 p1idx = zeros(M, M, N+1);
101 varCount = varCount + 1;
102 p1idx(j, i, ni+1) = varCount;
107 % p1c variables: complementary marginal p1c(j,i,ni), ni in 0:N
108 p1cidx = zeros(M, M, N+1);
112 varCount = varCount + 1;
113 p1cidx(j, i, ni+1) = varCount;
119 if verbose; fprintf('Total variables: %d\n', nVars); end
122 lb = zeros(nVars, 1);
146 ub(p1idx(j, i, ni+1)) = 1;
155 ub(p1cidx(j, i, ni+1)) = 1;
164 if verbose; fprintf('Building constraints...\n'); end
166 %% ZER1: p1(j,j,0) = 0 for all j (via upper bound)
167 if verbose; fprintf(' ZER1 constraints...\n'); end
169 ub(p1idx(j, j, 0+1)) = 0;
172 %% ZER3: p1(j,i,N) = 0 for j ~= i (via upper bound)
173 if verbose; fprintf(' ZER3 constraints...\n'); end
177 ub(p1idx(j, i, N+1)) = 0;
182 %% CEQU: C(j,j) = Q(j) for all j
183 if verbose; fprintf(' CEQU constraints...\n'); end
185 row = zeros(1, nVars);
192 %% ONE1: sum over ni of (p1(j,i,ni) + p1c(j,i,ni)) = 1 for each (j,i)
193 if verbose; fprintf(' ONE1 constraints...\n'); end
196 row = zeros(1, nVars);
198 row(p1idx(j, i, ni+1)) = 1;
199 row(p1cidx(j, i, ni+1)) = 1;
206 %% UTIL: U(i) = sum over t,nt of p1(i,t,nt) for each (i,t)
207 if verbose; fprintf(' UTIL constraints...\n'); end
210 row = zeros(1, nVars);
213 row(p1idx(i, t, nt+1)) = -1;
220 %% QLEN: Q(i) = sum over ni of ni*p1(i,i,ni) for each i
221 if verbose; fprintf(' QLEN constraints...\n'); end
223 row = zeros(1, nVars);
226 row(p1idx(i, i, ni+1)) = -ni;
232 %% CLEN: C(j,i) = sum over ni of ni*p1(j,i,ni) for each (j,i)
233 if verbose; fprintf(' CLEN constraints...\n'); end
236 row = zeros(1, nVars);
239 row(p1idx(j, i, ni+1)) = -ni;
246 %% MPCB: sum over i of C(j,i) = N*U(j) for each j
247 if verbose; fprintf(' MPCB constraints...\n'); end
249 row = zeros(1, nVars);
258 %% POPC: sum over i of Q(i) = N
259 if verbose; fprintf(' POPC constraint...\n'); end
260 row = zeros(1, nVars);
267 %% GFFL0: Global flow for ni=0
268 % For each i: sum over j~=i of q(j,i)*p1(j,i,0) = sum over j~=i of q(i,j)*p1(i,i,1)
269 if verbose; fprintf(' GFFL0 constraints...\n'); end
271 row = zeros(1, nVars);
274 row(p1idx(j, i, 0+1)) = row(p1idx(j, i, 0+1)) + q(j,i);
275 row(p1idx(i, i, 1+1)) = row(p1idx(i, i, 1+1)) - q(i,j);
282 %% GFFL: Global flow for ni in 1..N-1
283 % For each (i,ni): sum over j~=i of q(j,i)*p1(j,i,ni) = sum over j~=i of q(i,j)*p1(i,i,ni+1)
284 if verbose; fprintf(' GFFL constraints...\n'); end
287 row = zeros(1, nVars);
290 row(p1idx(j, i, ni+1)) = row(p1idx(j, i, ni+1)) + q(j,i);
291 row(p1idx(i, i, ni+1+1)) = row(p1idx(i, i, ni+1+1)) - q(i,j);
299 %% UJNT: Joint probability symmetry
300 % sum over ni=1..N of p1(j,i,ni) = sum over nj=1..N of p1(i,j,nj) for each (i,j)
301 if verbose; fprintf(' UJNT constraints...\n'); end
304 row = zeros(1, nVars);
306 row(p1idx(j, i, ni+1)) = row(p1idx(j, i, ni+1)) + 1;
309 row(p1idx(i, j, nj+1)) = row(p1idx(i, j, nj+1)) - 1;
316 %% QBAL: Queue balance
317 % For each i: sum over j~=i of (q(i,j)*U(i) - q(j,i)*sum_nj(p1(i,j,nj)) - q(j,i)*p1(j,i,0)) = 0
318 if verbose; fprintf(' QBAL constraints...\n'); end
320 row = zeros(1, nVars);
323 row(Uidx(i)) = row(Uidx(i)) + q(i,j);
325 row(p1idx(i, j, nj+1)) = row(p1idx(i, j, nj+1)) - q(j,i);
327 row(p1idx(j, i, 0+1)) = row(p1idx(j, i, 0+1)) - q(j,i);
334 %% Build objective function
335 if verbose; fprintf('Building objective function...\n'); end
337 c(Uidx(objective_queue)) = 1;
339 if strcmp(sense, 'max')
344 if verbose; fprintf('Solving LP with %d variables and %d equality constraints...\n', ...
345 nVars, size(Aeq, 1)); end
348 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
350 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
353 [x, fval, exitflag] = linprog(c, [], [], Aeq, beq, lb, ub, options);
355 if strcmp(sense, 'max')
361 result.objective = fval;
362 result.exitflag = exitflag;
365 result.U = zeros(M, 1);
366 result.Q = zeros(M, 1);
369 result.U(i) = x(Uidx(i));
370 result.Q(i) = x(Qidx(i));
374 if verbose; fprintf('\n=== Results ===\n'); end
375 if verbose; fprintf('Objective value: %f\n', fval); end
376 if verbose; fprintf('Exit flag: %d\n', exitflag); end
377 if exitflag > 0 && verbose
378 fprintf('\nUtilizations:\n');
380 fprintf(' Queue %d: U = %.6f\n', i, result.U(i));
382 fprintf('\nMean queue lengths:\n');
384 fprintf(' Queue %d: Q = %.6f\n', i, result.Q(i));