1function [result, x, fval, exitflag] = qrf_bas(params, objective, sense)
2% QRF_BAS - Quadratic Reduction Framework
for BAS (Blocking-After-Service) networks
4% MATLAB port of the AMPL model qrboundsbas_skel.mod
7% [result, x, fval, exitflag] = qrf_bas(params)
8% [result, x, fval, exitflag] = qrf_bas(params, objective)
9% [result, x, fval, exitflag] = qrf_bas(params, objective, sense)
12% params - Structure with model parameters:
13% .M - Number of queues
14% .N - Total population
15% .f - Index of finite capacity queue (1-based)
16% .F - [M x 1] Capacity of each queue
17% .K - [M x 1] Number of phases
for each queue
18% .mu - {M x 1} cell, each mu{i}
is K(i) x K(i) completion rates
19% .v - {M x 1} cell, each v{i}
is K(i) x K(i) background rates
20% .r - [M x M] Routing probabilities
21% .MR - Number of blocking configurations
22% .BB - [MR x M] Blocking state (0/1)
23% .MM - [MR x 2] Blocking order (queue indices)
24% .ZZ - [MR x 1] Number of blocked queues in each config
25% .ZM - Maximum blocking depth
26% .MM1 - [MR x M] Extended blocking order info
28% objective - (optional)
'U1min' (
default),
'U1max', or queue index 1..M
29% sense - (optional)
'min' (
default) or
'max'
32% result - Structure with results:
33% .U - [M x 1] Utilization of each queue
34% .e - [M x max(K)] Effective utilization by phase
35% x - Raw solution vector
36% fval - Objective function value
37% exitflag - Solver exit flag
39 if nargin < 2 || isempty(objective)
42 if nargin < 3 || isempty(sense)
49 f = params.f; % finite capacity queue index
61 if isfield(params,
'verbose')
62 verbose = params.verbose;
67 % Compute transition rates q(i,j,k,h)
68 % q{i,j}
is a K(i) x K(i)
array (not load-dependent
for BAS)
72 q{i,j} = zeros(K(i), K(i));
76 q{i,j}(ki, hi) = r(i,j) * mu{i}(ki, hi);
78 q{i,j}(ki, hi) = v{i}(ki, hi) + r(i,i) * mu{i}(ki, hi);
85 %% Build variable indexing
86 % p2(j, nj, kj, i, ni, hi, m)
for j in 1:M, nj in 0:N, kj in 1:K(j),
87 % i in 1:M, ni in 0:N, hi in 1:K(i), m in 1:MR
88 % e(i, ki)
for i in 1:M, ki in 1:K(i)
90 if verbose; fprintf(
'Building variable index map...\n'); end
96 p2idx{j} = cell(N+1, K(j), M, MR);
101 p2idx{j}{nj+1, kj, i, m} = zeros(N+1, K(i));
104 varCount = varCount + 1;
105 p2idx{j}{nj+1, kj, i, m}(ni+1, hi) = varCount;
117 eidx{i} = zeros(K(i), 1);
119 varCount = varCount + 1;
120 eidx{i}(ki) = varCount;
125 if verbose; fprintf(
'Total variables: %d\n', nVars); end
134 getP2Idx = @(j, nj, kj, i, ni, hi, m) p2idx{j}{nj+1, kj, i, m}(ni+1, hi);
135 getEIdx = @(i, ki) eidx{i}(ki);
137 if verbose; fprintf(
'Building constraints...\n'); end
140 lb = zeros(nVars, 1);
143 %% ZERO constraints - fix infeasible states
144 if verbose; fprintf(
' ZERO constraints...\n'); end
152 idx = getP2Idx(j, nj, kj, i, ni, hi, m);
154 % ZERO1: i==j, nj==ni, h<>k
155 if i == j && nj == ni && hi ~= kj
159 % ZERO2: i==j, nj<>ni
160 if i == j && nj ~= ni
164 % ZERO3: i<>j, nj+ni > N
165 if i ~= j && nj + ni > N
174 % ZERO5: BB(m,j)==1 and nj==0
175 if m >= 2 && BB(m, j) == 1 && nj == 0
179 % ZERO7: BB(m,j)==1 and i<>j and i<>f and ni+nj+F(f)>N
180 if m >= 2 && BB(m, j) == 1 && i ~= j && i ~= f && ni + nj + F(f) > N
184 % ZERO8: finite queue not at capacity in blocking config
185 if j == f && nj >= 1 && nj <= F(f)-1 && m >= 2
196 % ZERO4: For m>=2 and j<>f, p2(j,nj,k,f,nf,h,m)=0 when nf < F(f)
206 idx = getP2Idx(j, nj, kj, f, nf, hf, m);
215 %% ONE: Normalization
216 if verbose; fprintf(
' ONE constraints...\n'); end
218 row = zeros(1, nVars);
222 idx = getP2Idx(j, nj, kj, j, nj, kj, m);
232 if verbose; fprintf(
' SYMMETRY constraints...\n'); end
234 for nj = 0:min(N, F(j))
240 for ni = 0:min(N, F(i))
241 if i ~= j && nj + ni > N
246 idx1 = getP2Idx(j, nj, kj, i, ni, hi, m);
247 idx2 = getP2Idx(i, ni, hi, j, nj, kj, m);
248 if ub(idx1) == 0 && ub(idx2) == 0
252 row = zeros(1, nVars);
267 if verbose; fprintf(
' MARGINALS constraints...\n'); end
270 for nj = 0:min(N, F(j))
276 row = zeros(1, nVars);
277 idx_diag = getP2Idx(j, nj, kj, j, nj, kj, m);
279 for ni = 0:min(N-nj, F(i))
281 idx = getP2Idx(j, nj, kj, i, ni, hi, m);
282 row(idx) = row(idx) - 1;
293 %% UEFF: e(i,ki) = sum of p2 where queue i
is not blocked
294 if verbose; fprintf(
' UEFF constraints...\n'); end
297 row = zeros(1, nVars);
298 idx_e = getEIdx(i, ki);
301 for nj = 0:min(N, F(j))
305 for ni = 1:min(N, F(i))
306 idx = getP2Idx(j, nj, kj, i, ni, ki, m);
307 row(idx) = row(idx) + 1;
319 %% THM1: Phase balance (Theorem 1)
320 % sum {j, h: j<>i or h<>k} q(i,j,k,h)*e(i,k) = sum {j, h: j<>i or h<>k} q(i,j,h,k)*e(i,h)
321 if verbose; fprintf(
' THM1 (Phase balance) constraints...\n'); end
324 row = zeros(1, nVars);
328 if j ~= i || hi ~= ki
329 idx_e = getEIdx(i, ki);
330 row(idx_e) = row(idx_e) + q{i,j}(ki, hi);
337 if j ~= i || hi ~= ki
338 idx_e = getEIdx(i, hi);
339 row(idx_e) = row(idx_e) - q{i,j}(hi, ki);
348 %% THM2: Population constraint (Theorem 2)
349 if verbose; fprintf(
' THM2 (Population) constraints...\n'); end
354 row = zeros(1, nVars);
355 % RHS: -N * p2(j,nj,kj,j,nj,kj,m)
356 idx_diag = getP2Idx(j, nj, kj, j, nj, kj, m);
362 idx = getP2Idx(j, nj, kj, i, ni, ki, m);
363 row(idx) = row(idx) + ni;
374 %% COR1: Second moment constraint (Corollary to Theorem 2)
375 % sum_{m,i,j,nj,ni,ki,kj} ni*nj*p2(j,nj,kj,i,ni,ki,m) = N^2
376 if verbose; fprintf(
' COR1 (Second moment) constraint...\n'); end
377 row = zeros(1, nVars);
385 idx = getP2Idx(j, nj, kj, i, ni, ki, m);
386 row(idx) = row(idx) + ni * nj;
397 %% THM30: Marginal balance
for ni=0 (per phase), i<>f
398 if verbose; fprintf(
' THM30 (Marginal balance ni=0) constraints...\n'); end
404 row = zeros(1, nVars);
405 % LHS: arrivals from j<>i,j<>f with BB(m,j)==0
415 idx = getP2Idx(j, nj, kj, i, 0, ui, m);
416 row(idx) = row(idx) + q{j,i}(kj, hj);
423 % LHS: arrivals from j==f with MM(m,1)<>i
429 idx = getP2Idx(f, nj, kj, i, 0, ui, m);
430 row(idx) = row(idx) + q{f,i}(kj, hj);
437 % RHS: departures from i at ni=1 to j<>i,j<>f with BB(m,i)==0
447 idx = getP2Idx(j, nj, hj, i, 1, ki, m);
448 row(idx) = row(idx) - q{i,j}(ki, ui);
455 % RHS: departures to j==f with BB(m,i)==0 and nj<F(f)
461 idx = getP2Idx(f, nj, hj, i, 1, ki, m);
462 row(idx) = row(idx) - q{i,f}(ki, ui);
468 % RHS: unblocking when BB(m,i)==1 and MM(m,1)==i
470 if BB(m, i) == 1 && MM(m, 1) == i
475 idx = getP2Idx(f, F(f), kf, i, 1, ui, m);
476 row(idx) = row(idx) - q{f,w}(kf, pf);
489 %% THM3: Marginal balance
for ni in 1:F(i)-1, i<>f
490 if verbose; fprintf(
' THM3 (Marginal balance) constraints...\n'); end
496 row = zeros(1, nVars);
497 % LHS: arrivals from j<>i,j<>f with BB(m,j)==0
508 idx = getP2Idx(j, nj, kj, i, ni, ui, m);
509 row(idx) = row(idx) + q{j,i}(kj, hj);
517 % LHS: arrivals from j==f with MM(m,1)<>i
524 idx = getP2Idx(f, nj, kj, i, ni, ui, m);
525 row(idx) = row(idx) + q{f,i}(kj, hj);
533 % RHS: departures from i at ni+1 to j<>i,j<>f with BB(m,i)==0
544 idx = getP2Idx(j, nj, uj, i, ni+1, ki, m);
545 row(idx) = row(idx) - q{i,j}(ki, hi);
553 % RHS: departures to j==f with BB(m,i)==0 and nj<F(f)
560 idx = getP2Idx(f, nj, uj, i, ni+1, ki, m);
561 row(idx) = row(idx) - q{i,f}(ki, hi);
568 % RHS: unblocking when BB(m,i)==1 and MM(m,1)==i
570 if BB(m, i) == 1 && MM(m, 1) == i
576 idx = getP2Idx(f, F(f), kf, i, ni+1, ki, m);
577 row(idx) = row(idx) - q{f,w}(kf, pf);
591 %% THM3f: Marginal balance
for i==f
592 if verbose; fprintf(
' THM3f (Marginal balance for finite queue) constraints...\n'); end
594 row = zeros(1, nVars);
595 % LHS: arrivals from j<>f with BB(m,j)==0 and ni<F(f)
607 idx = getP2Idx(j, nj, kj, f, ni, uf, m);
608 row(idx) = row(idx) + q{j,f}(kj, hj);
618 % RHS: departures from f at ni+1 to j<>f (only m=1, no blocking)
628 idx = getP2Idx(j, nj, uj, f, ni+1, kf, 1);
629 row(idx) = row(idx) - q{f,j}(kf, hf);
641 %% THM3I: Blocking depth balance (Theorem 4)
642 if verbose; fprintf(
' THM3I (Blocking depth balance) constraints...\n'); end
644 row = zeros(1, nVars);
645 % LHS: arrivals to f at F(f) from j<>f with BB(m,j)==0 and ZZ(m)==z
655 if BB(m, j) == 0 && ZZ(m) == z
656 idx = getP2Idx(j, nj, kj, f, F(f), uf, m);
657 row(idx) = row(idx) + q{j,f}(kj, hj);
665 % RHS: departures from f to j<>f with ZZ(m)==z+1
676 idx = getP2Idx(j, nj, uj, f, F(f), kf, m);
677 row(idx) = row(idx) - q{f,j}(kf, hf);
690 %% THM3L: Maximum blocking depth constraint
691 if verbose; fprintf(
' THM3L (Max blocking depth) constraints...\n'); end
696 row = zeros(1, nVars);
697 % LHS: arrivals from j<>f with BB(m,j)==0 and MM1(m,j)>0
699 if j == f || BB(m, j) ~= 0 || MM1(m, j) <= 0
706 idx = getP2Idx(j, nj, kj, f, F(f), uf, m);
707 row(idx) = row(idx) + q{j,f}(kj, hj);
713 % RHS: uses MM1(m,j) to index into blocking configuration
715 if j == f || BB(m, j) ~= 0 || MM1(m, j) <= 0
718 mp = MM1(m, j); % blocking configuration index
723 idx = getP2Idx(f, F(f), kf, f, F(f), kf, mp);
724 row(idx) = row(idx) - q{f,w}(kf, uf);
735 %% THM4: Queue-length bound inequality (Theorem 5)
736 if verbose; fprintf(
' THM4 (Queue-length bound) constraints...\n'); end
741 row = zeros(1, nVars);
742 % LHS: sum_t sum_ht sum_nj sum_nt nt * p2
747 idx = getP2Idx(j, nj, kj, t, nt, ht, m);
748 row(idx) = row(idx) + nt;
757 idx = getP2Idx(j, nj, kj, i, ni, hi, m);
758 row(idx) = row(idx) - N;
762 Aineq = [Aineq; -row];
769 %% Build objective function
770 if verbose; fprintf(
'Building objective function...\n'); end
774 if strcmp(objective,
'U1min') || strcmp(objective,
'U1max')
777 error('Unknown objective: %s', objective);
780 targetQueue = objective;
783 % Utilization = sum over m, k, n of p2(i,n,k,i,n,k,m)
785 for ki = 1:K(targetQueue)
786 for ni = 1:F(targetQueue)
787 idx = getP2Idx(targetQueue, ni, ki, targetQueue, ni, ki, m);
793 if strcmp(sense, 'max')
798 if verbose; fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
799 nVars, size(Aeq, 1), size(Aineq, 1)); end
802 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
804 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
807 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
809 if strcmp(sense, 'max')
815 result.objective = fval;
816 result.exitflag = exitflag;
819 % Compute utilizations
820 result.U = zeros(M, 1);
821 result.e = zeros(M, max(K));
825 result.e(i, ki) = x(getEIdx(i, ki));
831 idx = getP2Idx(i, ni, ki, i, ni, ki, m);
832 result.U(i) = result.U(i) + x(idx);
839 if verbose; fprintf('\n=== Results ===\n'); end
840 if verbose; fprintf('Objective value: %f\n', fval); end
841 if verbose; fprintf('Exit flag: %d\n', exitflag); end
842 if exitflag > 0 && verbose
843 fprintf('\nUtilizations:\n');
845 fprintf(' Queue %d: U = %.6f\n', i, result.U(i));
847 fprintf('\nEffective utilizations by phase:\n');
849 fprintf(' Queue %d: e = [', i);
851 fprintf('%.6f ', result.e(i, ki));