5#ifndef LINE_API_MC_DTMC_SOLVE_H
6#define LINE_API_MC_DTMC_SOLVE_H
58inline typename std::enable_if<std::is_floating_point<T>::value,
bool>::type dtmc_same(
59 const T& a,
const T& b) {
60 return std::memcmp(&a, &b,
sizeof(T)) == 0;
64inline typename std::enable_if<!std::is_floating_point<T>::value,
bool>::type dtmc_same(
65 const T& a,
const T& b) {
70bool dtmc_same_matrix(
const Matrix<T>& a,
const Matrix<T>& b) {
71 if (a.rows() != b.rows() || a.cols() != b.cols())
return false;
72 for (std::size_t i = 0; i < a.rows(); ++i)
73 for (std::size_t j = 0; j < a.cols(); ++j)
74 if (!dtmc_same(a(i, j), b(i, j)))
return false;
92 static constexpr std::size_t kMax = 32;
93 std::vector<std::pair<Matrix<T>, std::vector<T>>> entries;
96 static DtmcSolveMemo& instance() {
97 static DtmcSolveMemo m;
107 const std::size_t n = P.
rows();
108 if (P.
cols() != n)
throw InputError(
"dtmc_solve: transition matrix is not square");
110 detail::DtmcSolveMemo<T>& memo = detail::DtmcSolveMemo<T>::instance();
112 std::lock_guard<std::mutex> lk(memo.mu);
113 for (std::size_t k = memo.entries.size(); k-- > 0;) {
114 if (detail::dtmc_same_matrix(memo.entries[k].first, P)) {
117 return memo.entries[k].second;
124 for (std::size_t i = 0; i < n; ++i) Q(i, i) -= one;
128 std::lock_guard<std::mutex> lk(memo.mu);
129 memo.entries.emplace_back(P, pi);
130 if (memo.entries.size() > detail::DtmcSolveMemo<T>::kMax) memo.entries.erase(memo.entries.begin());
151 const std::size_t n = Q.
rows();
152 if (Q.
cols() != n)
throw InputError(
"ctmc_stochcomp: generator is not square");
154 std::vector<bool> selected(n,
false);
155 for (std::size_t k : I) {
156 if (k >= n)
throw InputError(
"ctmc_stochcomp: state index out of range");
159 std::vector<std::size_t> Ic;
160 for (std::size_t i = 0; i < n; ++i)
161 if (!selected[i]) Ic.push_back(i);
162 if (I.empty())
throw InputError(
"ctmc_stochcomp: empty state subset");
165 r.
Q11 = detail::submatrix(Q, I);
166 r.
Q22 = detail::submatrix(Q, Ic);
169 for (std::size_t a = 0; a < I.size(); ++a)
170 for (std::size_t b = 0; b < Ic.size(); ++b) r.
Q12(a, b) = Q(I[a], Ic[b]);
171 for (std::size_t a = 0; a < Ic.size(); ++a)
172 for (std::size_t b = 0; b < I.size(); ++b) r.
Q21(a, b) = Q(Ic[a], I[b]);
182 for (std::size_t i = 0; i < A.
rows(); ++i)
183 for (std::size_t j = 0; j < A.
cols(); ++j) A(i, j) = -A(i, j);
185 const std::vector<std::size_t> piv =
lu_factor(LU);
188 for (std::size_t c = 0; c < I.size(); ++c) {
189 std::vector<T> rhs(Ic.size());
190 for (std::size_t i = 0; i < Ic.size(); ++i) rhs[i] = r.
Q21(i, c);
192 for (std::size_t i = 0; i < Ic.size(); ++i) Tm(i, c) = rhs[i];
197 for (std::size_t a = 0; a < I.size(); ++a)
198 for (std::size_t c = 0; c < I.size(); ++c) {
200 for (std::size_t k = 0; k < Ic.size(); ++k) s += r.
Q12(a, k) * Tm(k, c);
204 for (std::size_t a = 0; a < I.size(); ++a)
205 for (std::size_t c = 0; c < I.size(); ++c) r.
S(a, c) = r.
Q11(a, c) + r.
T12(a, c);
212 const std::size_t half = (Q.
rows() + 1) / 2;
213 std::vector<std::size_t> I(half);
214 for (std::size_t i = 0; i < half; ++i) I[i] = i;
Steady-state distribution of a continuous-time Markov chain.
The exception types the port throws.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
std::vector< T > ctmc_solve(const Matrix< T > &Qin)
Steady-state distribution of a continuous-time Markov chain.
StochCompResult< T > ctmc_stochcomp(const Matrix< T > &Q, const std::vector< std::size_t > &I)
std::vector< T > dtmc_solve(const Matrix< T > &P)
Stationary distribution of a stochastic matrix P.
void lu_solve(const Matrix< T > &LU, const std::vector< std::size_t > &piv, std::vector< T > &b)
Solve LUx = Pb in place on b, using the factors from lu_factor.
std::vector< std::size_t > lu_factor(Matrix< T > &A)
In-place LU of A (n x n).
Number-type abstraction for the templated API port.
Matrix< T > S
stochastic complement on the selected states
Matrix< T > Q11
the four blocks, as MATLAB returns them
Matrix< T > T12
Q12 (-Q22)^-1 Q21, the correction term.