5#ifndef LINE_API_MC_DTMC_SOLVE_REDUCIBLE_H
6#define LINE_API_MC_DTMC_SOLVE_REDUCIBLE_H
64 std::vector<std::size_t>
scc;
79 const std::size_t m = Pl.
rows();
83 std::vector<std::size_t>
tr, rec;
84 for (std::size_t i = 0; i < m; ++i) (isrec[i] ? rec :
tr).push_back(i);
87 for (std::size_t i : rec) PI(i, i) = one;
88 if (
tr.empty())
return PI;
91 "dtmc_solve_reducible: the chain has no recurrent component, so no limiting "
92 "distribution exists");
97 std::vector<std::vector<std::size_t>> succ(m);
98 for (std::size_t i :
tr)
99 for (std::size_t j = 0; j < m; ++j)
100 if (j != i && Pl(i, j) != zero) succ[i].push_back(j);
102 std::vector<char> mark(m, 0);
103 std::vector<std::size_t> order;
104 order.reserve(
tr.size());
105 for (std::size_t root :
tr) {
106 if (mark[root])
continue;
107 std::vector<std::pair<std::size_t, std::size_t>> stack(1, std::make_pair(root, std::size_t(0)));
109 while (!stack.empty()) {
110 const std::size_t v = stack.back().first;
111 std::size_t& k = stack.back().second;
112 if (k < succ[v].size()) {
113 const std::size_t w = succ[v][k++];
114 if (!isrec[w] && mark[w] == 1)
115 throw NumericError(
"dtmc_solve_reducible: the lumped transient chain is not acyclic");
116 if (!isrec[w] && !mark[w]) {
118 stack.push_back(std::make_pair(w, std::size_t(0)));
127 for (std::size_t v : order) {
128 const T d = one - Pl(v, v);
130 throw NumericError(
"dtmc_solve_reducible: a transient component reaches no recurrent one");
131 for (std::size_t c : rec) {
133 for (std::size_t w : succ[v]) {
134 const T pw = isrec[w] ? (w == c ? one : zero) : PI(w, c);
135 if (pw != zero) acc += Pl(v, w) * pw;
137 PI(v, c) = T(acc / d);
156 double zeroColTol = 1e-12) {
157 const std::size_t N = P.
rows();
158 if (P.
cols() != N)
throw InputError(
"dtmc_solve_reducible: transition matrix is not square");
159 if (!pin.empty() && pin.size() != N)
160 throw InputError(
"dtmc_solve_reducible: initial vector has the wrong length");
165 const std::size_t numSCC = s.
numSCC();
174 for (std::size_t j = 0; j < N; ++j) r.
pis(0, j) = r.
pi[j];
184 for (std::size_t i = 0; i < numSCC; ++i)
185 for (std::size_t j = 0; j < numSCC; ++j) {
186 if (i == j)
continue;
188 for (std::size_t a : s.
members[i])
189 for (std::size_t b : s.
members[j]) acc += P(a, b);
193 for (std::size_t i = 0; i < numSCC; ++i)
195 for (std::size_t j = 0; j < numSCC; ++j) Pl(i, j) = zero;
201 std::vector<T> pinl(numSCC, zero);
204 for (std::size_t i = 0; i < numSCC; ++i) pinl[i] = one;
205 for (std::size_t j = 0; j < N; ++j) {
207 for (std::size_t i = 0; i < N; ++i) cs += P(i, j);
208 if (cs < tol) pinl[s.
scc[j] - 1] = zero;
211 for (
const T& v : pinl) tot += v;
214 for (std::size_t i = 0; i < numSCC; ++i)
217 for (T& v : pinl) v /= tot;
220 for (std::size_t i = 0; i < numSCC; ++i) {
222 for (std::size_t a : s.
members[i]) acc += pin[a];
235 std::vector<std::vector<T>> within(numSCC);
236 for (std::size_t j = 0; j < numSCC; ++j)
242 r.
pi.assign(N, zero);
243 for (std::size_t i = 0; i < numSCC; ++i) {
245 for (std::size_t j = 0; j < numSCC; ++j) r.
pil(i, j) = PI(i, j);
246 for (std::size_t j = 0; j < numSCC; ++j) {
247 if (r.
pil(i, j) == zero)
continue;
248 for (std::size_t k = 0; k < s.
members[j].size(); ++k)
253 if (!(pinl[i] > zero))
continue;
254 for (std::size_t k = 0; k < N; ++k) r.
pi[k] += r.
pis(i, k) * pinl[i];
259 std::size_t nTrans = 0, transIdx = 0;
260 for (std::size_t i = 0; i < numSCC; ++i)
265 if (nTrans == 1 && pin.empty())
266 for (std::size_t k = 0; k < N; ++k) r.
pi[k] = r.
pis(transIdx, k);
NumericError(const std::string &what)
Normalize a non-negative matrix into a stochastic transition matrix.
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
The exception types the port throws.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
ReducibleResult< T > dtmc_solve_reducible(const Matrix< T > &P, const std::vector< T > &pin, double zeroColTol=1e-12)
Limiting distribution of a discrete-time Markov chain whose transition matrix may be reducible.
Matrix< T > dtmc_makestochastic(const Matrix< T > &Pin)
Normalize a non-negative matrix into a stochastic transition matrix.
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_solve(const Matrix< T > &P)
Stationary distribution of a stochastic matrix P.
Conservation laws of a layered queueing network, enumerated from its structure.
Number-type abstraction for the templated API port.
Strongly connected components of a directed graph, and which of them are recurrent (closed under the ...
Matrix< T > pil
numSCC x numSCC, limiting vector of the lumped chain
Matrix< T > pis
numSCC x N, limiting vector per starting component
std::vector< T > pi
limiting distribution, length N
Matrix< T > pi0
numSCC x numSCC, the lumped starting vectors (empty if irreducible)
std::vector< bool > isrec
recurrence flag per component
Matrix< T > Pl
lumped chain
std::vector< std::size_t > scc
component index of each state, 1-based
std::size_t numSCC() const
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.