LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
qbd_rap.m
1%{ @file qbd_rap.m
2 % @brief Solves a general QBD process with Rational Arrival Process components
3 %
4 % @author LINE Development Team
5%}
6
7%{
8 % @brief Equilibrium analysis of a Quasi-Birth-and-Death process with
9 % Rational Arrival Process (RAP) components
10 %
11 % @details
12 % The process is specified directly by its level-independent blocks
13 % (A0,A1,A2) and its boundary blocks (B0,B1), where A0 drives level
14 % increases, A2 drives level decreases and A1 the within-level evolution.
15 % Unlike a Markovian QBD the blocks need not be nonnegative: they are only
16 % required to be conservative, (A0+A1+A2)*e = 0, and to define a genuine
17 % RAP through the prediction-process interpretation. This makes qbd_rap
18 % strictly more general than qbd_raprap1, which builds a product-space QBD
19 % from two INDEPENDENT RAPs; here the arrival process and the sequence of
20 % service times may be driven from a shared phase space and therefore be
21 % cross-correlated.
22 %
23 % @par References:
24 % N. G. Bean and B. F. Nielsen, "Quasi-Birth-and-Death Processes with
25 % Rational Arrival Process Components", Stochastic Models, 26(3), 2010,
26 % pp. 309-334 (DTU technical report IMM-2007-20). The equilibrium
27 % construction below is their Theorem 7 and the stability test is their
28 % Corollary 8. The argument rests on the prediction-process interpretation
29 % of a RAP due to Asmussen and Bladt, which is what allows a QBD argument
30 % to be carried over to matrices that are not nonnegative; the same
31 % prediction process underlies the conditional-vector RAP sampler in
32 % rap_sample.m.
33 %
34 % @par Algorithm (Theorem 7):
35 % 1. Solve A0*G^2 + A1*G + A2 = 0 for G.
36 % 2. U = A1 + A0*G.
37 % 3. R = A0*inv(-U).
38 % 4. Find the row vector pihat0 with pihat0*(B1 + R*A2) = 0, pihat0*e = 1.
39 % 5. pi0 = K*pihat0 with K chosen so that pi0*inv(I-R)*e = 1.
40 % 6. pi_n = pi0*R^n, and the marginal level probability is pi_n*e.
41 % The process is positive recurrent iff Sp(R) < 1 and step 4 has a solution.
42 %
43 % @par Computation of G:
44 % The blocks are not nonnegative, so the probabilistic iterations used for
45 % Markovian QBDs (logarithmic reduction, cyclic reduction) carry no
46 % convergence guarantee here, and the paper explicitly leaves the general
47 % case open ("The issue of justifying algorithms for the evaluation of the
48 % matrix G for such processes has not been undertaken", Section 6). Two
49 % paths are therefore taken:
50 % - If A2 has rank one, A2 = u*v, then G = e*v/(v*e) solves the equation
51 % exactly. This is immediate from conservativity: G is idempotent and
52 % (A0+A1)*e = -A2*e = -u*(v*e), so (A0+A1)*e*v/(v*e) = -u*v = -A2. This
53 % is the case covered by the paper's example, and the residual is checked.
54 % - Otherwise the quadratic matrix equation is solved numerically by
55 % natural functional iteration G <- inv(-A1)*(A2 + A0*G^2) used as a warm
56 % start, followed by Newton's method on the Sylvester-form Jacobian,
57 % (A0*G+A1)*H + A0*H*G = -(A0*G^2 + A1*G + A2), solved through its
58 % Kronecker expansion. If the residual does not reach roundoff level, or
59 % the iterate does not satisfy the stochastic-analogue constraint G*e = e,
60 % an error is raised rather than returning an unconverged G.
61 %
62 % @par Syntax:
63 % @code
64 % [levelProb, QN, R, G, U, spr, pqueue, pi0] = qbd_rap(A0, A1, A2)
65 % [levelProb, QN, R, G, U, spr, pqueue, pi0] = qbd_rap(A0, A1, A2, B0, B1)
66 % [levelProb, QN, R, G, U, spr, pqueue, pi0] = qbd_rap(A0, A1, A2, B0, B1, numLevels)
67 % @endcode
68 %
69 % This is the block-level core of the RAP QBD family. qbd_raprap1 is the
70 % thin wrapper over it that builds the product-space blocks of two
71 % INDEPENDENT RAPs; callers with a coupled model, in which arrivals and
72 % services share a phase space, must call qbd_rap directly because no
73 % product form exists to factor out.
74 %
75 % @par Parameters:
76 % <table>
77 % <tr><th>Name<th>Description
78 % <tr><td>A0<td>Level-up block (m x m)
79 % <tr><td>A1<td>Local block (m x m)
80 % <tr><td>A2<td>Level-down block (m x m)
81 % <tr><td>B0<td>(Optional) boundary level-up block, default A0
82 % <tr><td>B1<td>(Optional) boundary local block, default A1
83 % <tr><td>numLevels<td>(Optional) highest level reported, default 20
84 % </table>
85 %
86 % @par Returns:
87 % <table>
88 % <tr><th>Name<th>Description
89 % <tr><td>levelProb<td>Row vector of marginal level probabilities, levels 0..numLevels
90 % <tr><td>QN<td>Mean queue length (exact, pi0*R*inv(I-R)^2*e)
91 % <tr><td>R<td>Rate matrix R
92 % <tr><td>G<td>Matrix G solving A0*G^2 + A1*G + A2 = 0
93 % <tr><td>U<td>Matrix U = A1 + A0*G
94 % <tr><td>spr<td>Spectral radius Sp(R)
95 % <tr><td>pqueue<td>(numLevels+1) x m matrix of the vectors pi_n
96 % <tr><td>pi0<td>Level-0 vector pi_0, the boundary vector of Theorem 7
97 % </table>
98 %
99 % @par Example:
100 % @code
101 % g = 0.25;
102 % A1 = [-1 0 0; -2/3 -1 1; 2/3 -1 -1];
103 % Da = [14/5 -9/10 -9/10; 26/15 -8/15 -8/15; 58/15 -19/15 -19/15];
104 % Ds = [1; 2/3; 4/3]*[3 -1 -1];
105 % [levelProb, QN] = qbd_rap(g*Da, A1, (1-g)*Ds, g*Da, g*A1, 8);
106 % % levelProb reproduces Table 1 of Bean and Nielsen (2010)
107 % @endcode
108%}
109function [levelProb, QN, R, G, U, spr, pqueue, pi0] = qbd_rap(A0, A1, A2, B0, B1, numLevels)
110% [LEVELPROB,QN,R,G,U,SPR,PQUEUE,PI0]=QBD_RAP(A0,A1,A2,B0,B1,NUMLEVELS)
111
112if nargin < 3
113 line_error(mfilename, 'qbd_rap requires at least the blocks A0, A1 and A2.');
114end
115if nargin < 4 || isempty(B0)
116 B0 = A0;
117end
118if nargin < 5 || isempty(B1)
119 B1 = A1;
120end
121if nargin < 6 || isempty(numLevels)
122 numLevels = 20;
123end
124
125m = size(A1,1);
126if size(A1,2) ~= m || any(size(A0) ~= [m m]) || any(size(A2) ~= [m m]) ...
127 || any(size(B0) ~= [m m]) || any(size(B1) ~= [m m])
128 line_error(mfilename, 'All QBD blocks must be square and of the same order.');
129end
130if numLevels < 0 || numLevels ~= round(numLevels)
131 line_error(mfilename, 'numLevels must be a nonnegative integer.');
132end
133
134e = ones(m,1);
135I = eye(m);
136blockScale = max([norm(A0,'fro'), norm(A1,'fro'), norm(A2,'fro'), 1]);
137
138% Conservativity of the repeating portion, (A0+A1+A2)*e = 0. This is the
139% RAP analogue of the generator row-sum condition and every step below
140% relies on it.
141if norm((A0+A1+A2)*e, inf) > 1e-8*blockScale
142 line_error(mfilename, sprintf(['The repeating blocks are not conservative: ' ...
143 '||(A0+A1+A2)*e||_inf = %g. A QBD with RAP components requires ' ...
144 '(A0+A1+A2)*e = 0.'], norm((A0+A1+A2)*e, inf)));
145end
146if norm((B0+B1)*e, inf) > 1e-8*blockScale
147 line_error(mfilename, sprintf(['The boundary blocks are not conservative: ' ...
148 '||(B0+B1)*e||_inf = %g. A QBD with RAP components requires ' ...
149 '(B0+B1)*e = 0 at level 0.'], norm((B0+B1)*e, inf)));
150end
151
152% Step 1: matrix G.
153G = qbd_rap_g(A0, A1, A2, blockScale);
154
155% Steps 2 and 3: U and R.
156U = A1 + A0*G;
157if rcond(-U) < eps
158 line_error(mfilename, 'The matrix U = A1 + A0*G is singular, R = A0*inv(-U) does not exist.');
159end
160R = A0/(-U);
161
162% Corollary 8(i): positive recurrence.
163spr = max(abs(eig(R)));
164% The threshold carries a 1e-12 margin: at the null-recurrent boundary
165% Sp(R) equals 1 in exact arithmetic but rounds to either side, and the
166% three codebases must agree on rejecting it. Any model within 1e-12 of the
167% boundary has an unbounded queue regardless.
168if spr >= 1 - 1e-12
169 line_error(mfilename, sprintf(['The process is not positive recurrent: ' ...
170 'Sp(R) = %.15g >= 1 (Corollary 8 of Bean and Nielsen, 2010).'], spr));
171end
172
173% Step 4: boundary vector, pihat0*(B1 + R*A2) = 0 normalised to pihat0*e = 1.
174% The left null space is extracted from the SVD of V.' with a relative
175% tolerance: V is only singular up to the accuracy with which R was
176% computed, so the absolute default tolerance of null() is too strict here.
177V = B1 + R*A2;
178[~, Sv, W] = svd(V.');
179sv = diag(Sv);
180nullTol = 1e-8*max(sv(1),1);
181if sv(end) > nullTol
182 line_error(mfilename, sprintf(['The boundary equation x*(B1 + R*A2) = 0 has no ' ...
183 'nontrivial solution (smallest singular value %g against tolerance %g), so ' ...
184 'the process is not positive recurrent (Corollary 8(ii) of Bean and ' ...
185 'Nielsen, 2010).'], sv(end), nullTol));
186end
187if m > 1 && sv(end-1) <= nullTol
188 line_error(mfilename, ['The boundary equation x*(B1 + R*A2) = 0 has a solution ' ...
189 'space of dimension greater than one, the equilibrium vector is not unique.']);
190end
191pihat0 = W(:,end).';
192if abs(pihat0*e) < 1e-12*norm(pihat0,inf)
193 line_error(mfilename, 'The boundary vector cannot be normalised, x*e = 0.');
194end
195pihat0 = pihat0/(pihat0*e);
196
197% Step 5: level-0 vector.
198K = 1/(pihat0*((I-R)\e));
199pi0 = K*pihat0;
200
201% Consistency of the supplied boundary up-block: the level-0 balance
202% equation pi0*B0 + pi1*A1 + pi2*A2 = 0 must hold with pi_n = pi0*R^n.
203bal = pi0*B0 + pi0*R*A1 + pi0*R*R*A2;
204if norm(bal, inf) > 1e-8*blockScale*max(norm(pi0,inf),1)
205 line_error(mfilename, sprintf(['The boundary block B0 is inconsistent with the ' ...
206 'repeating blocks: ||pi0*B0 + pi1*A1 + pi2*A2||_inf = %g. The level-0 ' ...
207 'balance equation of Theorem 7 requires pi0*(B0-A0) = 0.'], norm(bal,inf)));
208end
209
210% Step 6: level vectors and marginal level distribution.
211pqueue = zeros(numLevels+1, m);
212pin = pi0;
213for n = 0:numLevels
214 pqueue(n+1,:) = pin;
215 pin = pin*R;
216end
217levelProb = (pqueue*e).';
218
219% Exact mean queue length, sum_n n*pi0*R^n*e = pi0*R*inv(I-R)^2*e.
220QN = pi0*R*((I-R)\((I-R)\e));
221end
222
223%{
224 % @brief Solves A0*G^2 + A1*G + A2 = 0 for the matrix G
225 %
226 % @details Uses the exact rank-one closed form when A2 has rank one, and
227 % otherwise functional iteration followed by Newton's method. Never returns
228 % an unconverged iterate.
229%}
230function G = qbd_rap_g(A0, A1, A2, blockScale)
231m = size(A1,1);
232e = ones(m,1);
233resTol = 1e-10*blockScale;
234
235% Rank-one A2 = u*v admits the closed form G = e*v/(v*e), which is the case
236% deliberately chosen in the example of Bean and Nielsen (2010) precisely so
237% that G is available a priori.
238sv = svd(A2);
239if numel(sv) > 1 && sv(1) > 0 && sv(2) <= 1e-10*sv(1)
240 [~,~,W] = svd(A2);
241 v = W(:,1).';
242 if abs(v*e) < 1e-12*norm(v,inf)
243 line_error(mfilename, ['A2 has rank one but its right factor v satisfies ' ...
244 'v*e = 0, so the closed form G = e*v/(v*e) is undefined.']);
245 end
246 G = e*(v/(v*e));
247 res = norm(A0*G*G + A1*G + A2, 'fro');
248 if res > resTol
249 line_error(mfilename, sprintf(['The rank-one closed form for G leaves a ' ...
250 'residual ||A0*G^2 + A1*G + A2||_F = %g, which is above the roundoff ' ...
251 'level %g.'], res, resTol));
252 end
253 return
254end
255
256% General case. Natural functional iteration G <- inv(-A1)*(A2 + A0*G^2)
257% gives a warm start; it is the standard Markovian iteration but has no
258% convergence guarantee for blocks that are not nonnegative.
259if rcond(-A1) < eps
260 line_error(mfilename, 'The local block A1 is singular, the iteration for G cannot be started.');
261end
262G = zeros(m);
263for it = 1:200
264 Gnew = (-A1)\(A2 + A0*G*G);
265 if norm(Gnew-G, 'fro') <= 1e-14*max(norm(G,'fro'),1)
266 G = Gnew;
267 break
268 end
269 G = Gnew;
270 if ~all(isfinite(G(:)))
271 break
272 end
273end
274if ~all(isfinite(G(:)))
275 G = zeros(m);
276end
277
278% Newton's method on F(G) = A0*G^2 + A1*G + A2. The derivative in the
279% direction H is (A0*G+A1)*H + A0*H*G, a Sylvester-type operator solved here
280% through its Kronecker expansion (I kron (A0*G+A1) + G' kron A0)*vec(H).
281Im = eye(m);
282for it = 1:100
283 res = A0*G*G + A1*G + A2;
284 if norm(res, 'fro') <= resTol
285 break
286 end
287 J = kron(Im, A0*G + A1) + kron(G.', A0);
288 if rcond(J) < eps
289 break
290 end
291 H = reshape(-(J\res(:)), m, m);
292 G = G + H;
293 if ~all(isfinite(G(:)))
294 break
295 end
296end
297
298res = Inf;
299if all(isfinite(G(:)))
300 res = norm(A0*G*G + A1*G + A2, 'fro');
301end
302if ~(res <= resTol) || norm(G*e - e, inf) > 1e-8
303 line_error(mfilename, sprintf(['Could not compute the matrix G for this QBD with ' ...
304 'RAP components: residual ||A0*G^2 + A1*G + A2||_F = %g against a tolerance ' ...
305 'of %g, and ||G*e-e||_inf = %g. The blocks are not nonnegative, so neither ' ...
306 'the functional iteration nor Newton''s method is guaranteed to converge, and ' ...
307 'the justification of algorithms for G in this setting is left as an open ' ...
308 'problem in Section 6 of N. G. Bean and B. F. Nielsen, "Quasi-Birth-and-Death ' ...
309 'Processes with Rational Arrival Process Components", Stochastic Models, 26(3), ' ...
310 '2010, pp. 309-334. Supply a model with a rank-one A2, for which G is available ' ...
311 'in closed form.'], res, resTol, norm(G*e-e,inf)));
312end
313end
Definition Station.m:245