1function [result, x, fval, exitflag] = qrf_rsrd(params, objective, sense)
2% QRF_RSRD - Quadratic Reduction Framework
for RS-
RD blocking networks
4% MATLAB port of the AMPL model qrboundsrsrd_skel.mod
7% [result, x, fval, exitflag] = qrf_rsrd(params)
8% [result, x, fval, exitflag] = qrf_rsrd(params, objective)
9% [result, x, fval, exitflag] = qrf_rsrd(params, objective, sense)
12% params - Structure with model parameters:
13% .M - Number of queues
14% .N - Total population
15% .F - [M x 1] Capacity of each queue
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% .alpha - (optional) {M x 1} cell, each alpha{i}
is [N x 1] load-dependent rates
22% objective - (optional)
'U1min' (
default),
'U1max', or queue index 1..M
23% sense - (optional)
'min' (
default) or
'max'
26% result - Structure with results:
27% .U - [M x 1] Utilization of each queue
28% .Ueff - [M x 1] Effective utilization
29% .pb - [M x 1] Blocking probability
30% .p2 - Decision variable tensor (marginal probabilities)
31% x - Raw solution vector
32% fval - Objective function value
33% exitflag - Solver exit flag
35 if nargin < 2 || isempty(objective)
38 if nargin < 3 || isempty(sense)
50 if isfield(params,
'verbose')
51 verbose = params.verbose;
56 % Default alpha (load-independent)
57 if isfield(params, 'alpha') && ~isempty(params.alpha)
62 alpha{i} = ones(N, 1);
66 % Compute transition rates q(i,j,k,h,n)
67 % q{i,j}
is a K(i) x K(i) x (N+1)
array
71 q{i,j} = zeros(K(i), K(i), N+1);
74 for n = 1:N % n=0 gives q=0
76 q{i,j}(ki, hi, n+1) = r(i,j) * mu{i}(ki, hi) * alpha{i}(n);
78 q{i,j}(ki, hi, n+1) = (v{i}(ki, hi) + r(i,i) * mu{i}(ki, hi)) * alpha{i}(n);
86 %% Build variable indexing
87 % p2(j, nj, kj, i, ni, hi)
for j in 1:M, nj in 0:F(j), kj in 1:K(j),
88 % i in 1:M, ni in 0:F(i), hi in 1:K(i)
90 % Count variables and build index
map
91 if verbose; fprintf(
'Building variable index map...\n'); end
95 p2idx{j} = cell(F(j)+1, K(j), M);
99 p2idx{j}{nj+1, kj, i} = zeros(F(i)+1, K(i));
102 varCount = varCount + 1;
103 p2idx{j}{nj+1, kj, i}(ni+1, hi) = varCount;
112 % U and Ueff variables: U(i,k,ni) and Ueff(i,k,ni)
for ni >= 1
114 Ueffidx = cell(M, 1);
116 Uidx{i} = zeros(K(i), F(i));
117 Ueffidx{i} = zeros(K(i), F(i));
120 varCount = varCount + 1;
121 Uidx{i}(ki, ni) = varCount;
122 varCount = varCount + 1;
123 Ueffidx{i}(ki, ni) = varCount;
129 if verbose; fprintf(
'Total variables: %d (p2: %d, U/Ueff: %d)\n', nVars, nP2Vars, nVars - nP2Vars); end
137 % Helper to get variable index
138 getIdx = @(j, nj, kj, i, ni, hi) p2idx{j}{nj+1, kj, i}(ni+1, hi);
139 getUIdx = @(i, ki, ni) Uidx{i}(ki, ni);
140 getUeffIdx = @(i, ki, ni) Ueffidx{i}(ki, ni);
142 if verbose; fprintf(
'Building constraints...\n'); end
144 %% ONE: Normalization - sum over nj, kj equals 1
for each j
145 if verbose; fprintf(
' ONE constraints...\n'); end
147 row = zeros(1, nVars);
150 idx = getIdx(j, nj, kj, j, nj, kj);
158 %% ZERO constraints - fix infeasible states to zero
159 if verbose; fprintf(
' ZERO constraints...\n'); end
160 lb = zeros(nVars, 1);
162 % U and Ueff bounds are [0, 1]
166 ub(getUIdx(i, ki, ni)) = 1;
167 ub(getUeffIdx(i, ki, ni)) = 1;
178 idx = getIdx(j, nj, kj, i, ni, hi);
180 % ZERO1: i==j, nj==ni, h<>k
181 if i == j && nj == ni && hi ~= kj
185 % ZERO2: i==j, nj<>ni
186 if i == j && nj ~= ni
190 % ZERO3: i<>j, nj+ni > N
191 if i ~= j && nj + ni > N
195 % ZERO6: i<>j, N-nj-ni > sum of other capacities
197 sumOtherF = sum(F) - F(i) - F(j);
198 if N - nj - ni > sumOtherF
208 % ZERO7: N-nj > sum of other capacities (
for diagonal)
211 sumOtherF = sum(F) - F(j);
212 if N - nj > sumOtherF
213 idx = getIdx(j, nj, kj, j, nj, kj);
220 %% SYMMETRY: p2(i,ni,hi,j,nj,kj) = p2(j,nj,kj,i,ni,hi)
221 if verbose; fprintf(
' SYMMETRY constraints...\n'); end
227 continue; % avoid duplicate constraints
231 idx1 = getIdx(j, nj, kj, i, ni, hi);
232 idx2 = getIdx(i, ni, hi, j, nj, kj);
234 row = zeros(1, nVars);
247 %% MARGINALS: p2(j,nj,kj,j,nj,kj) = sum over ni,hi of p2(j,nj,kj,i,ni,hi)
248 if verbose; fprintf(
' MARGINALS constraints...\n'); end
256 row = zeros(1, nVars);
257 idx_diag = getIdx(j, nj, kj, j, nj, kj);
261 idx = getIdx(j, nj, kj, i, ni, hi);
262 row(idx) = row(idx) - 1;
272 %% UCLASSIC: U(i,k,ni) = p2(i,ni,k,i,ni,k)
273 if verbose; fprintf(
' UCLASSIC constraints...\n'); end
277 row = zeros(1, nVars);
278 idx_U = getUIdx(i, ki, ni);
279 idx_p2 = getIdx(i, ni, ki, i, ni, ki);
288 %% UEFFS: Ueff(i,k,ni) = p2(i,ni,k,i,ni,k) - sum_{j: r(i,j)>0} r(i,j)*p2(i,ni,k,j,F(j),h)
289 if verbose; fprintf(
' UEFFS constraints...\n'); end
293 row = zeros(1, nVars);
294 idx_Ueff = getUeffIdx(i, ki, ni);
295 idx_p2_diag = getIdx(i, ni, ki, i, ni, ki);
297 row(idx_p2_diag) = -1;
300 if j ~= i && r(i,j) > 0
302 idx_block = getIdx(i, ni, ki, j, F(j), hj);
303 row(idx_block) = r(i,j);
313 %% THM2: Phase balance (Theorem 1 - THM:sdeffective)
314 % sum_{ni} (sum_{j<>i, h<>k} q(i,j,k,h,ni)*Ueff(i,k,ni) + sum_{h<>k} q(i,i,k,h,ni)*U(i,k,ni))
315 % = sum_{ni} (sum_{j<>i, h<>k} q(i,j,h,k,ni)*Ueff(i,h,ni) + sum_{h<>k} q(i,i,h,k,ni)*U(i,h,ni))
316 if verbose; fprintf(
' THM2 (Phase balance) constraints...\n'); end
319 row = zeros(1, nVars);
322 % Terms with Ueff (j<>i)
331 coef = q{i,j}(ki, hi, ni+1);
332 idx_Ueff = getUeffIdx(i, ki, ni);
333 row(idx_Ueff) = row(idx_Ueff) + coef;
336 % Terms with U (j==i, h<>k)
341 coef = q{i,i}(ki, hi, ni+1);
342 idx_p2 = getIdx(i, ni, ki, i, ni, ki);
343 row(idx_p2) = row(idx_p2) + coef;
346 % RHS terms (subtract)
348 % Terms with Ueff (j<>i)
357 coef = q{i,j}(hi, ki, ni+1);
358 idx_Ueff = getUeffIdx(i, hi, ni);
359 row(idx_Ueff) = row(idx_Ueff) - coef;
362 % Terms with U (j==i, h<>k)
367 coef = q{i,i}(hi, ki, ni+1);
368 idx_p2 = getIdx(i, ni, hi, i, ni, hi);
369 row(idx_p2) = row(idx_p2) - coef;
377 %% THM1: Population constraint (Theorem 2)
378 % sum_i sum_ni sum_hi ni * p2(j,nj,kj,i,ni,hi) = N * p2(j,nj,kj,j,nj,kj)
379 if verbose; fprintf(
' THM1 (Population) constraints...\n'); end
383 row = zeros(1, nVars);
384 % RHS: -N * p2(j,nj,kj,j,nj,kj)
385 idx_diag = getIdx(j, nj, kj, j, nj, kj);
389 for ni = 1:F(i) % ni >= 1
391 idx = getIdx(j, nj, kj, i, ni, hi);
392 row(idx) = row(idx) + ni;
402 %% THM3a: Marginal balance
for ni in 1:F(i)-1
403 if verbose; fprintf(
' THM3a (Marginal balance) constraints...\n'); end
406 row = zeros(1, nVars);
407 % LHS: arrivals to queue i
416 idx = getIdx(j, nj, kj, i, ni, ui);
417 row(idx) = row(idx) + q{j,i}(kj, hj, nj+1);
423 % RHS: departures from queue i at ni+1
432 idx = getIdx(i, ni+1, ki, j, nj, uj);
433 row(idx) = row(idx) - q{i,j}(ki, hi, ni+2);
444 %% THM3b: Marginal balance
for ni=0, per phase
445 if verbose; fprintf(
' THM3b (Marginal balance ni=0) constraints...\n'); end
448 row = zeros(1, nVars);
449 % LHS: arrivals to queue i at ni=0
457 idx = getIdx(j, nj, kj, i, 0, ui);
458 row(idx) = row(idx) + q{j,i}(kj, hj, nj+1);
463 % RHS: departures from queue i at ni=1
471 idx = getIdx(i, 1, ki, j, nj, hj);
472 row(idx) = row(idx) - q{i,j}(ki, ui, 2);
482 %% QBAL: Queue balance constraint
483 % This
is a complex balance equation that tightens the bounds
484 if verbose; fprintf(
' QBAL (Queue balance) constraints...\n'); end
487 row = zeros(1, nVars);
489 % LHS Term 1: sum{h<>k} sum{j<>i} sum{ni} sum{u} sum{nj} q[i,j,k,h,ni]*ni*p2[i,ni,k,j,nj,u]
501 coef = q{i,j}(ki, hi, ni+1) * ni;
502 idx = getIdx(i, ni, ki, j, nj, uj);
503 row(idx) = row(idx) + coef;
510 % LHS Term 2: sum{h<>k} sum{ni} q[i,i,k,h,ni]*ni*p2[i,ni,k,i,ni,k]
516 coef = q{i,i}(ki, hi, ni+1) * ni;
517 idx = getIdx(i, ni, ki, i, ni, ki);
518 row(idx) = row(idx) + coef;
522 % LHS Term 3: sum{j<>i} sum{h} sum{ni} sum{u} sum{nj<=min(F(j)-1,N-ni)} q[i,j,h,k,ni]*p2[i,ni,h,j,nj,u]
530 maxNj = min(F(j)-1, N-ni);
532 coef = q{i,j}(hi, ki, ni+1);
533 idx = getIdx(i, ni, hi, j, nj, uj);
534 row(idx) = row(idx) + coef;
541 % RHS Term 1: sum{j<>i} sum{h} sum{ni<=F(i)-1} sum{u} sum{nj>=1} q[j,i,h,u,nj]*p2[i,ni,k,j,nj,h]
550 coef = q{j,i}(hj, uj, nj+1);
551 idx = getIdx(i, ni, ki, j, nj, hj);
552 row(idx) = row(idx) - coef;
559 % RHS Term 2: sum{h<>k} sum{ni} q[i,i,h,k,ni]*ni*p2[i,ni,h,i,ni,h]
565 coef = q{i,i}(hi, ki, ni+1) * ni;
566 idx = getIdx(i, ni, hi, i, ni, hi);
567 row(idx) = row(idx) - coef;
571 % RHS Term 3: sum{h<>k} sum{j<>i} sum{ni} sum{u} sum{nj} q[i,j,h,k,ni]*ni*p2[i,ni,h,j,nj,u]
583 coef = q{i,j}(hi, ki, ni+1) * ni;
584 idx = getIdx(i, ni, hi, j, nj, uj);
585 row(idx) = row(idx) - coef;
597 %% THM4: Queue-length bound inequality (Theorem 5)
598 % sum_t sum_ht sum_nj sum_nt nt*p2(j,nj,kj,t,nt,ht) >= N * sum_hi sum_nj sum_ni p2(j,nj,kj,i,ni,hi)
599 if verbose; fprintf(
' THM4 (Queue-length bound) constraints...\n'); end
603 row = zeros(1, nVars);
604 % LHS: sum_t sum_ht sum_nj sum_nt nt * p2
608 for nt = 1:F(t) % nt >= 1
609 idx = getIdx(j, nj, kj, t, nt, ht);
610 row(idx) = row(idx) + nt;
618 for ni = 1:F(i) % ni >= 1
619 idx = getIdx(j, nj, kj, i, ni, hi);
620 row(idx) = row(idx) - N;
624 Aineq = [Aineq; -row]; % >= becomes <= with negation
630 %% Build objective function
631 if verbose; fprintf(
'Building objective function...\n'); end
635 if strcmp(objective,
'U1min') || strcmp(objective,
'U1max')
638 error('Unknown objective: %s', objective);
641 targetQueue = objective;
644 % Utilization = sum over k, n of p2(i,n,k,i,n,k)
645 for ki = 1:K(targetQueue)
646 for ni = 1:F(targetQueue)
647 idx = getIdx(targetQueue, ni, ki, targetQueue, ni, ki);
652 if strcmp(sense, 'max')
657 if verbose; fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
658 nVars, size(Aeq, 1), size(Aineq, 1)); end
661 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
663 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
666 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
668 if strcmp(sense, 'max')
674 result.objective = fval;
675 result.exitflag = exitflag;
678 % Compute utilizations from U and Ueff variables
679 result.U = zeros(M, 1);
680 result.Ueff = zeros(M, 1);
681 result.pb = zeros(M, 1);
684 % U(i) = sum over k, ni of U(i,k,ni)
687 idx_U = getUIdx(i, ki, ni);
688 idx_Ueff = getUeffIdx(i, ki, ni);
689 result.U(i) = result.U(i) + x(idx_U);
690 result.Ueff(i) = result.Ueff(i) + x(idx_Ueff);
693 result.pb(i) = result.U(i) - result.Ueff(i);
696 % Store p2 as a function handle for easy access
697 result.getP2 = @(j, nj, kj, i, ni, hi) x(getIdx(j, nj, kj, i, ni, hi));
700 if verbose; fprintf('\n=== Results ===\n'); end
701 if verbose; fprintf('Objective value: %f\n', fval); end
702 if verbose; fprintf('Exit flag: %d\n', exitflag); end
703 if exitflag > 0 && verbose
704 fprintf('\nUtilizations:\n');
706 fprintf(' Queue %d: U = %.6f, Ueff = %.6f, pb = %.6f\n', ...
707 i, result.U(i), result.Ueff(i), result.pb(i));