LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
qsys_phmc.m
1function result = qsys_phmc(alpha, T, mu, c, varargin)
2% QSYS_PHMC Exact PH/M/c (Neuts' matrix-geometric).
3%
4% Solves the GI/M/c QBD via R^2*A2 + R*A1 + A0 = 0, where
5% A0 = (-T*1)*alpha, A1 = T - c*mu*I, A2 = c*mu*I.
6% pi_n = pi_c * R^(n-c) for n >= c. Boundary states pi_0..pi_c are obtained
7% from balance equations + normalization sum_{n<c} pi_n*1 + pi_c*(I-R)\1 = 1.
8%
9% Inputs:
10% ALPHA - PH entry probability row vector (1 x k)
11% T - PH sub-generator (k x k)
12% MU - Exponential service rate per server (positive scalar)
13% C - Number of servers (positive integer)
14%
15% Optional name-value:
16% 'maxIter' - max iterations for R fixed-point (default 50000)
17% 'tol' - convergence tolerance (default 1e-14)
18%
19% RESULT struct fields:
20% meanQueueLength, meanWaitingQueue, meanWaitingTime,
21% meanSojournTime, utilization, analyzer
22
23p = inputParser;
24addParameter(p, 'maxIter', 50000);
25addParameter(p, 'tol', 1e-14);
26parse(p, varargin{:});
27maxIter = p.Results.maxIter;
28tol = p.Results.tol;
29
30if mu <= 0
31 line_error(mfilename, 'Service rate MU must be positive');
32end
33if c < 1 || floor(c) ~= c
34 line_error(mfilename, 'C must be a positive integer');
35end
36alpha = alpha(:)';
37k = size(T, 1);
38if size(T, 2) ~= k || numel(alpha) ~= k
39 line_error(mfilename, 'ALPHA / T dimensions inconsistent');
40end
41
42ones_k = ones(k, 1);
43t_vec = -T * ones_k;
44D1 = t_vec * alpha;
45
46mean_ia = alpha * ((-T) \ ones_k);
47if mean_ia <= 0
48 line_error(mfilename, sprintf('Non-positive mean inter-arrival: %g', mean_ia));
49end
50lambda = 1.0 / mean_ia;
51rho = lambda / (c * mu);
52if rho >= 1 - 1e-12
53 line_error(mfilename, sprintf('Load rho=%g must be strictly less than 1', rho));
54end
55
56A0 = D1;
57A1 = T - c*mu*eye(k);
58A2 = c*mu*eye(k);
59R = zeros(k);
60for it = 1:maxIter
61 Mtmp = A1 + R*A2;
62 if abs(det(Mtmp)) < 1e-30
63 break;
64 end
65 R_new = -A0 / Mtmp;
66 if max(max(abs(R_new - R))) < tol
67 R = R_new;
68 break;
69 end
70 R = R_new;
71end
72
73% Boundary linear system
74n_var = (c+1)*k;
75M = zeros(n_var, n_var);
76block = @(n) (n*k);
77
78% Level 0: pi_0 T + pi_1 (mu I) = 0
79for j = 1:k
80 for i = 1:k
81 M(j, block(0)+i) = M(j, block(0)+i) + T(i, j);
82 end
83 M(j, block(1)+j) = M(j, block(1)+j) + mu;
84end
85
86% Levels 1..c-1: pi_{n-1} D1 + pi_n (T - n mu I) + pi_{n+1} ((n+1) mu I) = 0
87for n = 1:c-1
88 eqrow_base = n*k;
89 A1n = T - n*mu*eye(k);
90 for j = 1:k
91 row = eqrow_base + j;
92 for i = 1:k
93 M(row, block(n-1)+i) = M(row, block(n-1)+i) + D1(i, j);
94 M(row, block(n)+i) = M(row, block(n)+i) + A1n(i, j);
95 end
96 M(row, block(n+1)+j) = M(row, block(n+1)+j) + (n+1)*mu;
97 end
98end
99
100% Level c: pi_{c-1} D1 + pi_c (T - c mu I + R*(c mu I)) = 0
101A1c_plus_RA2 = T - c*mu*eye(k) + R*(c*mu*eye(k));
102eqrow_base = c*k;
103for j = 1:k
104 row = eqrow_base + j;
105 for i = 1:k
106 M(row, block(c-1)+i) = M(row, block(c-1)+i) + D1(i, j);
107 M(row, block(c)+i) = M(row, block(c)+i) + A1c_plus_RA2(i, j);
108 end
109end
110
111% Replace last row with normalization
112IR = eye(k) - R;
113sum_geom = IR \ ones_k;
114norm_row = zeros(1, n_var);
115for n = 0:c-1
116 for i = 1:k
117 norm_row(block(n)+i) = 1.0;
118 end
119end
120for i = 1:k
121 norm_row(block(c)+i) = sum_geom(i);
122end
123M(end, :) = norm_row;
124b = zeros(n_var, 1); b(end) = 1.0;
125
126x = M \ b;
127pis = cell(c+1, 1);
128for n = 0:c
129 pis{n+1} = x(block(n)+1 : block(n)+k);
130end
131pi_c = pis{c+1};
132
133IR_inv = inv(IR);
134IR_inv2 = IR_inv * IR_inv;
135Lq = pi_c' * R * IR_inv2 * ones_k;
136L_bulk = pi_c' * (c*IR_inv + R*IR_inv2) * ones_k;
137L = 0;
138for n = 0:c-1
139 L = L + n * sum(pis{n+1});
140end
141L = L + L_bulk;
142Wq = Lq / lambda;
143W = Wq + 1.0/mu;
144
145result = struct();
146result.meanQueueLength = L;
147result.meanWaitingQueue = Lq;
148result.meanWaitingTime = Wq;
149result.meanSojournTime = W;
150result.utilization = rho;
151result.analyzer = sprintf('PHMC:matrix-geom:c=%d', c);
152
153end