5#ifndef LINE_API_MC_CTMC_GTH_H
6#define LINE_API_MC_CTMC_GTH_H
61 const std::size_t n = Q.
rows();
62 if (Q.
cols() != n)
throw InputError(std::string(fn) +
": the generator must be a square matrix");
65 for (std::size_t i = 0; i < n; ++i)
66 for (std::size_t j = 0; j < n; ++j) {
71 if (bad)
throw InputError(std::string(fn) +
": the off-diagonal rates must be finite and nonnegative");
82std::vector<T> gth_irreducible(
Matrix<T> A,
bool& ok) {
83 const std::size_t n = A.
rows();
87 if (n == 1)
return std::vector<T>{one};
88 for (std::size_t i = 0; i < n; ++i) A(i, i) = zero;
89 for (std::size_t k = n - 1; k >= 1; --k) {
91 for (std::size_t j = 0; j < k; ++j) s += A(k, j);
92 bool bad = !(s > zero);
93 if constexpr (!num_traits<T>::is_exact) bad = bad || !std::isfinite(num_traits<T>::to_double(s));
96 return std::vector<T>();
98 for (std::size_t i = 0; i < k; ++i) A(i, k) /= s;
99 for (std::size_t i = 0; i < k; ++i) {
100 const T aik = A(i, k);
101 if (aik == zero)
continue;
102 for (std::size_t j = 0; j < k; ++j) A(i, j) += aik * A(k, j);
105 std::vector<T> x(n, zero);
107 for (std::size_t k = 1; k < n; ++k) {
109 for (std::size_t i = 0; i < k; ++i) v += x[i] * A(i, k);
113 for (
const T& v : x)
tot += v;
114 for (T& v : x) v /=
tot;
119Matrix<T> gth_sub(
const Matrix<T>& A,
const std::vector<std::size_t>& r,
const std::vector<std::size_t>& c) {
120 Matrix<T> B(r.size(), c.size());
121 for (std::size_t a = 0; a < r.size(); ++a)
122 for (std::size_t b = 0; b < c.size(); ++b) B(a, b) = A(r[a], c[b]);
136 const Matrix<T> A = detail::gth_offdiag(Q,
"ctmc_gth");
137 const std::size_t n = A.
rows();
139 if (n == 0)
throw InputError(
"ctmc_gth: empty generator");
142 std::size_t nclosed = 0, closed = 0;
143 for (std::size_t c = 0; c < s.
recurrent.size(); ++c)
148 if (nclosed != 1)
return GthResult<T>{std::vector<T>(), 1};
149 std::vector<std::size_t> S = s.
members[closed];
150 std::sort(S.begin(), S.end());
152 const std::vector<T> x = detail::gth_irreducible(detail::gth_sub(A, S, S), ok);
155 "ctmc_gth: GTH elimination met a zero pivot on a closed class: the rates underflowed");
156 std::vector<T> p(n, zero);
157 for (std::size_t k = 0; k < S.size(); ++k) p[S[k]] = x[k];
166 throw InputError(
"ctmc_gth: the chain has several closed classes, so its stationary distribution is "
167 "not unique. Use ctmc_cesaro(Q) for the limit from every initial state, or "
168 "ctmc_solve_reducible(Q, pi0).");
181 const Matrix<T> A = detail::gth_offdiag(Q,
"ctmc_cesaro");
182 const std::size_t n = A.
rows();
184 if (n == 0)
throw InputError(
"ctmc_cesaro: empty generator");
191 std::vector<std::size_t> closed, T_;
192 for (std::size_t c = 0; c < s.
recurrent.size(); ++c)
194 for (std::size_t v = 0; v < n; ++v)
196 const std::size_t K = closed.size(), nT = T_.size();
197 std::vector<std::vector<T>> H(K, std::vector<T>(nT, zero));
200 for (std::size_t a = 0; a < nT; ++a) {
202 for (std::size_t j = 0; j < n; ++j) out += A(T_[a], j);
203 for (std::size_t b = 0; b < nT; ++b) M(a, b) = zero - A(T_[a], T_[b]);
206 for (std::size_t k = 0; k < K; ++k) {
207 std::vector<T> rhs(nT, zero);
208 for (std::size_t a = 0; a < nT; ++a)
209 for (std::size_t j : s.
members[closed[k]]) rhs[a] += A(T_[a], j);
210 H[k] =
solve(M, rhs);
212 for (std::size_t a = 0; a < nT; ++a) {
214 for (std::size_t k = 0; k < K; ++k) {
215 if (H[k][a] < zero) H[k][a] = zero;
218 for (std::size_t k = 0; k < K; ++k) H[k][a] /= tot;
221 for (std::size_t k = 0; k < K; ++k) {
222 std::vector<std::size_t> Ck = s.
members[closed[k]];
223 std::sort(Ck.begin(), Ck.end());
225 const std::vector<T> pik = detail::gth_irreducible(detail::gth_sub(A, Ck, Ck), ok);
228 "ctmc_cesaro: GTH elimination met a zero pivot on a closed class: the rates underflowed");
229 for (std::size_t i : Ck)
230 for (std::size_t j = 0; j < Ck.size(); ++j) J(i, Ck[j]) = pik[j];
231 for (std::size_t a = 0; a < nT; ++a)
232 for (std::size_t j = 0; j < Ck.size(); ++j) J(T_[a], Ck[j]) = H[k][a] * pik[j];
NumericError(const std::string &what)
The exception types the port throws.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
double tot(const std::vector< double > &pops)
Matrix< T > ctmc_cesaro(const Matrix< T > &Q)
Limit matrix J = lim expm(Q t) of any finite CTMC (Agaev-Chebotarev 2000).
GthResult< T > ctmc_gth_flag(const Matrix< T > &Q)
Stationary vector of a CTMC by GTH elimination, with a flag.
SccResult stronglyconncomp(const Matrix< T > &A)
Strongly connected components of a directed graph, and which of them are recurrent (closed under the ...
std::vector< T > dtmc_gth(const Matrix< T > &P)
Stationary vector of a DTMC by GTH; self-loops are not read.
Matrix< T > dtmc_cesaro(const Matrix< T > &P)
Cesaro limit of P^k for any finite DTMC; equals ctmc_cesaro(P - I).
std::vector< T > ctmc_gth(const Matrix< T > &Q)
Stationary vector by GTH; throws InputError on several closed classes.
Conservation laws of a layered queueing network, enumerated from its structure.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Number-type abstraction for the templated API port.
Strongly connected components of a directed graph, and which of them are recurrent (closed under the ...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
int flag
0 success, 1 several closed classes (not unique)
std::vector< T > p
stationary vector (empty when flag == 1)
std::vector< bool > recurrent
recurrent[c-1] is true when component c has no edge leaving it.
std::vector< std::size_t > scc
Component index of each state, 1-based as in MATLAB (0 is never used).
std::vector< std::vector< std::size_t > > members
Member states of each component, ascending.