LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
mapqn_bnd_qr_delay.m
1function [result, x, fval, exitflag] = mapqn_bnd_qr_delay(params, objective_queue, objective_phase, objective_n, sense)
2% MAPQN_BND_QR_DELAY - Quadratic Reduction Bounds for Delay Systems
3%
4% MATLAB port of bnd_qr_delay.py (AMPL model bnd_quadraticreduction_delay.mod)
5%
6% Usage:
7% [result, x, fval, exitflag] = mapqn_bnd_qr_delay(params, objective_queue, objective_phase, objective_n)
8% [result, x, fval, exitflag] = mapqn_bnd_qr_delay(params, objective_queue, objective_phase, objective_n, sense)
9%
10% Inputs:
11% params - Structure with model parameters:
12% .M - Number of queues
13% .N - Total population
14% .K - [M x 1] Number of phases for each queue
15% .Z - Delay (think time) scalar
16% .D1 - Service demand at queue 1 scalar
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% .alpha - [M x N] load-dependent rates
20% .r - [M x M] Routing probabilities
21% .verbose - (optional) boolean
22%
23% objective_queue - Queue index for objective (1-based, 1..M)
24% objective_phase - Phase index for objective (1-based, 1..K(objective_queue))
25% objective_n - Population level for objective (0..N)
26% sense - (optional) 'max' (default) or 'min'
27%
28% Outputs:
29% result - Structure with results:
30% .objective - Objective function value
31% .exitflag - Solver exit flag
32% .p2marginals - Marginal probabilities extracted from p2 diagonal
33% x - Raw solution vector
34% fval - Objective function value
35% exitflag - Solver exit flag
36
37 if nargin < 5 || isempty(sense)
38 sense = 'max';
39 end
40
41 % Extract parameters
42 M = params.M;
43 N = params.N;
44 K = params.K(:);
45 Z = params.Z;
46 D1 = params.D1;
47 mu = params.mu;
48 v = params.v;
49 alpha = params.alpha;
50 r = params.r;
51 if isfield(params, 'verbose')
52 verbose = params.verbose;
53 else
54 verbose = true;
55 end
56
57 % Validate inputs
58 if objective_queue < 1 || objective_queue > M
59 error('objective_queue must be in range 1..%d', M);
60 end
61 if objective_phase < 1 || objective_phase > K(objective_queue)
62 error('objective_phase must be in range 1..%d', K(objective_queue));
63 end
64 if objective_n < 0 || objective_n > N
65 error('objective_n must be in range 0..%d', N);
66 end
67
68 % q function: load-dependent transition rate (1-based indexing)
69 % q_val(i, j, k, h, n): transition rate from queue i phase k to queue j phase h at population n
70 q_val = @(i, j, k, h, n) q_func(i, j, k, h, n, mu, v, r, alpha);
71
72 %% Build variable indexing
73 % p2(j, nj, k, i, ni, h) for j in 1:M, nj in 0:N, k in 1:K(j),
74 % i in 1:M, ni in 0:N, h in 1:K(i)
75 if verbose; fprintf('Building variable index map...\n'); end
76 varCount = 0;
77
78 % p2 variables
79 p2idx = cell(M, 1);
80 for j = 1:M
81 p2idx{j} = cell(N+1, K(j), M);
82 for nj = 0:N
83 for kj = 1:K(j)
84 for i = 1:M
85 p2idx{j}{nj+1, kj, i} = zeros(N+1, K(i));
86 for ni = 0:N
87 for hi = 1:K(i)
88 varCount = varCount + 1;
89 p2idx{j}{nj+1, kj, i}(ni+1, hi) = varCount;
90 end
91 end
92 end
93 end
94 end
95 end
96
97 nVars = varCount;
98 if verbose; fprintf('Total variables: %d\n', nVars); end
99
100 % Helper function
101 getP2Idx = @(j, nj, kj, i, ni, hi) p2idx{j}{nj+1, kj, i}(ni+1, hi);
102
103 %% Build constraints
104 Aeq = [];
105 beq = [];
106 Aineq = [];
107 bineq = [];
108
109 if verbose; fprintf('Building constraints...\n'); end
110
111 %% Initialize bounds
112 lb = zeros(nVars, 1);
113 ub = ones(nVars, 1);
114
115 %% ZERO constraints - fix infeasible states via ub=0
116 if verbose; fprintf(' ZERO constraints...\n'); end
117 for j = 1:M
118 for nj = 0:N
119 for kj = 1:K(j)
120 for i = 1:M
121 for ni = 0:N
122 for hi = 1:K(i)
123 idx = getP2Idx(j, nj, kj, i, ni, hi);
124
125 % ZERO1: i==j, nj==ni, h~=k
126 if i == j && nj == ni && hi ~= kj
127 ub(idx) = 0;
128 end
129
130 % ZERO2: i==j, nj~=ni
131 if i == j && nj ~= ni
132 ub(idx) = 0;
133 end
134
135 % ZERO3: i~=j, nj+ni > N
136 if i ~= j && nj + ni > N
137 ub(idx) = 0;
138 end
139 end
140 end
141 end
142 end
143 end
144 end
145
146 %% ONE: Normalization
147 % sum{nj=0..N, k=1..K(j)} p2(j,nj,k,j,nj,k) = 1 for each j
148 if verbose; fprintf(' ONE constraints...\n'); end
149 for j = 1:M
150 row = zeros(1, nVars);
151 for nj = 0:N
152 for kj = 1:K(j)
153 idx = getP2Idx(j, nj, kj, j, nj, kj);
154 row(idx) = 1;
155 end
156 end
157 Aeq = [Aeq; row];
158 beq = [beq; 1];
159 end
160
161 %% SYMMETRY: p2(i,ni,h,j,nj,k) = p2(j,nj,k,i,ni,h)
162 % Only add for one ordering to avoid redundancy
163 if verbose; fprintf(' SYMMETRY constraints...\n'); end
164 for j = 1:M
165 for nj = 0:N
166 for kj = 1:K(j)
167 for i = 1:M
168 if i <= j && ~(i == j)
169 % skip: only add when i > j to avoid redundancy
170 % actually we want i > j
171 end
172 if i <= j
173 continue;
174 end
175 for ni = 0:N
176 if i ~= j && nj + ni > N
177 continue;
178 end
179 for hi = 1:K(i)
180 idx1 = getP2Idx(j, nj, kj, i, ni, hi);
181 idx2 = getP2Idx(i, ni, hi, j, nj, kj);
182 if ub(idx1) == 0 && ub(idx2) == 0
183 continue;
184 end
185 if idx1 ~= idx2
186 row = zeros(1, nVars);
187 row(idx1) = 1;
188 row(idx2) = -1;
189 Aeq = [Aeq; row];
190 beq = [beq; 0];
191 end
192 end
193 end
194 end
195 end
196 end
197 end
198
199 %% MARGINALS: p2(j,nj,k,j,nj,k) = sum{ni=0..N-nj, h=1..K(i)} p2(j,nj,k,i,ni,h) for each (j,k,nj,i~=j)
200 if verbose; fprintf(' MARGINALS constraints...\n'); end
201 for j = 1:M
202 for kj = 1:K(j)
203 for nj = 0:N
204 for i = 1:M
205 if i == j
206 continue;
207 end
208 row = zeros(1, nVars);
209 idx_diag = getP2Idx(j, nj, kj, j, nj, kj);
210 row(idx_diag) = 1;
211 for ni = 0:(N-nj)
212 for hi = 1:K(i)
213 idx = getP2Idx(j, nj, kj, i, ni, hi);
214 row(idx) = row(idx) - 1;
215 end
216 end
217 Aeq = [Aeq; row];
218 beq = [beq; 0];
219 end
220 end
221 end
222 end
223
224 %% PC2: sum{i,j,ni>=1,nj>=1,h,k} nj*ni*p2(j,nj,k,i,ni,h) = N^2
225 if verbose; fprintf(' PC2 (Second moment) constraint...\n'); end
226 row = zeros(1, nVars);
227 for i = 1:M
228 for j = 1:M
229 for ni = 1:N
230 for nj = 1:N
231 for hi = 1:K(i)
232 for kj = 1:K(j)
233 idx = getP2Idx(j, nj, kj, i, ni, hi);
234 row(idx) = row(idx) + nj * ni;
235 end
236 end
237 end
238 end
239 end
240 end
241 Aeq = [Aeq; row];
242 beq = [beq; N^2];
243
244 %% THM1: Queue length theorem
245 % for each (j,k): sum{i,nj>=1,ni>=1,h} ni*p2(j,nj,k,i,ni,h) - N*sum{nj>=1} p2(j,nj,k,j,nj,k) = 0
246 if verbose; fprintf(' THM1 (Queue length) constraints...\n'); end
247 for j = 1:M
248 for kj = 1:K(j)
249 row = zeros(1, nVars);
250 % Left side: sum ni*p2(j,nj,k,i,ni,h) for nj>=1, ni>=1
251 for i = 1:M
252 for nj = 1:N
253 for ni = 1:N
254 for hi = 1:K(i)
255 idx = getP2Idx(j, nj, kj, i, ni, hi);
256 row(idx) = row(idx) + ni;
257 end
258 end
259 end
260 end
261 % Right side: -N * sum p2(j,nj,k,j,nj,k) for nj>=1
262 for nj = 1:N
263 idx = getP2Idx(j, nj, kj, j, nj, kj);
264 row(idx) = row(idx) - N;
265 end
266 Aeq = [Aeq; row];
267 beq = [beq; 0];
268 end
269 end
270
271 %% THM1c: Empty queue theorem
272 % for each (j,k): sum{i,ni>=1,h} ni*p2(j,0,k,i,ni,h) - N*p2(j,0,k,j,0,k) = 0
273 if verbose; fprintf(' THM1c (Empty queue) constraints...\n'); end
274 for j = 1:M
275 for kj = 1:K(j)
276 row = zeros(1, nVars);
277 % Left side
278 for i = 1:M
279 for ni = 1:N
280 for hi = 1:K(i)
281 idx = getP2Idx(j, 0, kj, i, ni, hi);
282 row(idx) = row(idx) + ni;
283 end
284 end
285 end
286 % Right side
287 idx = getP2Idx(j, 0, kj, j, 0, kj);
288 row(idx) = row(idx) - N;
289 Aeq = [Aeq; row];
290 beq = [beq; 0];
291 end
292 end
293
294 %% XZ: Delay constraint
295 % sum{ni>=1,k} ni*p2(M,ni,k,M,ni,k) - (Z/D1)*sum{k,nj>=1} p2(1,nj,k,1,nj,k) = 0
296 if verbose; fprintf(' XZ (Delay) constraint...\n'); end
297 row = zeros(1, nVars);
298 % Left side: queue length at delay (queue M)
299 for ni = 1:N
300 for kM = 1:K(M)
301 idx = getP2Idx(M, ni, kM, M, ni, kM);
302 row(idx) = row(idx) + ni;
303 end
304 end
305 % Right side: -(Z/D1) * throughput at queue 1
306 for kj = 1:K(1)
307 for nj = 1:N
308 idx = getP2Idx(1, nj, kj, 1, nj, kj);
309 row(idx) = row(idx) - Z / D1;
310 end
311 end
312 Aeq = [Aeq; row];
313 beq = [beq; 0];
314
315 %% THM2: Phase balance
316 % for each (i,k): sum{j,h: j~=i or h~=k, ni>=1} (q(i,j,k,h,ni)*p2(i,ni,k,i,ni,k) - q(i,j,h,k,ni)*p2(i,ni,h,i,ni,h)) = 0
317 if verbose; fprintf(' THM2 (Phase balance) constraints...\n'); end
318 for i = 1:M
319 for ki = 1:K(i)
320 row = zeros(1, nVars);
321 for j = 1:M
322 for hi = 1:K(i)
323 if ~(hi == ki && j == i)
324 for ni = 1:N
325 q_out = q_val(i, j, ki, hi, ni);
326 q_in = q_val(i, j, hi, ki, ni);
327 idx_k = getP2Idx(i, ni, ki, i, ni, ki);
328 idx_h = getP2Idx(i, ni, hi, i, ni, hi);
329 row(idx_k) = row(idx_k) + q_out;
330 row(idx_h) = row(idx_h) - q_in;
331 end
332 end
333 end
334 end
335 Aeq = [Aeq; row];
336 beq = [beq; 0];
337 end
338 end
339
340 %% THM3a: Flow balance for ni=1..N-1
341 % for each (i,ni): sum{j~=i,k,h,u,nj=1..N-ni} q(j,i,k,h,nj)*p2(j,nj,k,i,ni,u)
342 % - sum{j~=i,k,h} q(i,j,k,h,ni+1)*p2(i,ni+1,k,i,ni+1,k) = 0
343 if verbose; fprintf(' THM3a (Flow balance) constraints...\n'); end
344 for i = 1:M
345 for ni = 1:(N-1)
346 row = zeros(1, nVars);
347 % Incoming flow
348 for j = 1:M
349 if j ~= i
350 for kj = 1:K(j)
351 for hj = 1:K(j)
352 for ui = 1:K(i)
353 for nj = 1:(N-ni)
354 qv = q_val(j, i, kj, hj, nj);
355 idx = getP2Idx(j, nj, kj, i, ni, ui);
356 row(idx) = row(idx) + qv;
357 end
358 end
359 end
360 end
361 end
362 end
363 % Outgoing flow
364 for j = 1:M
365 if j ~= i
366 for ki = 1:K(i)
367 for hi = 1:K(i)
368 qv = q_val(i, j, ki, hi, ni+1);
369 idx = getP2Idx(i, ni+1, ki, i, ni+1, ki);
370 row(idx) = row(idx) - qv;
371 end
372 end
373 end
374 end
375 Aeq = [Aeq; row];
376 beq = [beq; 0];
377 end
378 end
379
380 %% THM3b: Flow balance for ni=0
381 % for each (i,u): sum{j~=i,k,h,nj=1..N} q(j,i,k,h,nj)*p2(j,nj,k,i,0,u)
382 % - sum{j~=i,k} q(i,j,k,u,1)*p2(i,1,k,i,1,k) = 0
383 if verbose; fprintf(' THM3b (Flow balance ni=0) constraints...\n'); end
384 for i = 1:M
385 for ui = 1:K(i)
386 row = zeros(1, nVars);
387 % Incoming to empty queue
388 for j = 1:M
389 if j ~= i
390 for kj = 1:K(j)
391 for hj = 1:K(j)
392 for nj = 1:N
393 qv = q_val(j, i, kj, hj, nj);
394 idx = getP2Idx(j, nj, kj, i, 0, ui);
395 row(idx) = row(idx) + qv;
396 end
397 end
398 end
399 end
400 end
401 % Outgoing from single customer
402 for j = 1:M
403 if j ~= i
404 for ki = 1:K(i)
405 qv = q_val(i, j, ki, ui, 1);
406 idx = getP2Idx(i, 1, ki, i, 1, ki);
407 row(idx) = row(idx) - qv;
408 end
409 end
410 end
411 Aeq = [Aeq; row];
412 beq = [beq; 0];
413 end
414 end
415
416 %% QBAL: Queue balance (from AMPL bnd_quadraticreduction_delay.mod)
417 % for each (i, k):
418 % LHS1: sum{h~=k, j, ni>=1} q(i,j,k,h,ni)*ni*p2(i,ni,k,i,ni,k)
419 % + LHS2: sum{j~=i, h, ni>=1} q(i,j,h,k,ni)*p2(i,ni,h,i,ni,h)
420 % = RHS1: sum{j~=i, u in K(j), w in K(j), nj>=1} q(j,i,u,w,nj)*(p2(j,nj,u,i,0,k) + sum{ni>=1} p2(i,ni,k,j,nj,u))
421 % + RHS2: sum{h~=k, j, ni>=1} q(i,j,h,k,ni)*ni*p2(i,ni,h,i,ni,h)
422 if verbose; fprintf(' QBAL (Queue balance) constraints...\n'); end
423 for i = 1:M
424 for ki = 1:K(i)
425 row = zeros(1, nVars);
426 % LHS1: sum{h~=k, j, ni>=1} q(i,j,k,h,ni)*ni*p2(i,ni,k,i,ni,k)
427 for hi = 1:K(i)
428 if hi ~= ki
429 for j = 1:M
430 for ni = 1:N
431 qv = q_val(i, j, ki, hi, ni);
432 idx = getP2Idx(i, ni, ki, i, ni, ki);
433 row(idx) = row(idx) + qv * ni;
434 end
435 end
436 end
437 end
438 % LHS2: sum{j~=i, h, ni>=1} q(i,j,h,k,ni)*p2(i,ni,h,i,ni,h)
439 for j = 1:M
440 if j ~= i
441 for hi = 1:K(i)
442 for ni = 1:N
443 qv = q_val(i, j, hi, ki, ni);
444 idx = getP2Idx(i, ni, hi, i, ni, hi);
445 row(idx) = row(idx) + qv;
446 end
447 end
448 end
449 end
450 % -RHS1 part a: sum{j~=i, u in K(j), w in K(j), nj>=1} q(j,i,u,w,nj)*p2(j,nj,u,i,0,k)
451 for j = 1:M
452 if j ~= i
453 for u = 1:K(j)
454 for w = 1:K(j)
455 for nj = 1:N
456 qv = q_val(j, i, u, w, nj);
457 idx = getP2Idx(j, nj, u, i, 0, ki);
458 row(idx) = row(idx) - qv;
459 end
460 end
461 end
462 end
463 end
464 % -RHS1 part b: sum{j~=i, u in K(j), w in K(j), nj>=1, ni>=1} q(j,i,u,w,nj)*p2(i,ni,k,j,nj,u)
465 for j = 1:M
466 if j ~= i
467 for u = 1:K(j)
468 for w = 1:K(j)
469 for nj = 1:N
470 qv = q_val(j, i, u, w, nj);
471 for ni = 1:N
472 idx = getP2Idx(i, ni, ki, j, nj, u);
473 row(idx) = row(idx) - qv;
474 end
475 end
476 end
477 end
478 end
479 end
480 % -RHS2: sum{h~=k, j, ni>=1} q(i,j,h,k,ni)*ni*p2(i,ni,h,i,ni,h)
481 for hi = 1:K(i)
482 if hi ~= ki
483 for j = 1:M
484 for ni = 1:N
485 qv = q_val(i, j, hi, ki, ni);
486 idx = getP2Idx(i, ni, hi, i, ni, hi);
487 row(idx) = row(idx) - qv * ni;
488 end
489 end
490 end
491 end
492 Aeq = [Aeq; row];
493 beq = [beq; 0];
494 end
495 end
496
497 %% COR1a: Correlation constraint (from AMPL bnd_quadraticreduction_delay.mod)
498 % for each (i, kstar, ni_c in 0..N-2)
499 if verbose; fprintf(' COR1a constraints...\n'); end
500 for i = 1:M
501 for kstar = 1:K(i)
502 for ni_c = 0:(N-2)
503 row = zeros(1, nVars);
504 % A: sum{j~=i,kj,hj,u~=kstar,nj=1..N-ni_c} q(j,i,kj,hj,nj)*p2(j,nj,kj,i,ni_c,u)
505 for j = 1:M
506 if j ~= i
507 for kj = 1:K(j)
508 for hj = 1:K(j)
509 for u = 1:K(i)
510 if u ~= kstar
511 for nj = 1:(N-ni_c)
512 qv = q_val(j, i, kj, hj, nj);
513 idx = getP2Idx(j, nj, kj, i, ni_c, u);
514 row(idx) = row(idx) + qv;
515 end
516 end
517 end
518 end
519 end
520 end
521 end
522 % B: sum{j~=i,kj,hj,nj=1..N-ni_c} q(j,i,kj,hj,nj)*p2(j,nj,kj,i,ni_c+1,kstar)
523 for j = 1:M
524 if j ~= i
525 for kj = 1:K(j)
526 for hj = 1:K(j)
527 for nj = 1:(N-ni_c)
528 qv = q_val(j, i, kj, hj, nj);
529 idx = getP2Idx(j, nj, kj, i, ni_c+1, kstar);
530 row(idx) = row(idx) + qv;
531 end
532 end
533 end
534 end
535 end
536 % C: sum{k~=kstar} q(i,i,kstar,k,ni_c+1)*p2(i,ni_c+1,kstar,i,ni_c+1,kstar)
537 for k2 = 1:K(i)
538 if k2 ~= kstar
539 qv = q_val(i, i, kstar, k2, ni_c+1);
540 idx = getP2Idx(i, ni_c+1, kstar, i, ni_c+1, kstar);
541 row(idx) = row(idx) + qv;
542 end
543 end
544 % -D: -sum{j~=i,k~=kstar} q(i,j,k,k,ni_c+1)*p2(i,ni_c+1,k,i,ni_c+1,k)
545 for j = 1:M
546 if j ~= i
547 for k2 = 1:K(i)
548 if k2 ~= kstar
549 qv = q_val(i, j, k2, k2, ni_c+1);
550 idx = getP2Idx(i, ni_c+1, k2, i, ni_c+1, k2);
551 row(idx) = row(idx) - qv;
552 end
553 end
554 end
555 end
556 % -E: -sum{j~=i,k~=kstar,h~=k} q(i,j,k,h,ni_c+1)*p2(i,ni_c+1,k,i,ni_c+1,k)
557 for j = 1:M
558 if j ~= i
559 for k2 = 1:K(i)
560 if k2 ~= kstar
561 for h2 = 1:K(i)
562 if h2 ~= k2
563 qv = q_val(i, j, k2, h2, ni_c+1);
564 idx = getP2Idx(i, ni_c+1, k2, i, ni_c+1, k2);
565 row(idx) = row(idx) - qv;
566 end
567 end
568 end
569 end
570 end
571 end
572 % -F: -sum{j~=i,k~=kstar} q(i,j,k,kstar,ni_c+2)*p2(i,ni_c+2,k,i,ni_c+2,k)
573 for j = 1:M
574 if j ~= i
575 for k2 = 1:K(i)
576 if k2 ~= kstar
577 qv = q_val(i, j, k2, kstar, ni_c+2);
578 idx = getP2Idx(i, ni_c+2, k2, i, ni_c+2, k2);
579 row(idx) = row(idx) - qv;
580 end
581 end
582 end
583 end
584 % -G: -sum{j~=i} q(i,j,kstar,kstar,ni_c+2)*p2(i,ni_c+2,kstar,i,ni_c+2,kstar)
585 for j = 1:M
586 if j ~= i
587 qv = q_val(i, j, kstar, kstar, ni_c+2);
588 idx = getP2Idx(i, ni_c+2, kstar, i, ni_c+2, kstar);
589 row(idx) = row(idx) - qv;
590 end
591 end
592 % -H: -sum{k~=kstar} q(i,i,k,kstar,ni_c+1)*p2(i,ni_c+1,k,i,ni_c+1,k)
593 for k2 = 1:K(i)
594 if k2 ~= kstar
595 qv = q_val(i, i, k2, kstar, ni_c+1);
596 idx = getP2Idx(i, ni_c+1, k2, i, ni_c+1, k2);
597 row(idx) = row(idx) - qv;
598 end
599 end
600 Aeq = [Aeq; row];
601 beq = [beq; 0];
602 end
603 end
604 end
605
606 %% COR1b: Correlation constraint boundary (from AMPL bnd_quadraticreduction_delay.mod)
607 % for each (i, kstar)
608 if verbose; fprintf(' COR1b constraints...\n'); end
609 for i = 1:M
610 for kstar = 1:K(i)
611 row = zeros(1, nVars);
612 % A': sum{j~=i,kj,hj,u~=kstar} q(j,i,kj,hj,1)*p2(j,1,kj,i,N-1,u)
613 for j = 1:M
614 if j ~= i
615 for kj = 1:K(j)
616 for hj = 1:K(j)
617 for u = 1:K(i)
618 if u ~= kstar
619 qv = q_val(j, i, kj, hj, 1);
620 idx = getP2Idx(j, 1, kj, i, N-1, u);
621 row(idx) = row(idx) + qv;
622 end
623 end
624 end
625 end
626 end
627 end
628 % C': sum{k~=kstar} q(i,i,kstar,k,N)*p2(i,N,kstar,i,N,kstar)
629 for k2 = 1:K(i)
630 if k2 ~= kstar
631 qv = q_val(i, i, kstar, k2, N);
632 idx = getP2Idx(i, N, kstar, i, N, kstar);
633 row(idx) = row(idx) + qv;
634 end
635 end
636 % -D': -sum{j~=i,k~=kstar} q(i,j,k,k,N)*p2(i,N,k,i,N,k)
637 for j = 1:M
638 if j ~= i
639 for k2 = 1:K(i)
640 if k2 ~= kstar
641 qv = q_val(i, j, k2, k2, N);
642 idx = getP2Idx(i, N, k2, i, N, k2);
643 row(idx) = row(idx) - qv;
644 end
645 end
646 end
647 end
648 % -E': -sum{j~=i,k~=kstar,h~=k} q(i,j,k,h,N)*p2(i,N,k,i,N,k)
649 for j = 1:M
650 if j ~= i
651 for k2 = 1:K(i)
652 if k2 ~= kstar
653 for h2 = 1:K(i)
654 if h2 ~= k2
655 qv = q_val(i, j, k2, h2, N);
656 idx = getP2Idx(i, N, k2, i, N, k2);
657 row(idx) = row(idx) - qv;
658 end
659 end
660 end
661 end
662 end
663 end
664 % -H': -sum{k~=kstar} q(i,i,k,kstar,N)*p2(i,N,k,i,N,k)
665 for k2 = 1:K(i)
666 if k2 ~= kstar
667 qv = q_val(i, i, k2, kstar, N);
668 idx = getP2Idx(i, N, k2, i, N, k2);
669 row(idx) = row(idx) - qv;
670 end
671 end
672 Aeq = [Aeq; row];
673 beq = [beq; 0];
674 end
675 end
676
677 %% THM4: QMIN inequality constraint
678 % for each (j,k,i): sum{t,h,nj,nt} nt*p2(j,nj,k,t,nt,h) - N*sum{h,nj,ni} p2(j,nj,k,i,ni,h) >= 0
679 if verbose; fprintf(' THM4 (QMIN) constraints...\n'); end
680 for j = 1:M
681 for kj = 1:K(j)
682 for i = 1:M
683 row = zeros(1, nVars);
684 % Left side: total queue length
685 for t = 1:M
686 for ht = 1:K(t)
687 for nj = 0:N
688 for nt = 0:N
689 idx = getP2Idx(j, nj, kj, t, nt, ht);
690 row(idx) = row(idx) + nt;
691 end
692 end
693 end
694 end
695 % Right side: -N * probability mass at queue i
696 for hi = 1:K(i)
697 for nj = 0:N
698 for ni = 0:N
699 idx = getP2Idx(j, nj, kj, i, ni, hi);
700 row(idx) = row(idx) - N;
701 end
702 end
703 end
704 % row >= 0 => -row <= 0
705 Aineq = [Aineq; -row];
706 bineq = [bineq; 0];
707 end
708 end
709 end
710
711 %% Build objective function
712 % maximize p2(objective_queue, objective_n, objective_phase, objective_queue, objective_n, objective_phase)
713 if verbose; fprintf('Building objective function...\n'); end
714 c = zeros(nVars, 1);
715 idx_obj = getP2Idx(objective_queue, objective_n, objective_phase, objective_queue, objective_n, objective_phase);
716 c(idx_obj) = 1;
717
718 if strcmp(sense, 'max')
719 c = -c; % linprog minimizes, so negate for maximization
720 end
721
722 %% Solve LP
723 if verbose; fprintf('Solving LP with %d variables and %d equality + %d inequality constraints...\n', ...
724 nVars, size(Aeq, 1), size(Aineq, 1)); end
725
726 if verbose
727 options = optimoptions('linprog', 'Display', 'final', 'Algorithm', 'interior-point');
728 else
729 options = optimoptions('linprog', 'Display', 'off', 'Algorithm', 'interior-point');
730 end
731
732 [x, fval, exitflag] = linprog(c, Aineq, bineq, Aeq, beq, lb, ub, options);
733
734 if strcmp(sense, 'max')
735 fval = -fval;
736 end
737
738 %% Extract results
739 result = struct();
740 result.objective = fval;
741 result.exitflag = exitflag;
742
743 if exitflag > 0
744 % Extract marginal probabilities from p2 diagonal
745 % p2marginals{j}(nj, kj) = p2(j, nj, kj, j, nj, kj)
746 result.p2marginals = cell(M, 1);
747 for j = 1:M
748 result.p2marginals{j} = zeros(N+1, K(j));
749 for nj = 0:N
750 for kj = 1:K(j)
751 idx = getP2Idx(j, nj, kj, j, nj, kj);
752 result.p2marginals{j}(nj+1, kj) = x(idx);
753 end
754 end
755 end
756 end
757
758 if verbose; fprintf('\n=== Results ===\n'); end
759 if verbose; fprintf('Objective value: %f\n', fval); end
760 if verbose; fprintf('Exit flag: %d\n', exitflag); end
761 if exitflag > 0 && verbose
762 fprintf('\nMarginal probabilities (diagonal):\n');
763 for j = 1:M
764 fprintf(' Queue %d:\n', j);
765 for nj = 0:N
766 for kj = 1:K(j)
767 val = result.p2marginals{j}(nj+1, kj);
768 if val > 1e-8
769 fprintf(' p2(%d,%d,%d) = %.6f\n', j, nj, kj, val);
770 end
771 end
772 end
773 end
774 end
775end
776
777function qv = q_func(i, j, k, h, n, mu, v, r, alpha)
778% Q_FUNC - Compute load-dependent transition rate (1-based indexing)
779%
780% i: source queue (1-based)
781% j: destination queue (1-based)
782% k: source phase (1-based)
783% h: destination phase (1-based)
784% n: population level (0 returns 0)
785
786 if n == 0
787 qv = 0;
788 return;
789 end
790
791 % Load-dependent scaling
792 if n <= size(alpha, 2)
793 alpha_val = alpha(i, n);
794 else
795 alpha_val = 1;
796 end
797
798 if j ~= i
799 qv = r(i, j) * mu{i}(k, h) * alpha_val;
800 else
801 qv = (v{i}(k, h) + r(i, i) * mu{i}(k, h)) * alpha_val;
802 end
803end