LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qbd_r.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_MAM_QBD_R_H
6#define LINE_API_MAM_QBD_R_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * Quasi-birth-death processes: the rate matrix R, the fundamental matrix G,
12 * the caudal characteristic, and the stationary distribution.
13 *
14 * Templated port of matlab/src/api/mam/qbd_R.m, qbd_R_logred.m, qbd_fundmat.m,
15 * and of the boundary solve of matlab/lib/thirdparty/smcsolver/QBD_pi.m and the
16 * caudal characteristic of QBD_Caudal.m. Cross-checked against
17 * jar/src/main/java/jline/api/mam/Qbd_R.java and Qbd_R_logred.java.
18 *
19 * Block convention, level-independent QBD in continuous time:
20 *
21 * Q = | Lbar F 0 0 ... |
22 * | B L F 0 ... |
23 * | 0 B L F ... |
24 * | ... |
25 *
26 * with B the backward (downward) block, L the local block and F the forward
27 * (upward) block. R is the minimal non-negative solution of
28 *
29 * F + R L + R^2 B = 0,
30 *
31 * which is the equation A0 + R A1 + R^2 A2 = 0 with A0 = F, A1 = L, A2 = B.
32 * G is the minimal non-negative solution of B + L G + F G^2 = 0.
33 *
34 * ARITHMETIC. Three routines here compute R or G by a fixed-point iteration
35 * driven to a tolerance -- successive substitution, logarithmic reduction and
36 * cyclic reduction -- and one (qbd_caudal) brackets a spectral radius. None of
37 * them terminates in a finite number of field operations, so the value they
38 * return is an approximation no matter how the arithmetic is carried out;
39 * running them at exact rational arithmetic would produce a rational number
40 * with a denominator doubling at every cyclic-reduction step and still not the
41 * exact R. They are therefore gated on num_traits<T>::has_transcendental,
42 * which admits double and Real<D> and rejects Rational at compile time.
43 *
44 * Everything downstream of R is a finite rational computation and is left
45 * un-gated: qbd_R_residual, qbd_pi (a null-vector solve plus a geometric tail)
46 * and the moment formulas in qbd_mapmap1.h all instantiate at Rational. That
47 * split is the point of the port -- given R to whatever accuracy, the boundary
48 * probabilities and the queue-length moments carry no additional error.
49 */
50
51#include <cstddef>
52#include <vector>
53
54#include "line/num/number.h"
55#include "line/util/error.h"
56#include "line/util/linalg.h"
57#include "line/util/lu.h"
58#include "line/util/matrix.h"
59
60namespace line {
61namespace mam {
62
63namespace qbd_detail {
64
65/** Elementwise A - B. */
66template <class T>
67Matrix<T> msub(const Matrix<T>& A, const Matrix<T>& B) {
68 if (A.rows() != B.rows() || A.cols() != B.cols()) throw InputError("qbd: shape mismatch");
69 Matrix<T> C(A.rows(), A.cols());
70 for (std::size_t i = 0; i < A.rows(); ++i)
71 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) = A(i, j) - B(i, j);
72 return C;
73}
74
75/** Elementwise A + B. */
76template <class T>
77Matrix<T> madd(const Matrix<T>& A, const Matrix<T>& B) {
78 if (A.rows() != B.rows() || A.cols() != B.cols()) throw InputError("qbd: shape mismatch");
79 Matrix<T> C(A.rows(), A.cols());
80 for (std::size_t i = 0; i < A.rows(); ++i)
81 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) = A(i, j) + B(i, j);
82 return C;
83}
84
85/** Elementwise s*A. */
86template <class T>
87Matrix<T> mscale(const Matrix<T>& A, const T& s) {
88 Matrix<T> C(A.rows(), A.cols());
89 for (std::size_t i = 0; i < A.rows(); ++i)
90 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) = A(i, j) * s;
91 return C;
92}
93
94/** MATLAB's norm(X,1): the largest absolute column sum. */
95template <class T>
96T norm1(const Matrix<T>& A) {
97 T best = num_traits<T>::from_int(0);
98 for (std::size_t j = 0; j < A.cols(); ++j) {
99 T s = num_traits<T>::from_int(0);
100 for (std::size_t i = 0; i < A.rows(); ++i) s += num_abs(T(A(i, j)));
101 if (s > best) best = s;
102 }
103 return best;
104}
105
106/** MATLAB's norm(X,inf): the largest absolute row sum. */
107template <class T>
108T norminf(const Matrix<T>& A) {
109 T best = num_traits<T>::from_int(0);
110 for (std::size_t i = 0; i < A.rows(); ++i) {
111 T s = num_traits<T>::from_int(0);
112 for (std::size_t j = 0; j < A.cols(); ++j) s += num_abs(T(A(i, j)));
113 if (s > best) best = s;
114 }
115 return best;
116}
117
118/**
119 * Stationary row vector of M: solves x M = 0 with sum(x) = 1 by replacing the
120 * last column of M with ones and solving the transposed system, as MATLAB's
121 * statvec does. Unlike mc::ctmc_solve this does NOT recompute the diagonal of
122 * M: the level-zero block Lbar + R B is already a generator by construction,
123 * and repairing its diagonal would silently absorb an error in R instead of
124 * letting it show up in the residual.
125 */
126template <class T>
127std::vector<T> statvec(const Matrix<T>& M) {
128 const std::size_t m = M.rows();
129 if (M.cols() != m) throw InputError("qbd statvec: matrix is not square");
130 if (m == 0) throw InputError("qbd statvec: empty matrix");
131 if (m == 1) return std::vector<T>{num_traits<T>::from_int(1)};
132 const T one = num_traits<T>::from_int(1);
133 const T zero = num_traits<T>::from_int(0);
134 Matrix<T> A(m, m);
135 for (std::size_t i = 0; i < m; ++i)
136 for (std::size_t j = 0; j < m; ++j) A(i, j) = (i == m - 1) ? one : M(j, i);
137 std::vector<T> b(m, zero);
138 b[m - 1] = one;
139 return solve(A, b);
140}
141
142} // namespace qbd_detail
143
144/**
145 * Residual of the defining equation of R, ||F + R L + R^2 B||_inf.
146 *
147 * Exact in rational arithmetic, which is what makes it a usable oracle: a
148 * residual computed in the same double arithmetic that produced R can be small
149 * simply because the error cancels, whereas evaluating the same expression on
150 * the exact rationals of B, L, F and on the exact rational lift of R measures
151 * only how far R itself is from a solution.
152 */
153template <class T>
154T qbd_R_residual(const Matrix<T>& B, const Matrix<T>& L, const Matrix<T>& F, const Matrix<T>& R) {
155 using namespace qbd_detail;
156 const Matrix<T> res = madd(madd(F, matmul(R, L)), matmul(matmul(R, R), B));
157 return norminf(res);
158}
159
160/** Residual of the defining equation of G, ||B + L G + F G^2||_inf. */
161template <class T>
162T qbd_G_residual(const Matrix<T>& B, const Matrix<T>& L, const Matrix<T>& F, const Matrix<T>& G) {
163 using namespace qbd_detail;
164 const Matrix<T> res = madd(madd(B, matmul(L, G)), matmul(F, matmul(G, G)));
165 return norminf(res);
166}
167
168/**
169 * R by successive substitutions (qbd_R.m): iterate R <- -(F + R^2 B) L^-1.
170 *
171 * Linearly convergent, at the rate of the caudal characteristic, so it is slow
172 * near saturation; qbd_fundmat is quadratically convergent and is what
173 * qbd_mapmap1 uses. Kept because it is the MATLAB entry point of the same name
174 * and because its simplicity makes it a useful independent check on the
175 * cyclic-reduction result.
176 *
177 * @param tol stopping tolerance on ||R_k - R_{k+1}||_1 (MATLAB uses 1e-12)
178 * @param B backward (level down) block A_2
179 * @param L local (within level) block A_1
180 * @param F forward (level up) block A_0
181 * @param iter_max iteration cap
182 */
183template <class T>
184Matrix<T> qbd_R(const Matrix<T>& B, const Matrix<T>& L, const Matrix<T>& F, unsigned iter_max,
185 const T& tol) {
186 static_assert(num_traits<T>::has_transcendental, "qbd_R requires transcendental arithmetic");
187 using namespace qbd_detail;
188 const Matrix<T> Linv = inverse(L);
189 const Matrix<T> Fil = matmul(F, Linv);
190 const Matrix<T> BiL = matmul(B, Linv);
191 const Matrix<T> negFil = mscale(Fil, T(num_traits<T>::from_int(-1)));
192 Matrix<T> R = negFil;
193 Matrix<T> Rprime = msub(negFil, matmul(matmul(R, R), BiL));
194 for (unsigned it = 0; it < iter_max; ++it) {
195 R = Rprime;
196 Rprime = msub(negFil, matmul(matmul(R, R), BiL));
197 if (norm1(msub(R, Rprime)) <= tol) break;
198 }
199 return Rprime;
200}
201
202/** qbd_R with the MATLAB defaults, 100000 iterations and tolerance 1e-12. */
203template <class T>
204Matrix<T> qbd_R(const Matrix<T>& B, const Matrix<T>& L, const Matrix<T>& F) {
205 return qbd_R(B, L, F, 100000u, T(num_traits<T>::from_double(1e-12)));
206}
207
208/**
209 * R by logarithmic reduction (qbd_R_logred.m).
210 *
211 * Builds the matrix S of the taboo probabilities of ever going down, doubling
212 * the horizon at every step, then recovers R = -F (L + F S)^-1. Quadratically
213 * convergent; the stopping test is on how close S e is to e, i.e. on how much
214 * mass of the downward passage is still unaccounted for.
215 */
216template <class T>
217Matrix<T> qbd_R_logred(const Matrix<T>& B, const Matrix<T>& L, const Matrix<T>& F, unsigned iter_max,
218 const T& tol) {
220 "qbd_R_logred requires transcendental arithmetic");
221 using namespace qbd_detail;
222 const std::size_t r = L.rows();
223 const Matrix<T> Linv = inverse(L);
224 const T minus = num_traits<T>::from_int(-1);
225 Matrix<T> iLF = mscale(matmul(Linv, F), minus);
226 Matrix<T> iLB = mscale(matmul(Linv, B), minus);
227 Matrix<T> T_ = iLF;
228 Matrix<T> S = iLB;
229 const Matrix<T> I = eye<T>(r);
230 const std::vector<T> e = ones<T>(r);
231 for (unsigned it = 0; it < iter_max; ++it) {
232 const Matrix<T> D = madd(matmul(iLF, iLB), matmul(iLB, iLF));
233 const Matrix<T> Minv = inverse(msub(I, D));
234 const Matrix<T> iLFn = matmul(Minv, matmul(iLF, iLF));
235 const Matrix<T> iLBn = matmul(Minv, matmul(iLB, iLB));
236 iLF = iLFn;
237 iLB = iLBn;
238 S = madd(S, matmul(T_, iLB));
239 T_ = matmul(T_, iLF);
240 // ||e - S e||_1 over the column vector, i.e. the sum of absolute
241 // deficits, exactly as MATLAB's norm(ones - S*ones, 1).
242 const std::vector<T> Se = mulvec(S, e);
243 T dev = num_traits<T>::from_int(0);
244 for (std::size_t i = 0; i < r; ++i) dev += num_abs(T(e[i] - Se[i]));
245 if (dev <= tol) break;
246 }
247 const Matrix<T> U = madd(L, matmul(F, S));
248 return mscale(matmul(F, inverse(U)), minus);
249}
250
251/** qbd_R_logred with the MATLAB defaults, 100000 iterations and tolerance 1e-12. */
252template <class T>
253Matrix<T> qbd_R_logred(const Matrix<T>& B, const Matrix<T>& L, const Matrix<T>& F) {
254 return qbd_R_logred(B, L, F, 100000u, T(num_traits<T>::from_double(1e-12)));
255}
256
257/** G and R together, as returned by qbd_fundmat. */
258template <class T>
264
265/**
266 * G and R by cyclic reduction (qbd_fundmat.m, the Bini-Meini logarithmic
267 * reduction on the raw level blocks).
268 *
269 * The blocks are first uniformized by lambda = max(-diag(L)) into a discrete
270 * QBD (Bm, Lm, Fm), G is accumulated over doubling horizons, and R is
271 * recovered as R = Fm (I - (Lm + Fm G))^-1. The uniformization does not change
272 * either G or R: G is a probability matrix of the embedded jump chain, and the
273 * R of the discrete chain solves R = Fm + R Lm + R^2 Bm, which is F + R L +
274 * R^2 B = 0 after multiplying through by lambda.
275 *
276 * Quadratically convergent, so 50 iterations is a generous bound even at
277 * utilizations where successive substitution needs millions.
278 */
279template <class T>
281 unsigned iter_max, const T& tol) {
283 "qbd_fundmat requires transcendental arithmetic");
284 using namespace qbd_detail;
285 const std::size_t m = L.rows();
286 if (L.cols() != m || B.rows() != m || B.cols() != m || F.rows() != m || F.cols() != m)
287 throw InputError("qbd_fundmat: B, L and F must be square and of equal order");
288 const Matrix<T> I = eye<T>(m);
289
290 T lamb = num_traits<T>::from_int(0);
291 for (std::size_t i = 0; i < m; ++i) {
292 const T d = -L(i, i);
293 if (d > lamb) lamb = d;
294 }
295 if (lamb <= num_traits<T>::from_int(0))
296 throw NumericError("qbd_fundmat: the local block has no negative diagonal entry");
297 const T inv_lamb = num_traits<T>::from_int(1) / lamb;
298 const Matrix<T> Bm = mscale(B, inv_lamb);
299 const Matrix<T> Lm = madd(mscale(L, inv_lamb), I);
300 const Matrix<T> Fm = mscale(F, inv_lamb);
301
302 Matrix<T> BF = inverse(msub(I, Lm));
303 Matrix<T> BB = matmul(BF, Fm);
304 BF = matmul(BF, Bm);
305 Matrix<T> G = BF;
306 Matrix<T> PI = BB;
307 T check = num_traits<T>::from_int(1);
308 unsigned numit = 0;
309 while (check > tol && numit < iter_max) {
310 const Matrix<T> Lstar = madd(matmul(BF, BB), matmul(BB, BF));
311 const Matrix<T> Bstar = matmul(BB, BB);
312 const Matrix<T> Fstar = matmul(BF, BF);
313 const Matrix<T> Minv = inverse(msub(I, Lstar));
314 BF = matmul(Minv, Fstar);
315 BB = matmul(Minv, Bstar);
316 G = madd(G, matmul(PI, BF));
317 PI = matmul(PI, BB);
318 const T nb = norminf(BB);
319 const T nf = norminf(BF);
320 check = nb < nf ? nb : nf;
321 ++numit;
322 }
323 QbdFundMat<T> out;
324 out.G = G;
325 out.R = matmul(Fm, inverse(msub(I, madd(Lm, matmul(Fm, G)))));
326 out.iterations = numit;
327 return out;
328}
329
330/** qbd_fundmat with the MATLAB defaults, 50 iterations and tolerance 1e-14. */
331template <class T>
333 return qbd_fundmat(B, L, F, 50u, T(num_traits<T>::from_double(1e-14)));
334}
335
336/**
337 * Caudal characteristic eta = sp(R), the decay rate of the queue-length tail.
338 *
339 * R is entrywise non-negative, so its spectral radius is its Perron root and
340 * is bracketed by the Collatz-Wielandt bounds
341 *
342 * min_i (R x)_i / x_i <= sp(R) <= max_i (R x)_i / x_i
343 *
344 * for any strictly positive x. Power iteration is run on I + R, which is
345 * aperiodic whenever R is irreducible and keeps the iterate strictly positive,
346 * and the midpoint of the bracket is returned once it is tighter than tol.
347 *
348 * This is the same quantity as MATLAB's QBD_Caudal, which brackets it by
349 * bisecting on the dominant eigenvalue of A(eta) = B + L eta + F eta^2, and as
350 * the JAR's spectralRadiusMapmap1, which takes the largest eigenvalue modulus
351 * of R from a full eigendecomposition. The bracket form is preferred here
352 * because it needs no eigensolver and reports its own accuracy.
353 */
354template <class T>
355T qbd_caudal(const Matrix<T>& R, unsigned iter_max, const T& tol) {
357 "qbd_caudal requires transcendental arithmetic");
358 const std::size_t n = R.rows();
359 if (R.cols() != n) throw InputError("qbd_caudal: matrix is not square");
360 if (n == 0) throw InputError("qbd_caudal: empty matrix");
361 const T zero = num_traits<T>::from_int(0);
362 std::vector<T> x = ones<T>(n);
363 T lo = zero, hi = zero;
364 for (unsigned it = 0; it < iter_max; ++it) {
365 const std::vector<T> Rx = mulvec(R, x);
366 lo = Rx[0] / x[0];
367 hi = lo;
368 for (std::size_t i = 1; i < n; ++i) {
369 const T q = Rx[i] / x[i];
370 if (q < lo) lo = q;
371 if (q > hi) hi = q;
372 }
373 if (T(hi - lo) <= tol) break;
374 // Advance with I + R: sp(I+R) = 1 + sp(R) and the iterate stays > 0.
375 T s = zero;
376 for (std::size_t i = 0; i < n; ++i) {
377 x[i] += Rx[i];
378 s += x[i];
379 }
380 if (s == zero) throw NumericError("qbd_caudal: iterate collapsed to zero");
381 for (std::size_t i = 0; i < n; ++i) x[i] /= s;
382 }
383 return T((lo + hi) / num_traits<T>::from_int(2));
384}
385
386/** qbd_caudal with 10000 iterations and tolerance 1e-14. */
387template <class T>
389 return qbd_caudal(R, 10000u, T(num_traits<T>::from_double(1e-14)));
390}
391
392/**
393 * Stationary distribution of a QBD given R (QBD_pi.m, continuous-time branch,
394 * default boundary).
395 *
396 * The level-zero vector solves pi_0 (Lbar + R B) = 0 normalized by
397 * pi_0 (I - R)^-1 e = 1, and pi_{k+1} = pi_k R. Levels are generated until the
398 * accumulated mass exceeds 1 - mass_tol or max_levels is reached.
399 *
400 * Un-gated: given R this is a linear solve, a matrix inverse and a geometric
401 * recursion, all finite sequences of field operations. At Rational the level
402 * probabilities are the exact ones implied by the R that was supplied.
403 *
404 * @param B backward block (QBD_pi's B0)
405 * @param Lbar level-zero local block (QBD_pi's B1)
406 * @param R the rate matrix of the QBD
407 * @param max_levels level truncation used for the returned distribution
408 * @param mass_tol probability mass left above the truncation that is tolerated
409 * @return (levels x m) matrix, row k holding pi_k
410 */
411template <class T>
412Matrix<T> qbd_pi(const Matrix<T>& B, const Matrix<T>& Lbar, const Matrix<T>& R,
413 std::size_t max_levels, const T& mass_tol) {
414 using namespace qbd_detail;
415 const std::size_t m = R.rows();
416 if (R.cols() != m) throw InputError("qbd_pi: R is not square");
417 if (Lbar.rows() != m || Lbar.cols() != m || B.rows() != m || B.cols() != m)
418 throw InputError("qbd_pi: block orders disagree with R");
419 if (max_levels == 0) throw InputError("qbd_pi: max_levels must be positive");
420 const T one = num_traits<T>::from_int(1);
421 const T zero = num_traits<T>::from_int(0);
422
423 const Matrix<T> ImR = msub(eye<T>(m), R);
424 const Matrix<T> ImRinv = inverse(ImR);
425
426 std::vector<T> pi0 = statvec(madd(Lbar, matmul(R, B)));
427 // Normalize so that the whole chain has unit mass: sum_k pi_0 R^k e = 1.
428 const std::vector<T> t = vecmul(pi0, ImRinv);
429 T tot = zero;
430 for (const T& v : t) tot += v;
431 if (tot == zero) throw NumericError("qbd_pi: degenerate normalization, sp(R) may exceed 1");
432 for (T& v : pi0) v /= tot;
433
434 std::vector<std::vector<T>> levels;
435 levels.push_back(pi0);
436 T mass = zero;
437 for (const T& v : pi0) mass += v;
438 while (levels.size() < max_levels && T(one - mass) > mass_tol) {
439 const std::vector<T> nxt = vecmul(levels.back(), R);
440 T add = zero;
441 for (const T& v : nxt) add += v;
442 levels.push_back(nxt);
443 mass += add;
444 }
445 Matrix<T> out(levels.size(), m);
446 for (std::size_t k = 0; k < levels.size(); ++k)
447 for (std::size_t j = 0; j < m; ++j) out(k, j) = levels[k][j];
448 return out;
449}
450
451/** qbd_pi with the MATLAB-side defaults, 20000 levels and mass tolerance 1e-10. */
452template <class T>
453Matrix<T> qbd_pi(const Matrix<T>& B, const Matrix<T>& Lbar, const Matrix<T>& R) {
454 return qbd_pi(B, Lbar, R, static_cast<std::size_t>(20000),
456}
457
458} // namespace mam
459} // namespace line
460
461#endif // LINE_API_MAM_QBD_R_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
T qbd_G_residual(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, const Matrix< T > &G)
Residual of the defining equation of G, ||B + L G + F G^2||_inf.
Definition qbd_r.h:162
T qbd_caudal(const Matrix< T > &R, unsigned iter_max, const T &tol)
Caudal characteristic eta = sp(R), the decay rate of the queue-length tail.
Definition qbd_r.h:355
QbdFundMat< T > qbd_fundmat(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, unsigned iter_max, const T &tol)
G and R by cyclic reduction (qbd_fundmat.m, the Bini-Meini logarithmic reduction on the raw level blo...
Definition qbd_r.h:280
Matrix< T > qbd_R_logred(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, unsigned iter_max, const T &tol)
R by logarithmic reduction (qbd_R_logred.m).
Definition qbd_r.h:217
Matrix< T > qbd_R(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, unsigned iter_max, const T &tol)
R by successive substitutions (qbd_R.m): iterate R <- -(F + R^2 B) L^-1.
Definition qbd_r.h:184
Matrix< T > qbd_pi(const Matrix< T > &B, const Matrix< T > &Lbar, const Matrix< T > &R, std::size_t max_levels, const T &mass_tol)
Stationary distribution of a QBD given R (QBD_pi.m, continuous-time branch, default boundary).
Definition qbd_r.h:412
T qbd_R_residual(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, const Matrix< T > &R)
Residual of the defining equation of R, ||F + R L + R^2 B||_inf.
Definition qbd_r.h:154
T num_abs(const T &v)
Definition number.h:172
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Definition linalg.h:50
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Definition linalg.h:72
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
Definition linalg.h:36
std::vector< T > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
Definition linalg.h:62
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Definition lu.h:158
std::vector< T > ones(std::size_t n)
Column vector of ones, the ubiquitous e in MAP algebra.
Definition linalg.h:104
Matrix< T > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
Number-type abstraction for the templated API port.
G and R together, as returned by qbd_fundmat.
Definition qbd_r.h:259
Matrix< T > G
Definition qbd_r.h:260
Matrix< T > R
Definition qbd_r.h:261
unsigned iterations
Definition qbd_r.h:262