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]));
245 if (dev <= tol)
break;
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();
287 throw InputError(
"qbd_fundmat: B, L and F must be square and of equal order");
291 for (std::size_t i = 0; i < m; ++i) {
292 const T d = -L(i, i);
293 if (d > lamb) lamb = d;
296 throw NumericError(
"qbd_fundmat: the local block has no negative diagonal entry");
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);
309 while (check > tol && numit < iter_max) {
316 G = madd(G,
matmul(PI, BF));
318 const T nb = norminf(BB);
319 const T nf = norminf(BF);
320 check = nb < nf ? nb : nf;
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");
363 T lo = zero, hi = zero;
364 for (
unsigned it = 0; it < iter_max; ++it) {
365 const std::vector<T> Rx =
mulvec(R, x);
368 for (std::size_t i = 1; i < n; ++i) {
369 const T q = Rx[i] / x[i];
373 if (T(hi - lo) <= tol)
break;
376 for (std::size_t i = 0; i < n; ++i) {
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;
413 std::size_t max_levels,
const T& mass_tol) {
414 using namespace qbd_detail;
415 const std::size_t m = R.
rows();
418 throw InputError(
"qbd_pi: block orders disagree with R");
419 if (max_levels == 0)
throw InputError(
"qbd_pi: max_levels must be positive");
426 std::vector<T> pi0 = statvec(madd(Lbar,
matmul(R, B)));
428 const std::vector<T> t =
vecmul(pi0, ImRinv);
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;
434 std::vector<std::vector<T>> levels;
435 levels.push_back(pi0);
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);
441 for (
const T& v : nxt) add += v;
442 levels.push_back(nxt);
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];
454 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.
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.
G and R together, as returned by qbd_fundmat.