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
62 % Compute transition rates q(i,j,k,h)
63 % q{i,j}
is a K(i) x K(i) array (not load-dependent
for BAS)
67 q{i,j} = zeros(K(i), K(i));
71 q{i,j}(ki, hi) = r(i,j) * mu{i}(ki, hi);
73 q{i,j}(ki, hi) = v{i}(ki, hi) + r(i,i) * mu{i}(ki, hi);
80 %% Build variable indexing
81 % p2(j, nj, kj, i, ni, hi, m)
for j in 1:M, nj in 0:N, kj in 1:K(j),
82 % i in 1:M, ni in 0:N, hi in 1:K(i), m in 1:MR
83 % e(i, ki)
for i in 1:M, ki in 1:K(i)
85 fprintf(
'Building variable index map...\n');
91 p2idx{j} = cell(N+1, K(j), M, MR);
96 p2idx{j}{nj+1, kj, i, m} = zeros(N+1, K(i));
99 varCount = varCount + 1;
100 p2idx{j}{nj+1, kj, i, m}(ni+1, hi) = varCount;
112 eidx{i} = zeros(K(i), 1);
114 varCount = varCount + 1;
115 eidx{i}(ki) = varCount;
120 fprintf(
'Total variables: %d\n', nVars);
129 getP2Idx = @(j, nj, kj, i, ni, hi, m) p2idx{j}{nj+1, kj, i, m}(ni+1, hi);
130 getEIdx = @(i, ki) eidx{i}(ki);
132 fprintf(
'Building constraints...\n');
135 lb = zeros(nVars, 1);
138 %% ZERO constraints - fix infeasible states
139 fprintf(
' ZERO constraints...\n');
147 idx = getP2Idx(j, nj, kj, i, ni, hi, m);
149 % ZERO1: i==j, nj==ni, h<>k
150 if i == j && nj == ni && hi ~= kj
154 % ZERO2: i==j, nj<>ni
155 if i == j && nj ~= ni
159 % ZERO3: i<>j, nj+ni > N
160 if i ~= j && nj + ni > N
169 % ZERO5: BB(m,j)==1 and nj==0
170 if m >= 2 && BB(m, j) == 1 && nj == 0
174 % ZERO7: BB(m,j)==1 and i<>j and i<>f and ni+nj+F(f)>N
175 if m >= 2 && BB(m, j) == 1 && i ~= j && i ~= f && ni + nj + F(f) > N
179 % ZERO8: finite queue not at capacity in blocking config
180 if j == f && nj >= 1 && nj <= F(f)-1 && m >= 2
191 % ZERO4: For m>=2 and j<>f, p2(j,nj,k,f,nf,h,m)=0 when nf < F(f)
201 idx = getP2Idx(j, nj, kj, f, nf, hf, m);
210 %% ONE: Normalization
211 fprintf(
' ONE constraints...\n');
213 row = zeros(1, nVars);
217 idx = getP2Idx(j, nj, kj, j, nj, kj, m);
227 fprintf(
' SYMMETRY constraints...\n');
229 for nj = 0:min(N, F(j))
235 for ni = 0:min(N, F(i))
236 if i ~= j && nj + ni > N
241 idx1 = getP2Idx(j, nj, kj, i, ni, hi, m);
242 idx2 = getP2Idx(i, ni, hi, j, nj, kj, m);
243 if ub(idx1) == 0 && ub(idx2) == 0
247 row = zeros(1, nVars);
262 fprintf(
' MARGINALS constraints...\n');
265 for nj = 0:min(N, F(j))
271 row = zeros(1, nVars);
272 idx_diag = getP2Idx(j, nj, kj, j, nj, kj, m);
274 for ni = 0:min(N-nj, F(i))
276 idx = getP2Idx(j, nj, kj, i, ni, hi, m);
277 row(idx) = row(idx) - 1;
288 %% UEFF: e(i,ki) = sum of p2 where queue i
is not blocked
289 fprintf(
' UEFF constraints...\n');
292 row = zeros(1, nVars);
293 idx_e = getEIdx(i, ki);
296 for nj = 0:min(N, F(j))
300 for ni = 1:min(N, F(i))
301 idx = getP2Idx(j, nj, kj, i, ni, ki, m);
302 row(idx) = row(idx) + 1;
314 %% THM1: Phase balance (Theorem 1)
315 % 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)
316 fprintf(
' THM1 (Phase balance) constraints...\n');
319 row = zeros(1, nVars);
323 if j ~= i || hi ~= ki
324 idx_e = getEIdx(i, ki);
325 row(idx_e) = row(idx_e) + q{i,j}(ki, hi);
332 if j ~= i || hi ~= ki
333 idx_e = getEIdx(i, hi);
334 row(idx_e) = row(idx_e) - q{i,j}(hi, ki);
343 %% THM2: Population constraint (Theorem 2)
344 fprintf(
' THM2 (Population) constraints...\n');
349 row = zeros(1, nVars);
350 % RHS: -N * p2(j,nj,kj,j,nj,kj,m)
351 idx_diag = getP2Idx(j, nj, kj, j, nj, kj, m);
357 idx = getP2Idx(j, nj, kj, i, ni, ki, m);
358 row(idx) = row(idx) + ni;
369 %% COR1: Second moment constraint (Corollary to Theorem 2)
370 % sum_{m,i,j,nj,ni,ki,kj} ni*nj*p2(j,nj,kj,i,ni,ki,m) = N^2
371 fprintf(
' COR1 (Second moment) constraint...\n');
372 row = zeros(1, nVars);
380 idx = getP2Idx(j, nj, kj, i, ni, ki, m);
381 row(idx) = row(idx) + ni * nj;
392 %% THM30: Marginal balance
for ni=0 (per phase), i<>f
393 fprintf(
' THM30 (Marginal balance ni=0) constraints...\n');
399 row = zeros(1, nVars);
400 % LHS: arrivals from j<>i,j<>f with BB(m,j)==0
410 idx = getP2Idx(j, nj, kj, i, 0, ui, m);
411 row(idx) = row(idx) + q{j,i}(kj, hj);
418 % LHS: arrivals from j==f with MM(m,1)<>i
424 idx = getP2Idx(f, nj, kj, i, 0, ui, m);
425 row(idx) = row(idx) + q{f,i}(kj, hj);
432 % RHS: departures from i at ni=1 to j<>i,j<>f with BB(m,i)==0
442 idx = getP2Idx(j, nj, hj, i, 1, ki, m);
443 row(idx) = row(idx) - q{i,j}(ki, ui);
450 % RHS: departures to j==f with BB(m,i)==0 and nj<F(f)
456 idx = getP2Idx(f, nj, hj, i, 1, ki, m);
457 row(idx) = row(idx) - q{i,f}(ki, ui);
463 % RHS: unblocking when BB(m,i)==1 and MM(m,1)==i
465 if BB(m, i) == 1 && MM(m, 1) == i
470 idx = getP2Idx(f, F(f), kf, i, 1, ui, m);
471 row(idx) = row(idx) - q{f,w}(kf, pf);
484 %% THM3: Marginal balance
for ni in 1:F(i)-1, i<>f
485 fprintf(
' THM3 (Marginal balance) constraints...\n');
491 row = zeros(1, nVars);
492 % LHS: arrivals from j<>i,j<>f with BB(m,j)==0
503 idx = getP2Idx(j, nj, kj, i, ni, ui, m);
504 row(idx) = row(idx) + q{j,i}(kj, hj);
512 % LHS: arrivals from j==f with MM(m,1)<>i
519 idx = getP2Idx(f, nj, kj, i, ni, ui, m);
520 row(idx) = row(idx) + q{f,i}(kj, hj);
528 % RHS: departures from i at ni+1 to j<>i,j<>f with BB(m,i)==0
539 idx = getP2Idx(j, nj, uj, i, ni+1, ki, m);
540 row(idx) = row(idx) - q{i,j}(ki, hi);
548 % RHS: departures to j==f with BB(m,i)==0 and nj<F(f)
555 idx = getP2Idx(f, nj, uj, i, ni+1, ki, m);
556 row(idx) = row(idx) - q{i,f}(ki, hi);
563 % RHS: unblocking when BB(m,i)==1 and MM(m,1)==i
565 if BB(m, i) == 1 && MM(m, 1) == i
571 idx = getP2Idx(f, F(f), kf, i, ni+1, ki, m);
572 row(idx) = row(idx) - q{f,w}(kf, pf);
586 %% THM3f: Marginal balance
for i==f
587 fprintf(
' THM3f (Marginal balance for finite queue) constraints...\n');
589 row = zeros(1, nVars);
590 % LHS: arrivals from j<>f with BB(m,j)==0 and ni<F(f)
602 idx = getP2Idx(j, nj, kj, f, ni, uf, m);
603 row(idx) = row(idx) + q{j,f}(kj, hj);
613 % RHS: departures from f at ni+1 to j<>f (only m=1, no blocking)
623 idx = getP2Idx(j, nj, uj, f, ni+1, kf, 1);
624 row(idx) = row(idx) - q{f,j}(kf, hf);
636 %% THM3I: Blocking depth balance (Theorem 4)
637 fprintf(
' THM3I (Blocking depth balance) constraints...\n');
639 row = zeros(1, nVars);
640 % LHS: arrivals to f at F(f) from j<>f with BB(m,j)==0 and ZZ(m)==z
650 if BB(m, j) == 0 && ZZ(m) == z
651 idx = getP2Idx(j, nj, kj, f, F(f), uf, m);
652 row(idx) = row(idx) + q{j,f}(kj, hj);
660 % RHS: departures from f to j<>f with ZZ(m)==z+1
671 idx = getP2Idx(j, nj, uj, f, F(f), kf, m);
672 row(idx) = row(idx) - q{f,j}(kf, hf);
685 %% THM3L: Maximum blocking depth constraint
686 fprintf(
' THM3L (Max blocking depth) constraints...\n');
691 row = zeros(1, nVars);
692 % LHS: arrivals from j<>f with BB(m,j)==0 and MM1(m,j)>0
694 if j == f || BB(m, j) ~= 0 || MM1(m, j) <= 0
701 idx = getP2Idx(j, nj, kj, f, F(f), uf, m);
702 row(idx) = row(idx) + q{j,f}(kj, hj);
708 % RHS: uses MM1(m,j) to index into blocking configuration
710 if j == f || BB(m, j) ~= 0 || MM1(m, j) <= 0
713 mp = MM1(m, j); % blocking configuration index
718 idx = getP2Idx(f, F(f), kf, f, F(f), kf, mp);
719 row(idx) = row(idx) - q{f,w}(kf, uf);
730 %% THM4: Queue-length bound inequality (Theorem 5)
731 fprintf(
' THM4 (Queue-length bound) constraints...\n');
736 row = zeros(1, nVars);
737 % LHS: sum_t sum_ht sum_nj sum_nt nt * p2
742 idx = getP2Idx(j, nj, kj, t, nt, ht, m);
743 row(idx) = row(idx) + nt;
752 idx = getP2Idx(j, nj, kj, i, ni, hi, m);
753 row(idx) = row(idx) - N;
757 Aineq = [Aineq; -row];
764 %% Build objective function
765 fprintf(
'Building objective function...\n');
769 if strcmp(objective,
'U1min') || strcmp(objective,
'U1max')
772 error('Unknown objective: %s', objective);
775 targetQueue = objective;
778 % Utilization = sum over m, k, n of p2(i,n,k,i,n,k,m)
780 for ki = 1:K(targetQueue)
781 for ni = 1:F(targetQueue)
782 idx = getP2Idx(targetQueue, ni, ki, targetQueue, ni, ki, m);
788 if strcmp(sense, 'max')
793 fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
794 nVars, size(Aeq, 1), size(Aineq, 1));
796 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
798 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
800 if strcmp(sense, 'max')
806 result.objective = fval;
807 result.exitflag = exitflag;
810 % Compute utilizations
811 result.U = zeros(M, 1);
812 result.e = zeros(M, max(K));
816 result.e(i, ki) = x(getEIdx(i, ki));
822 idx = getP2Idx(i, ni, ki, i, ni, ki, m);
823 result.U(i) = result.U(i) + x(idx);
830 fprintf('\n=== Results ===\n');
831 fprintf('Objective value: %f\n', fval);
832 fprintf('Exit flag: %d\n', exitflag);
834 fprintf('\nUtilizations:\n');
836 fprintf(' Queue %d: U = %.6f\n', i, result.U(i));
838 fprintf('\nEffective utilizations by phase:\n');
840 fprintf(' Queue %d: e = [', i);
842 fprintf('%.6f ', result.e(i, ki));