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)
51 % Default alpha (load-independent)
52 if isfield(params,
'alpha') && ~isempty(params.alpha)
57 alpha{i} = ones(N, 1);
61 % Compute transition rates q(i,j,k,h,n)
62 % q{i,j}
is a K(i) x K(i) x (N+1) array
66 q{i,j} = zeros(K(i), K(i), N+1);
69 for n = 1:N % n=0 gives q=0
71 q{i,j}(ki, hi, n+1) = r(i,j) * mu{i}(ki, hi) * alpha{i}(n);
73 q{i,j}(ki, hi, n+1) = (v{i}(ki, hi) + r(i,i) * mu{i}(ki, hi)) * alpha{i}(n);
81 %% Build variable indexing
82 % p2(j, nj, kj, i, ni, hi)
for j in 1:M, nj in 0:F(j), kj in 1:K(j),
83 % i in 1:M, ni in 0:F(i), hi in 1:K(i)
85 % Count variables and build index
map
86 fprintf(
'Building variable index map...\n');
90 p2idx{j} = cell(F(j)+1, K(j), M);
94 p2idx{j}{nj+1, kj, i} = zeros(F(i)+1, K(i));
97 varCount = varCount + 1;
98 p2idx{j}{nj+1, kj, i}(ni+1, hi) = varCount;
107 % U and Ueff variables: U(i,k,ni) and Ueff(i,k,ni)
for ni >= 1
109 Ueffidx = cell(M, 1);
111 Uidx{i} = zeros(K(i), F(i));
112 Ueffidx{i} = zeros(K(i), F(i));
115 varCount = varCount + 1;
116 Uidx{i}(ki, ni) = varCount;
117 varCount = varCount + 1;
118 Ueffidx{i}(ki, ni) = varCount;
124 fprintf(
'Total variables: %d (p2: %d, U/Ueff: %d)\n', nVars, nP2Vars, nVars - nP2Vars);
132 % Helper to get variable index
133 getIdx = @(j, nj, kj, i, ni, hi) p2idx{j}{nj+1, kj, i}(ni+1, hi);
134 getUIdx = @(i, ki, ni) Uidx{i}(ki, ni);
135 getUeffIdx = @(i, ki, ni) Ueffidx{i}(ki, ni);
137 fprintf(
'Building constraints...\n');
139 %% ONE: Normalization - sum over nj, kj equals 1
for each j
140 fprintf(
' ONE constraints...\n');
142 row = zeros(1, nVars);
145 idx = getIdx(j, nj, kj, j, nj, kj);
153 %% ZERO constraints - fix infeasible states to zero
154 fprintf(
' ZERO constraints...\n');
155 lb = zeros(nVars, 1);
157 % U and Ueff bounds are [0, 1]
161 ub(getUIdx(i, ki, ni)) = 1;
162 ub(getUeffIdx(i, ki, ni)) = 1;
173 idx = getIdx(j, nj, kj, i, ni, hi);
175 % ZERO1: i==j, nj==ni, h<>k
176 if i == j && nj == ni && hi ~= kj
180 % ZERO2: i==j, nj<>ni
181 if i == j && nj ~= ni
185 % ZERO3: i<>j, nj+ni > N
186 if i ~= j && nj + ni > N
190 % ZERO6: i<>j, N-nj-ni > sum of other capacities
192 sumOtherF = sum(F) - F(i) - F(j);
193 if N - nj - ni > sumOtherF
203 % ZERO7: N-nj > sum of other capacities (
for diagonal)
206 sumOtherF = sum(F) - F(j);
207 if N - nj > sumOtherF
208 idx = getIdx(j, nj, kj, j, nj, kj);
215 %% SYMMETRY: p2(i,ni,hi,j,nj,kj) = p2(j,nj,kj,i,ni,hi)
216 fprintf(
' SYMMETRY constraints...\n');
222 continue; % avoid duplicate constraints
226 idx1 = getIdx(j, nj, kj, i, ni, hi);
227 idx2 = getIdx(i, ni, hi, j, nj, kj);
229 row = zeros(1, nVars);
242 %% MARGINALS: p2(j,nj,kj,j,nj,kj) = sum over ni,hi of p2(j,nj,kj,i,ni,hi)
243 fprintf(
' MARGINALS constraints...\n');
251 row = zeros(1, nVars);
252 idx_diag = getIdx(j, nj, kj, j, nj, kj);
256 idx = getIdx(j, nj, kj, i, ni, hi);
257 row(idx) = row(idx) - 1;
267 %% UCLASSIC: U(i,k,ni) = p2(i,ni,k,i,ni,k)
268 fprintf(
' UCLASSIC constraints...\n');
272 row = zeros(1, nVars);
273 idx_U = getUIdx(i, ki, ni);
274 idx_p2 = getIdx(i, ni, ki, i, ni, ki);
283 %% 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)
284 fprintf(
' UEFFS constraints...\n');
288 row = zeros(1, nVars);
289 idx_Ueff = getUeffIdx(i, ki, ni);
290 idx_p2_diag = getIdx(i, ni, ki, i, ni, ki);
292 row(idx_p2_diag) = -1;
295 if j ~= i && r(i,j) > 0
297 idx_block = getIdx(i, ni, ki, j, F(j), hj);
298 row(idx_block) = r(i,j);
308 %% THM2: Phase balance (Theorem 1 - THM:sdeffective)
309 % 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))
310 % = 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))
311 fprintf(
' THM2 (Phase balance) constraints...\n');
314 row = zeros(1, nVars);
317 % Terms with Ueff (j<>i)
326 coef = q{i,j}(ki, hi, ni+1);
327 idx_Ueff = getUeffIdx(i, ki, ni);
328 row(idx_Ueff) = row(idx_Ueff) + coef;
331 % Terms with U (j==i, h<>k)
336 coef = q{i,i}(ki, hi, ni+1);
337 idx_p2 = getIdx(i, ni, ki, i, ni, ki);
338 row(idx_p2) = row(idx_p2) + coef;
341 % RHS terms (subtract)
343 % Terms with Ueff (j<>i)
352 coef = q{i,j}(hi, ki, ni+1);
353 idx_Ueff = getUeffIdx(i, hi, ni);
354 row(idx_Ueff) = row(idx_Ueff) - coef;
357 % Terms with U (j==i, h<>k)
362 coef = q{i,i}(hi, ki, ni+1);
363 idx_p2 = getIdx(i, ni, hi, i, ni, hi);
364 row(idx_p2) = row(idx_p2) - coef;
372 %% THM1: Population constraint (Theorem 2)
373 % sum_i sum_ni sum_hi ni * p2(j,nj,kj,i,ni,hi) = N * p2(j,nj,kj,j,nj,kj)
374 fprintf(
' THM1 (Population) constraints...\n');
378 row = zeros(1, nVars);
379 % RHS: -N * p2(j,nj,kj,j,nj,kj)
380 idx_diag = getIdx(j, nj, kj, j, nj, kj);
384 for ni = 1:F(i) % ni >= 1
386 idx = getIdx(j, nj, kj, i, ni, hi);
387 row(idx) = row(idx) + ni;
397 %% THM3a: Marginal balance
for ni in 1:F(i)-1
398 fprintf(
' THM3a (Marginal balance) constraints...\n');
401 row = zeros(1, nVars);
402 % LHS: arrivals to queue i
411 idx = getIdx(j, nj, kj, i, ni, ui);
412 row(idx) = row(idx) + q{j,i}(kj, hj, nj+1);
418 % RHS: departures from queue i at ni+1
427 idx = getIdx(i, ni+1, ki, j, nj, uj);
428 row(idx) = row(idx) - q{i,j}(ki, hi, ni+2);
439 %% THM3b: Marginal balance
for ni=0, per phase
440 fprintf(
' THM3b (Marginal balance ni=0) constraints...\n');
443 row = zeros(1, nVars);
444 % LHS: arrivals to queue i at ni=0
452 idx = getIdx(j, nj, kj, i, 0, ui);
453 row(idx) = row(idx) + q{j,i}(kj, hj, nj+1);
458 % RHS: departures from queue i at ni=1
466 idx = getIdx(i, 1, ki, j, nj, hj);
467 row(idx) = row(idx) - q{i,j}(ki, ui, 2);
477 %% QBAL: Queue balance constraint
478 % This
is a complex balance equation that tightens the bounds
479 fprintf(
' QBAL (Queue balance) constraints...\n');
482 row = zeros(1, nVars);
484 % 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]
496 coef = q{i,j}(ki, hi, ni+1) * ni;
497 idx = getIdx(i, ni, ki, j, nj, uj);
498 row(idx) = row(idx) + coef;
505 % LHS Term 2: sum{h<>k} sum{ni} q[i,i,k,h,ni]*ni*p2[i,ni,k,i,ni,k]
511 coef = q{i,i}(ki, hi, ni+1) * ni;
512 idx = getIdx(i, ni, ki, i, ni, ki);
513 row(idx) = row(idx) + coef;
517 % 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]
525 maxNj = min(F(j)-1, N-ni);
527 coef = q{i,j}(hi, ki, ni+1);
528 idx = getIdx(i, ni, hi, j, nj, uj);
529 row(idx) = row(idx) + coef;
536 % 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]
545 coef = q{j,i}(hj, uj, nj+1);
546 idx = getIdx(i, ni, ki, j, nj, hj);
547 row(idx) = row(idx) - coef;
554 % RHS Term 2: sum{h<>k} sum{ni} q[i,i,h,k,ni]*ni*p2[i,ni,h,i,ni,h]
560 coef = q{i,i}(hi, ki, ni+1) * ni;
561 idx = getIdx(i, ni, hi, i, ni, hi);
562 row(idx) = row(idx) - coef;
566 % 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]
578 coef = q{i,j}(hi, ki, ni+1) * ni;
579 idx = getIdx(i, ni, hi, j, nj, uj);
580 row(idx) = row(idx) - coef;
592 %% THM4: Queue-length bound inequality (Theorem 5)
593 % 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)
594 fprintf(
' THM4 (Queue-length bound) constraints...\n');
598 row = zeros(1, nVars);
599 % LHS: sum_t sum_ht sum_nj sum_nt nt * p2
603 for nt = 1:F(t) % nt >= 1
604 idx = getIdx(j, nj, kj, t, nt, ht);
605 row(idx) = row(idx) + nt;
613 for ni = 1:F(i) % ni >= 1
614 idx = getIdx(j, nj, kj, i, ni, hi);
615 row(idx) = row(idx) - N;
619 Aineq = [Aineq; -row]; % >= becomes <= with negation
625 %% Build objective function
626 fprintf(
'Building objective function...\n');
630 if strcmp(objective,
'U1min') || strcmp(objective,
'U1max')
633 error('Unknown objective: %s', objective);
636 targetQueue = objective;
639 % Utilization = sum over k, n of p2(i,n,k,i,n,k)
640 for ki = 1:K(targetQueue)
641 for ni = 1:F(targetQueue)
642 idx = getIdx(targetQueue, ni, ki, targetQueue, ni, ki);
647 if strcmp(sense, 'max')
652 fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
653 nVars, size(Aeq, 1), size(Aineq, 1));
655 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
657 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
659 if strcmp(sense, 'max')
665 result.objective = fval;
666 result.exitflag = exitflag;
669 % Compute utilizations from U and Ueff variables
670 result.U = zeros(M, 1);
671 result.Ueff = zeros(M, 1);
672 result.pb = zeros(M, 1);
675 % U(i) = sum over k, ni of U(i,k,ni)
678 idx_U = getUIdx(i, ki, ni);
679 idx_Ueff = getUeffIdx(i, ki, ni);
680 result.U(i) = result.U(i) + x(idx_U);
681 result.Ueff(i) = result.Ueff(i) + x(idx_Ueff);
684 result.pb(i) = result.U(i) - result.Ueff(i);
687 % Store p2 as a function handle for easy access
688 result.getP2 = @(j, nj, kj, i, ni, hi) x(getIdx(j, nj, kj, i, ni, hi));
691 fprintf('\n=== Results ===\n');
692 fprintf('Objective value: %f\n', fval);
693 fprintf('Exit flag: %d\n', exitflag);
695 fprintf('\nUtilizations:\n');
697 fprintf(' Queue %d: U = %.6f, Ueff = %.6f, pb = %.6f\n', ...
698 i, result.U(i), result.Ueff(i), result.pb(i));