5#ifndef LINE_API_MAM_QBD_R_H
6#define LINE_API_MAM_QBD_R_H
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);
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);
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;
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;
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;
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);
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);
155 using namespace qbd_detail;
163 using namespace qbd_detail;
187 using namespace qbd_detail;
194 for (
unsigned it = 0; it < iter_max; ++it) {
197 if (norm1(msub(R, Rprime)) <= tol)
break;
220 "qbd_R_logred requires transcendental arithmetic");
221 using namespace qbd_detail;
222 const std::size_t r = L.
rows();
230 const std::vector<T> e =
ones<T>(r);
231 for (
unsigned it = 0; it < iter_max; ++it) {
238 S = madd(S,
matmul(T_, iLB));
242 const std::vector<T> Se =
mulvec(S, e);
244 for (std::size_t i = 0; i < r; ++i) dev +=
num_abs(T(e[i] - Se[i]));
247 const std::vector<T> Te =
mulvec(T_, e);
249 for (std::size_t i = 0; i < r; ++i) left +=
num_abs(Te[i]);
250 if (dev <= tol || left <= tol)
break;
286 unsigned iter_max,
const T& tol) {
288 "qbd_fundmat requires transcendental arithmetic");
289 using namespace qbd_detail;
290 const std::size_t m = L.
rows();
292 throw InputError(
"qbd_fundmat: B, L and F must be square and of equal order");
296 for (std::size_t i = 0; i < m; ++i) {
297 const T d = -L(i, i);
298 if (d > lamb) lamb = d;
301 throw NumericError(
ErrorKind{
"Degenerate"},
"qbd_fundmat: the local block has no negative diagonal entry");
303 const Matrix<T> Bm = mscale(B, inv_lamb);
304 const Matrix<T> Lm = madd(mscale(L, inv_lamb), I);
305 const Matrix<T> Fm = mscale(F, inv_lamb);
314 while (check > tol && numit < iter_max) {
321 G = madd(G,
matmul(PI, BF));
323 const T nb = norminf(BB);
324 const T nf = norminf(BF);
325 check = nb < nf ? nb : nf;
362 "qbd_caudal requires transcendental arithmetic");
363 const std::size_t n = R.
rows();
364 if (R.
cols() != n)
throw InputError(
"qbd_caudal: matrix is not square");
365 if (n == 0)
throw InputError(
"qbd_caudal: empty matrix");
368 T lo = zero, hi = zero;
369 for (
unsigned it = 0; it < iter_max; ++it) {
370 const std::vector<T> Rx =
mulvec(R, x);
373 for (std::size_t i = 1; i < n; ++i) {
374 const T q = Rx[i] / x[i];
378 if (T(hi - lo) <= tol)
break;
381 for (std::size_t i = 0; i < n; ++i) {
386 for (std::size_t i = 0; i < n; ++i) x[i] /= s;
418 std::size_t max_levels,
const T& mass_tol) {
419 using namespace qbd_detail;
420 const std::size_t m = R.
rows();
423 throw InputError(
"qbd_pi: block orders disagree with R");
424 if (max_levels == 0)
throw InputError(
"qbd_pi: max_levels must be positive");
431 std::vector<T> pi0 = statvec(madd(Lbar,
matmul(R, B)));
433 const std::vector<T> t =
vecmul(pi0, ImRinv);
435 for (
const T& v : t) tot += v;
436 if (tot == zero)
throw NumericError(
ErrorKind{
"Degenerate"},
"qbd_pi: degenerate normalization, sp(R) may exceed 1");
437 for (T& v : pi0) v /= tot;
439 std::vector<std::vector<T>> levels;
440 levels.push_back(pi0);
442 for (
const T& v : pi0) mass += v;
443 while (levels.size() < max_levels && T(one - mass) > mass_tol) {
444 const std::vector<T> nxt =
vecmul(levels.back(), R);
446 for (
const T& v : nxt) add += v;
447 levels.push_back(nxt);
451 for (std::size_t k = 0; k < levels.size(); ++k)
452 for (std::size_t j = 0; j < m; ++j) out(k, j) = levels[k][j];
459 return qbd_pi(B, Lbar, R,
static_cast<std::size_t
>(20000),
NumericError(const std::string &what)
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.
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.
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...
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).
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.
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).
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.
Conservation laws of a layered queueing network, enumerated from its structure.
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
std::vector< T > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
std::vector< T > ones(std::size_t n)
Column vector of ones, the ubiquitous e in MAP algebra.
Matrix< T > eye(std::size_t n)
Identity of order n.
Number-type abstraction for the templated API port.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
G and R together, as returned by qbd_fundmat.