5#ifndef LINE_API_MAM_LDQBD_H
6#define LINE_API_MAM_LDQBD_H
83namespace ldqbd_detail {
87T det_lu(
const Matrix<T>& A) {
88 const std::size_t n = A.rows();
89 if (A.cols() != n)
throw InputError(
"ldqbd: determinant of a non-square matrix");
90 if (n == 0)
return num_traits<T>::from_int(1);
92 std::vector<std::size_t> piv;
95 }
catch (
const NumericError&) {
96 return num_traits<T>::from_int(0);
98 T d = num_traits<T>::from_int(1);
99 for (std::size_t i = 0; i < n; ++i) d *= LU(i, i);
100 for (std::size_t k = 0; k < n; ++k)
101 if (piv[k] != k) d = -d;
107Matrix<T> right_divide(
const Matrix<T>& X,
const Matrix<T>& U, std::size_t level) {
108 const T d = det_lu(U);
110 if (num_traits<T>::is_exact) {
111 singular = (d == num_traits<T>::from_int(0));
113 singular = !(
num_abs(T(d)) > num_traits<T>::from_double(1e-14));
117 if (num_traits<T>::is_exact)
118 throw NumericError(
"ldqbd_R: the local block at level " + std::to_string(level) +
119 " is exactly singular; its pseudo-inverse is not rational, so the "
120 "exact instantiation refuses rather than returning a rounded value");
121 Matrix<double> Ud(U.rows(), U.cols());
122 for (std::size_t i = 0; i < U.rows(); ++i)
123 for (std::size_t j = 0; j < U.cols(); ++j) Ud(i, j) = num_traits<T>::to_double(U(i, j));
124 const Matrix<double> P =
pinv(Ud);
125 Matrix<T> Pt(P.rows(), P.cols());
126 for (std::size_t i = 0; i < P.rows(); ++i)
127 for (std::size_t j = 0; j < P.cols(); ++j) Pt(i, j) = num_traits<T>::from_double(P(i, j));
146 if (q1.size() < 2)
throw InputError(
"ldqbd_R: at least two levels are required");
147 const std::size_t N = q1.size() - 1;
148 if (q0.size() < N)
throw InputError(
"ldqbd_R: q0 must hold the up-blocks of levels 0..N-1");
149 if (q2.size() < N + 1)
throw InputError(
"ldqbd_R: q2 must be indexed by level, 0..N");
151 std::vector<Matrix<T>> R(N + 1);
153 R[N] = ldqbd_detail::right_divide(
156 for (std::size_t n = N - 1; n >= 1; --n) {
160 R[n] = ldqbd_detail::right_divide(q0[n - 1], U, n);
205 if (q1.size() < 2)
throw InputError(
"ldqbd_pi: at least two levels are required");
206 const std::size_t N = q1.size() - 1;
207 if (R.size() != N + 1)
throw InputError(
"ldqbd_pi: R must be indexed by level, 0..N");
213 if (q1[0].rows() == 1) {
217 out.
pi_level[0] = qbd_detail::statvec(A);
223 for (std::size_t n = 0; n <= N; ++n)
224 for (
const T& v : out.
pi_level[n]) total += v;
225 if (total == zero)
throw NumericError(
"ldqbd_pi: the stationary vector has zero total mass");
227 out.
pi.assign(N + 1, zero);
228 for (std::size_t n = 0; n <= N; ++n) {
229 for (T& v : out.
pi_level[n]) v /= total;
231 for (
const T& v : out.
pi_level[n]) s += v;
240 std::vector<Matrix<T>>
R;
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.
LdqbdPi< T > ldqbd_pi(const std::vector< Matrix< T > > &R, const std::vector< Matrix< T > > &q0, const std::vector< Matrix< T > > &q1, const std::vector< Matrix< T > > &q2)
Stationary distribution of a level-dependent QBD given its rate matrices (ldqbd_pi....
std::vector< Matrix< T > > ldqbd_R(const std::vector< Matrix< T > > &q0, const std::vector< Matrix< T > > &q1, const std::vector< Matrix< T > > &q2)
Rate matrices R^(1), ..., R^(N) of a level-dependent QBD (ldqbd_R.m).
LdqbdResult< T > ldqbd(const std::vector< Matrix< T > > &q0, const std::vector< Matrix< T > > &q1, const std::vector< Matrix< T > > &q2)
Solve a level-dependent QBD: rate matrices and stationary law (ldqbd.m).
Matrix< double > pinv(const Matrix< double > &A)
Moore-Penrose pseudo-inverse, A^+ = V diag(1/s_i) U^T over the singular values above max(m,...
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
std::vector< std::size_t > lu_factor(Matrix< T > &A)
In-place LU of A (n x n).
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.
Number-type abstraction for the templated API port.
Quasi-birth-death processes: the rate matrix R, the fundamental matrix G, the caudal characteristic,...
Stationary distribution of a level-dependent QBD, per level and per phase.
std::vector< T > pi
pi[n] = sum of pi_level[n], the level marginal
std::vector< std::vector< T > > pi_level
pi_level[n], one entry per phase of level n
R and the stationary distribution together (ldqbd.m).
std::vector< Matrix< T > > R
Singular value decomposition WITH the singular vectors, and the Moore-Penrose pseudo-inverse built fr...