LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
mapqn_bnd_qr.m
1function [result, x, fval, exitflag] = mapqn_bnd_qr(params, objective_queue, objective_phase, sense)
2% MAPQN_BND_QR - General Quadratic Reduction Bounds for MAP Queueing Networks
3%
4% MATLAB port of the Python file bnd_qr.py
5%
6% Usage:
7% [result, x, fval, exitflag] = mapqn_bnd_qr(params)
8% [result, x, fval, exitflag] = mapqn_bnd_qr(params, objective_queue)
9% [result, x, fval, exitflag] = mapqn_bnd_qr(params, objective_queue, objective_phase)
10% [result, x, fval, exitflag] = mapqn_bnd_qr(params, objective_queue, objective_phase, sense)
11%
12% Inputs:
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
21%
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'
25%
26% Outputs:
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% .getP2 - Function handle to extract p2 values
34% x - Raw solution vector
35% fval - Objective function value
36% exitflag - Solver exit flag
37
38 if nargin < 2 || isempty(objective_queue)
39 objective_queue = 1;
40 end
41 if nargin < 3 || isempty(objective_phase)
42 objective_phase = 1;
43 end
44 if nargin < 4 || isempty(sense)
45 sense = 'max';
46 end
47
48 % Extract parameters
49 M = params.M;
50 N = params.N;
51 K = params.K(:);
52 mu = params.mu;
53 v = params.v;
54 r = params.r;
55 if isfield(params, 'verbose')
56 verbose = params.verbose;
57 else
58 verbose = true;
59 end
60
61 maxK = max(K);
62
63 % Compute transition rates q{i,j}(k,h)
64 q = cell(M, M);
65 for i = 1:M
66 for j = 1:M
67 q{i,j} = zeros(K(i), K(i));
68 for ki = 1:K(i)
69 for hi = 1:K(i)
70 if j ~= i
71 q{i,j}(ki, hi) = r(i,j) * mu{i}(ki, hi);
72 else
73 q{i,j}(ki, hi) = v{i}(ki, hi) + r(i,i) * mu{i}(ki, hi);
74 end
75 end
76 end
77 end
78 end
79
80 %% Build variable indexing
81 if verbose; fprintf('Building variable index map...\n'); end
82 varCount = 0;
83
84 % U(i,k) variables: utilization at queue i, phase k
85 Uidx = zeros(M, maxK);
86 for i = 1:M
87 for k = 1:K(i)
88 varCount = varCount + 1;
89 Uidx(i, k) = varCount;
90 end
91 end
92
93 % IT(i,k) variables: idle time at queue i, phase k
94 ITidx = zeros(M, maxK);
95 for i = 1:M
96 for k = 1:K(i)
97 varCount = varCount + 1;
98 ITidx(i, k) = varCount;
99 end
100 end
101
102 % UP(j,k,i,h) variables: utilization products
103 UPidx = zeros(M, maxK, M, maxK);
104 for j = 1:M
105 for kj = 1:K(j)
106 for i = 1:M
107 for hi = 1:K(i)
108 varCount = varCount + 1;
109 UPidx(j, kj, i, hi) = varCount;
110 end
111 end
112 end
113 end
114
115 % QP(j,k,i,h) variables: queue-length products
116 QPidx = zeros(M, maxK, M, maxK);
117 for j = 1:M
118 for kj = 1:K(j)
119 for i = 1:M
120 for hi = 1:K(i)
121 varCount = varCount + 1;
122 QPidx(j, kj, i, hi) = varCount;
123 end
124 end
125 end
126 end
127
128 % Q(i,k) variables: mean queue length
129 Qidx = zeros(M, maxK);
130 for i = 1:M
131 for k = 1:K(i)
132 varCount = varCount + 1;
133 Qidx(i, k) = varCount;
134 end
135 end
136
137 % C(j,k,i) variables: conditional queue lengths
138 Cidx = zeros(M, maxK, M);
139 for j = 1:M
140 for kj = 1:K(j)
141 for i = 1:M
142 varCount = varCount + 1;
143 Cidx(j, kj, i) = varCount;
144 end
145 end
146 end
147
148 % I_var(j,k,i) variables: conditional idle lengths
149 Iidx = zeros(M, maxK, M);
150 for j = 1:M
151 for kj = 1:K(j)
152 for i = 1:M
153 varCount = varCount + 1;
154 Iidx(j, kj, i) = varCount;
155 end
156 end
157 end
158
159 % p1(j,k,i,ni,h) variables: marginal probabilities, ni from 0 to N
160 % Stored as p1idx(j, k, i, ni+1, h)
161 p1idx = zeros(M, maxK, M, N+1, maxK);
162 for j = 1:M
163 for kj = 1:K(j)
164 for i = 1:M
165 for ni = 0:N
166 for hi = 1:K(i)
167 varCount = varCount + 1;
168 p1idx(j, kj, i, ni+1, hi) = varCount;
169 end
170 end
171 end
172 end
173 end
174
175 % p1c(j,k,i,ni,h) variables: complementary marginal probabilities, ni from 0 to N
176 % Stored as p1cidx(j, k, i, ni+1, h)
177 p1cidx = zeros(M, maxK, M, N+1, maxK);
178 for j = 1:M
179 for kj = 1:K(j)
180 for i = 1:M
181 for ni = 0:N
182 for hi = 1:K(i)
183 varCount = varCount + 1;
184 p1cidx(j, kj, i, ni+1, hi) = varCount;
185 end
186 end
187 end
188 end
189 end
190
191 % p2(j,nj,k,i,ni,h) variables: joint probabilities, nj from 0 to N, ni from 0 to N
192 % Stored as p2idx(j, nj+1, k, i, ni+1, h)
193 p2idx = zeros(M, N+1, maxK, M, N+1, maxK);
194 for j = 1:M
195 for nj = 0:N
196 for kj = 1:K(j)
197 for i = 1:M
198 for ni = 0:N
199 for hi = 1:K(i)
200 varCount = varCount + 1;
201 p2idx(j, nj+1, kj, i, ni+1, hi) = varCount;
202 end
203 end
204 end
205 end
206 end
207 end
208
209 nVars = varCount;
210 if verbose; fprintf('Total variables: %d\n', nVars); end
211
212 %% Initialize bounds
213 lb = zeros(nVars, 1);
214 ub = inf(nVars, 1);
215
216 % Set upper bounds for each variable type
217 for i = 1:M
218 for k = 1:K(i)
219 ub(Uidx(i, k)) = 1;
220 ub(ITidx(i, k)) = 1;
221 ub(Qidx(i, k)) = N;
222 end
223 end
224 for j = 1:M
225 for kj = 1:K(j)
226 for i = 1:M
227 for hi = 1:K(i)
228 ub(UPidx(j, kj, i, hi)) = 1;
229 ub(QPidx(j, kj, i, hi)) = N;
230 end
231 end
232 end
233 end
234 for j = 1:M
235 for kj = 1:K(j)
236 for i = 1:M
237 ub(Cidx(j, kj, i)) = N;
238 ub(Iidx(j, kj, i)) = N;
239 end
240 end
241 end
242 for j = 1:M
243 for kj = 1:K(j)
244 for i = 1:M
245 for ni = 0:N
246 for hi = 1:K(i)
247 ub(p1idx(j, kj, i, ni+1, hi)) = 1;
248 ub(p1cidx(j, kj, i, ni+1, hi)) = 1;
249 end
250 end
251 end
252 end
253 end
254 for j = 1:M
255 for nj = 0:N
256 for kj = 1:K(j)
257 for i = 1:M
258 for ni = 0:N
259 for hi = 1:K(i)
260 ub(p2idx(j, nj+1, kj, i, ni+1, hi)) = 1;
261 end
262 end
263 end
264 end
265 end
266 end
267
268 %% Build constraints
269 Aeq = [];
270 beq = [];
271 Aineq = [];
272 bineq = [];
273
274 if verbose; fprintf('Building constraints...\n'); end
275
276 %% ZER1: p1(j,k,j,0,k) = 0 for all j,k (via upper bounds)
277 if verbose; fprintf(' ZER1 constraints...\n'); end
278 for j = 1:M
279 for k = 1:K(j)
280 idx = p1idx(j, k, j, 0+1, k);
281 ub(idx) = 0;
282 end
283 end
284
285 %% ZER2: p1(j,k,j,nj,h) = 0 for h ~= k, all j,k,nj (via upper bounds)
286 if verbose; fprintf(' ZER2 constraints...\n'); end
287 for j = 1:M
288 for k = 1:K(j)
289 for nj = 0:N
290 for h = 1:K(j)
291 if h ~= k
292 idx = p1idx(j, k, j, nj+1, h);
293 ub(idx) = 0;
294 end
295 end
296 end
297 end
298 end
299
300 %% ZER3: p1(j,k,i,N,h) = 0 for j ~= i, all j,k,i,h (via upper bounds)
301 if verbose; fprintf(' ZER3 constraints...\n'); end
302 for j = 1:M
303 for k = 1:K(j)
304 for i = 1:M
305 if j ~= i
306 for h = 1:K(i)
307 idx = p1idx(j, k, i, N+1, h);
308 ub(idx) = 0;
309 end
310 end
311 end
312 end
313 end
314
315 %% ZER4: p1c(j,k,j,nj,h) = 0 for nj >= 1, all j,k,nj,h (via upper bounds)
316 if verbose; fprintf(' ZER4 constraints...\n'); end
317 for j = 1:M
318 for k = 1:K(j)
319 for nj = 1:N
320 for h = 1:K(j)
321 idx = p1cidx(j, k, j, nj+1, h);
322 ub(idx) = 0;
323 end
324 end
325 end
326 end
327
328 %% CEQU: C(j,k,j) = Q(j,k) for all j,k
329 if verbose; fprintf(' CEQU constraints...\n'); end
330 for j = 1:M
331 for k = 1:K(j)
332 row = zeros(1, nVars);
333 row(Cidx(j, k, j)) = 1;
334 row(Qidx(j, k)) = -1;
335 Aeq = [Aeq; row];
336 beq = [beq; 0];
337 end
338 end
339
340 %% ONE1: sum over kj,hi,ni of (p1 + p1c) = 1 for each (j,i)
341 if verbose; fprintf(' ONE1 constraints...\n'); end
342 for j = 1:M
343 for i = 1:M
344 row = zeros(1, nVars);
345 for kj = 1:K(j)
346 for hi = 1:K(i)
347 for ni = 0:N
348 row(p1idx(j, kj, i, ni+1, hi)) = 1;
349 row(p1cidx(j, kj, i, ni+1, hi)) = 1;
350 end
351 end
352 end
353 Aeq = [Aeq; row];
354 beq = [beq; 1];
355 end
356 end
357
358 %% UTLB: U(i,k) = sum over t,nt,h of p1(i,k,t,nt,h) for each (i,k,t)
359 if verbose; fprintf(' UTLB constraints...\n'); end
360 for i = 1:M
361 for k = 1:K(i)
362 for t = 1:M
363 row = zeros(1, nVars);
364 row(Uidx(i, k)) = 1;
365 for nt = 0:N
366 for h = 1:K(t)
367 row(p1idx(i, k, t, nt+1, h)) = -1;
368 end
369 end
370 Aeq = [Aeq; row];
371 beq = [beq; 0];
372 end
373 end
374 end
375
376 %% UTLC: IT(i,k) = sum over t,nt,h of p1c(i,k,t,nt,h) for each (i,k,t)
377 if verbose; fprintf(' UTLC constraints...\n'); end
378 for i = 1:M
379 for k = 1:K(i)
380 for t = 1:M
381 row = zeros(1, nVars);
382 row(ITidx(i, k)) = 1;
383 for nt = 0:N
384 for h = 1:K(t)
385 row(p1cidx(i, k, t, nt+1, h)) = -1;
386 end
387 end
388 Aeq = [Aeq; row];
389 beq = [beq; 0];
390 end
391 end
392 end
393
394 %% QLEN: Q(i,k) = sum over ni of ni*p1(i,k,i,ni,k) for each (i,k)
395 if verbose; fprintf(' QLEN constraints...\n'); end
396 for i = 1:M
397 for k = 1:K(i)
398 row = zeros(1, nVars);
399 row(Qidx(i, k)) = 1;
400 for ni = 0:N
401 row(p1idx(i, k, i, ni+1, k)) = row(p1idx(i, k, i, ni+1, k)) - ni;
402 end
403 Aeq = [Aeq; row];
404 beq = [beq; 0];
405 end
406 end
407
408 %% SRVB: Service balance
409 % sum{j,h} q{i,j}(k,h)*U(i,k) = sum{j,h} q{i,j}(h,k)*U(i,h) for each (i,k)
410 if verbose; fprintf(' SRVB constraints...\n'); end
411 for i = 1:M
412 for k = 1:K(i)
413 row = zeros(1, nVars);
414 for j = 1:M
415 for h = 1:K(i)
416 row(Uidx(i, k)) = row(Uidx(i, k)) + q{i,j}(k, h);
417 row(Uidx(i, h)) = row(Uidx(i, h)) - q{i,j}(h, k);
418 end
419 end
420 Aeq = [Aeq; row];
421 beq = [beq; 0];
422 end
423 end
424
425 %% POPC: sum over i,k of Q(i,k) = N
426 if verbose; fprintf(' POPC constraint...\n'); end
427 row = zeros(1, nVars);
428 for i = 1:M
429 for k = 1:K(i)
430 row(Qidx(i, k)) = 1;
431 end
432 end
433 Aeq = [Aeq; row];
434 beq = [beq; N];
435
436 %% ONE: sum over k of (U(j,k) + IT(j,k)) = 1 for each j
437 if verbose; fprintf(' ONE constraints...\n'); end
438 for j = 1:M
439 row = zeros(1, nVars);
440 for k = 1:K(j)
441 row(Uidx(j, k)) = 1;
442 row(ITidx(j, k)) = 1;
443 end
444 Aeq = [Aeq; row];
445 beq = [beq; 1];
446 end
447
448 %% PCL2: sum over i,j,ni>=1,nj>=1,h,k of ni*nj*p2(i,ni,h,j,nj,k) = N^2
449 if verbose; fprintf(' PCL2 constraint...\n'); end
450 row = zeros(1, nVars);
451 for i = 1:M
452 for j = 1:M
453 for ni = 1:N
454 for nj = 1:N
455 for h = 1:K(i)
456 for k = 1:K(j)
457 idx = p2idx(i, ni+1, h, j, nj+1, k);
458 row(idx) = row(idx) + ni * nj;
459 end
460 end
461 end
462 end
463 end
464 end
465 Aeq = [Aeq; row];
466 beq = [beq; N^2];
467
468 %% PI21: p1(j,k,i,ni,h) = sum over nj=1..N of p2(j,nj,k,i,ni,h)
469 if verbose; fprintf(' PI21 constraints...\n'); end
470 for j = 1:M
471 for k = 1:K(j)
472 for i = 1:M
473 for ni = 0:N
474 for h = 1:K(i)
475 row = zeros(1, nVars);
476 row(p1idx(j, k, i, ni+1, h)) = 1;
477 for nj = 1:N
478 idx = p2idx(j, nj+1, k, i, ni+1, h);
479 row(idx) = row(idx) - 1;
480 end
481 Aeq = [Aeq; row];
482 beq = [beq; 0];
483 end
484 end
485 end
486 end
487 end
488
489 %% PI22: p1c(j,k,i,ni,h) = p2(j,0,k,i,ni,h)
490 if verbose; fprintf(' PI22 constraints...\n'); end
491 for j = 1:M
492 for k = 1:K(j)
493 for i = 1:M
494 for ni = 0:N
495 for h = 1:K(i)
496 row = zeros(1, nVars);
497 row(p1cidx(j, k, i, ni+1, h)) = 1;
498 row(p2idx(j, 0+1, k, i, ni+1, h)) = -1;
499 Aeq = [Aeq; row];
500 beq = [beq; 0];
501 end
502 end
503 end
504 end
505 end
506
507 %% PI23: p2(i,ni,h,j,nj,k) = p2(j,nj,k,i,ni,h) (symmetry)
508 % Only generate for j < i, or (j == i and nj < ni), to avoid redundancy
509 if verbose; fprintf(' PI23 (symmetry) constraints...\n'); end
510 for j = 1:M
511 for nj = 0:N
512 for k = 1:K(j)
513 for i = 1:M
514 for ni = 0:N
515 for h = 1:K(i)
516 % Only generate constraint if (j,nj,k) < (i,ni,h) in lex order
517 if j < i || (j == i && nj < ni) || (j == i && nj == ni && k < h)
518 idx1 = p2idx(i, ni+1, h, j, nj+1, k);
519 idx2 = p2idx(j, nj+1, k, i, ni+1, h);
520 if idx1 ~= idx2
521 row = zeros(1, nVars);
522 row(idx1) = 1;
523 row(idx2) = -1;
524 Aeq = [Aeq; row];
525 beq = [beq; 0];
526 end
527 end
528 end
529 end
530 end
531 end
532 end
533 end
534
535 %% UUB1: sum over k of U(i,k) <= 1 for each i (inequality)
536 if verbose; fprintf(' UUB1 constraints...\n'); end
537 for i = 1:M
538 row = zeros(1, nVars);
539 for k = 1:K(i)
540 row(Uidx(i, k)) = 1;
541 end
542 Aineq = [Aineq; row];
543 bineq = [bineq; 1];
544 end
545
546 %% QUB1: Q(j,k) <= N*U(j,k) for each (j,k) (inequality)
547 if verbose; fprintf(' QUB1 constraints...\n'); end
548 for j = 1:M
549 for k = 1:K(j)
550 row = zeros(1, nVars);
551 row(Qidx(j, k)) = 1;
552 row(Uidx(j, k)) = -N;
553 Aineq = [Aineq; row];
554 bineq = [bineq; 0];
555 end
556 end
557
558 %% CUB1: C(j,k,i) <= sum over h of Q(i,h) for each (j,k,i) (inequality)
559 if verbose; fprintf(' CUB1 constraints...\n'); end
560 for j = 1:M
561 for k = 1:K(j)
562 for i = 1:M
563 row = zeros(1, nVars);
564 row(Cidx(j, k, i)) = 1;
565 for h = 1:K(i)
566 row(Qidx(i, h)) = -1;
567 end
568 Aineq = [Aineq; row];
569 bineq = [bineq; 0];
570 end
571 end
572 end
573
574 %% CUB2: C(j,k,i) <= N*U(j,k) for each (j,k,i) (inequality)
575 if verbose; fprintf(' CUB2 constraints...\n'); end
576 for j = 1:M
577 for k = 1:K(j)
578 for i = 1:M
579 row = zeros(1, nVars);
580 row(Cidx(j, k, i)) = 1;
581 row(Uidx(j, k)) = -N;
582 Aineq = [Aineq; row];
583 bineq = [bineq; 0];
584 end
585 end
586 end
587
588 %% Build objective function
589 if verbose; fprintf('Building objective function...\n'); end
590 c = zeros(nVars, 1);
591 c(Uidx(objective_queue, objective_phase)) = 1;
592
593 if strcmp(sense, 'max')
594 c = -c;
595 end
596
597 %% Solve LP
598 if verbose
599 fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
600 nVars, size(Aeq, 1), size(Aineq, 1));
601 end
602
603 if verbose
604 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
605 else
606 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
607 end
608
609 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
610
611 if strcmp(sense, 'max')
612 fval = -fval;
613 end
614
615 %% Extract results
616 result = struct();
617 result.objective = fval;
618 result.exitflag = exitflag;
619
620 if exitflag > 0
621 % Compute utilizations, idle times, queue lengths
622 result.U = zeros(M, maxK);
623 result.IT = zeros(M, maxK);
624 result.Q = zeros(M, maxK);
625
626 for i = 1:M
627 for k = 1:K(i)
628 result.U(i, k) = x(Uidx(i, k));
629 result.IT(i, k) = x(ITidx(i, k));
630 result.Q(i, k) = x(Qidx(i, k));
631 end
632 end
633
634 % Function handle to extract p2 values: p2(j,nj,k,i,ni,h)
635 result.getP2 = @(j, nj, k, i, ni, h) x(p2idx(j, nj+1, k, i, ni+1, h));
636 end
637
638 if verbose; fprintf('\n=== Results ===\n'); end
639 if verbose; fprintf('Objective value: %f\n', fval); end
640 if verbose; fprintf('Exit flag: %d\n', exitflag); end
641 if exitflag > 0 && verbose
642 fprintf('\nUtilizations:\n');
643 for i = 1:M
644 fprintf(' Queue %d: U = [', i);
645 for k = 1:K(i)
646 fprintf('%.6f ', result.U(i, k));
647 end
648 fprintf(']\n');
649 end
650 fprintf('\nIdle times:\n');
651 for i = 1:M
652 fprintf(' Queue %d: IT = [', i);
653 for k = 1:K(i)
654 fprintf('%.6f ', result.IT(i, k));
655 end
656 fprintf(']\n');
657 end
658 fprintf('\nQueue lengths:\n');
659 for i = 1:M
660 fprintf(' Queue %d: Q = [', i);
661 for k = 1:K(i)
662 fprintf('%.6f ', result.Q(i, k));
663 end
664 fprintf(']\n');
665 end
666 end
667end