LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
mapqn_bnd_lr_pf.m
1function [result, x, fval, exitflag] = mapqn_bnd_lr_pf(params, objective_queue, sense)
2% MAPQN_BND_LR_PF - Product-Form Linear Reduction Bounds
3%
4% MATLAB port of bnd_lr_pf.py
5%
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).
9%
10% Usage:
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)
14%
15% Inputs:
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
22%
23% objective_queue - (optional) Queue index for objective (1-based), default 1
24% sense - (optional) 'min' (default) or 'max'
25%
26% Outputs:
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
35
36 if nargin < 2 || isempty(objective_queue)
37 objective_queue = 1;
38 end
39 if nargin < 3 || isempty(sense)
40 sense = 'min';
41 end
42
43 % Extract parameters
44 M = params.M;
45 N = params.N;
46 mu = params.mu(:);
47 r = params.r;
48 if isfield(params, 'verbose')
49 verbose = params.verbose;
50 else
51 verbose = true;
52 end
53
54 % Compute transition rates q(i,j) = r(i,j) * mu(i)
55 q = zeros(M, M);
56 for i = 1:M
57 for j = 1:M
58 q(i,j) = r(i,j) * mu(i);
59 end
60 end
61
62 %% Build variable indexing
63 % Variables (all 1-based):
64 % U(i) for i in 1:M
65 % Q(i) for i in 1:M
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
69
70 if verbose; fprintf('Building variable index map...\n'); end
71 varCount = 0;
72
73 % U variables: utilization
74 Uidx = zeros(M, 1);
75 for i = 1:M
76 varCount = varCount + 1;
77 Uidx(i) = varCount;
78 end
79
80 % Q variables: mean queue length
81 Qidx = zeros(M, 1);
82 for i = 1:M
83 varCount = varCount + 1;
84 Qidx(i) = varCount;
85 end
86
87 % C variables: conditional queue lengths C(j,i)
88 Cidx = zeros(M, M);
89 for j = 1:M
90 for i = 1:M
91 varCount = varCount + 1;
92 Cidx(j,i) = varCount;
93 end
94 end
95
96 % p1 variables: marginal probabilities p1(j,i,ni), ni in 0:N
97 p1idx = zeros(M, M, N+1);
98 for j = 1:M
99 for i = 1:M
100 for ni = 0:N
101 varCount = varCount + 1;
102 p1idx(j, i, ni+1) = varCount;
103 end
104 end
105 end
106
107 % p1c variables: complementary marginal p1c(j,i,ni), ni in 0:N
108 p1cidx = zeros(M, M, N+1);
109 for j = 1:M
110 for i = 1:M
111 for ni = 0:N
112 varCount = varCount + 1;
113 p1cidx(j, i, ni+1) = varCount;
114 end
115 end
116 end
117
118 nVars = varCount;
119 if verbose; fprintf('Total variables: %d\n', nVars); end
120
121 %% Initialize bounds
122 lb = zeros(nVars, 1);
123 ub = inf(nVars, 1);
124
125 % U bounds: [0, 1]
126 for i = 1:M
127 ub(Uidx(i)) = 1;
128 end
129
130 % Q bounds: [0, N]
131 for i = 1:M
132 ub(Qidx(i)) = N;
133 end
134
135 % C bounds: [0, N]
136 for j = 1:M
137 for i = 1:M
138 ub(Cidx(j,i)) = N;
139 end
140 end
141
142 % p1 bounds: [0, 1]
143 for j = 1:M
144 for i = 1:M
145 for ni = 0:N
146 ub(p1idx(j, i, ni+1)) = 1;
147 end
148 end
149 end
150
151 % p1c bounds: [0, 1]
152 for j = 1:M
153 for i = 1:M
154 for ni = 0:N
155 ub(p1cidx(j, i, ni+1)) = 1;
156 end
157 end
158 end
159
160 %% Build constraints
161 Aeq = [];
162 beq = [];
163
164 if verbose; fprintf('Building constraints...\n'); end
165
166 %% ZER1: p1(j,j,0) = 0 for all j (via upper bound)
167 if verbose; fprintf(' ZER1 constraints...\n'); end
168 for j = 1:M
169 ub(p1idx(j, j, 0+1)) = 0;
170 end
171
172 %% ZER3: p1(j,i,N) = 0 for j ~= i (via upper bound)
173 if verbose; fprintf(' ZER3 constraints...\n'); end
174 for j = 1:M
175 for i = 1:M
176 if j ~= i
177 ub(p1idx(j, i, N+1)) = 0;
178 end
179 end
180 end
181
182 %% CEQU: C(j,j) = Q(j) for all j
183 if verbose; fprintf(' CEQU constraints...\n'); end
184 for j = 1:M
185 row = zeros(1, nVars);
186 row(Cidx(j,j)) = 1;
187 row(Qidx(j)) = -1;
188 Aeq = [Aeq; row];
189 beq = [beq; 0];
190 end
191
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
194 for j = 1:M
195 for i = 1:M
196 row = zeros(1, nVars);
197 for ni = 0:N
198 row(p1idx(j, i, ni+1)) = 1;
199 row(p1cidx(j, i, ni+1)) = 1;
200 end
201 Aeq = [Aeq; row];
202 beq = [beq; 1];
203 end
204 end
205
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
208 for i = 1:M
209 for t = 1:M
210 row = zeros(1, nVars);
211 row(Uidx(i)) = 1;
212 for nt = 0:N
213 row(p1idx(i, t, nt+1)) = -1;
214 end
215 Aeq = [Aeq; row];
216 beq = [beq; 0];
217 end
218 end
219
220 %% QLEN: Q(i) = sum over ni of ni*p1(i,i,ni) for each i
221 if verbose; fprintf(' QLEN constraints...\n'); end
222 for i = 1:M
223 row = zeros(1, nVars);
224 row(Qidx(i)) = 1;
225 for ni = 0:N
226 row(p1idx(i, i, ni+1)) = -ni;
227 end
228 Aeq = [Aeq; row];
229 beq = [beq; 0];
230 end
231
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
234 for j = 1:M
235 for i = 1:M
236 row = zeros(1, nVars);
237 row(Cidx(j,i)) = 1;
238 for ni = 0:N
239 row(p1idx(j, i, ni+1)) = -ni;
240 end
241 Aeq = [Aeq; row];
242 beq = [beq; 0];
243 end
244 end
245
246 %% MPCB: sum over i of C(j,i) = N*U(j) for each j
247 if verbose; fprintf(' MPCB constraints...\n'); end
248 for j = 1:M
249 row = zeros(1, nVars);
250 for i = 1:M
251 row(Cidx(j,i)) = 1;
252 end
253 row(Uidx(j)) = -N;
254 Aeq = [Aeq; row];
255 beq = [beq; 0];
256 end
257
258 %% POPC: sum over i of Q(i) = N
259 if verbose; fprintf(' POPC constraint...\n'); end
260 row = zeros(1, nVars);
261 for i = 1:M
262 row(Qidx(i)) = 1;
263 end
264 Aeq = [Aeq; row];
265 beq = [beq; N];
266
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
270 for i = 1:M
271 row = zeros(1, nVars);
272 for j = 1:M
273 if j ~= i
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);
276 end
277 end
278 Aeq = [Aeq; row];
279 beq = [beq; 0];
280 end
281
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
285 for i = 1:M
286 for ni = 1:(N-1)
287 row = zeros(1, nVars);
288 for j = 1:M
289 if j ~= i
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);
292 end
293 end
294 Aeq = [Aeq; row];
295 beq = [beq; 0];
296 end
297 end
298
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
302 for i = 1:M
303 for j = 1:M
304 row = zeros(1, nVars);
305 for ni = 1:N
306 row(p1idx(j, i, ni+1)) = row(p1idx(j, i, ni+1)) + 1;
307 end
308 for nj = 1:N
309 row(p1idx(i, j, nj+1)) = row(p1idx(i, j, nj+1)) - 1;
310 end
311 Aeq = [Aeq; row];
312 beq = [beq; 0];
313 end
314 end
315
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
319 for i = 1:M
320 row = zeros(1, nVars);
321 for j = 1:M
322 if j ~= i
323 row(Uidx(i)) = row(Uidx(i)) + q(i,j);
324 for nj = 1:N
325 row(p1idx(i, j, nj+1)) = row(p1idx(i, j, nj+1)) - q(j,i);
326 end
327 row(p1idx(j, i, 0+1)) = row(p1idx(j, i, 0+1)) - q(j,i);
328 end
329 end
330 Aeq = [Aeq; row];
331 beq = [beq; 0];
332 end
333
334 %% Build objective function
335 if verbose; fprintf('Building objective function...\n'); end
336 c = zeros(nVars, 1);
337 c(Uidx(objective_queue)) = 1;
338
339 if strcmp(sense, 'max')
340 c = -c;
341 end
342
343 %% Solve LP
344 if verbose; fprintf('Solving LP with %d variables and %d equality constraints...\n', ...
345 nVars, size(Aeq, 1)); end
346
347 if verbose
348 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
349 else
350 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
351 end
352
353 [x, fval, exitflag] = linprog(c, [], [], Aeq, beq, lb, ub, options);
354
355 if strcmp(sense, 'max')
356 fval = -fval;
357 end
358
359 %% Extract results
360 result = struct();
361 result.objective = fval;
362 result.exitflag = exitflag;
363
364 if exitflag > 0
365 result.U = zeros(M, 1);
366 result.Q = zeros(M, 1);
367
368 for i = 1:M
369 result.U(i) = x(Uidx(i));
370 result.Q(i) = x(Qidx(i));
371 end
372 end
373
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');
379 for i = 1:M
380 fprintf(' Queue %d: U = %.6f\n', i, result.U(i));
381 end
382 fprintf('\nMean queue lengths:\n');
383 for i = 1:M
384 fprintf(' Queue %d: Q = %.6f\n', i, result.Q(i));
385 end
386 end
387end