LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
dtmc_transient.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_MC_DTMC_TRANSIENT_H
6#define LINE_API_MC_DTMC_TRANSIENT_H
7
8/**
9 * @file
10 * @ingroup api_mc
11 * Discrete-time transient distributions, hitting times and uniformization.
12 *
13 * Templated port of matlab/src/api/mc/dtmc_transient.m,
14 * matlab/src/api/mc/dtmc_hitting_time.m and
15 * matlab/lib/kpctoolbox/mc/dtmc_uniformization.m.
16 *
17 * dtmc_transient returns the WHOLE trajectory, steps+1 rows with row 0 the
18 * initial law, not just the distribution at the last step.
19 *
20 * dtmc_hitting_time solves (I - P_TT) h = 1 on the non-target states only. A
21 * state that cannot reach the target set makes that system singular, and the
22 * right answer there is an INFINITE hitting time, not the least-squares
23 * solution of the singular system: the code detects the singularity and reports
24 * infinity for the unreachable block rather than a finite fabricated number.
25 */
26
27#include <cmath>
28#include <cstddef>
29#include <limits>
30#include <set>
31#include <vector>
32
35#include "line/num/number.h"
36#include "line/util/error.h"
37#include "line/util/lu.h"
38#include "line/util/matrix.h"
39
40namespace line {
41namespace mc {
42
43/** Trajectory of the law over steps transitions, row k holding pi0 P^k. */
44template <class T>
45Matrix<T> dtmc_transient(const Matrix<T>& P, const std::vector<T>& pi0, std::size_t steps) {
46 const std::size_t n = P.rows();
47 if (P.cols() != n) throw InputError("dtmc_transient: P is not square");
48 std::vector<T> pik = pi0;
49 if (pik.empty())
50 pik.assign(n, num_traits<T>::from_int(1) / num_traits<T>::from_int(static_cast<int>(n)));
51 if (pik.size() != n) throw InputError("dtmc_transient: pi0 has the wrong length");
52 Matrix<T> out(steps + 1, n, num_traits<T>::from_int(0));
53 for (std::size_t j = 0; j < n; ++j) out(0, j) = pik[j];
54 for (std::size_t k = 1; k <= steps; ++k) {
55 std::vector<T> next(n, num_traits<T>::from_int(0));
56 for (std::size_t i = 0; i < n; ++i)
57 for (std::size_t j = 0; j < n; ++j) next[j] += pik[i] * P(i, j);
58 pik = next;
59 for (std::size_t j = 0; j < n; ++j) out(k, j) = pik[j];
60 }
61 return out;
62}
63
64/** Uniform initial law and one step, matching the one-argument MATLAB call. */
65template <class T>
67 return dtmc_transient(P, std::vector<T>(), 1);
68}
69
70/** Expected number of steps to reach the target set, zero on the target set itself. */
71template <class T>
72std::vector<T> dtmc_hitting_time(const Matrix<T>& P, const std::vector<std::size_t>& target) {
74 "dtmc_hitting_time requires a backend with an infinity, since a state that "
75 "cannot reach the target set has an infinite hitting time");
76 const std::size_t n = P.rows();
77 if (P.cols() != n) throw InputError("dtmc_hitting_time: P is not square");
78 std::vector<bool> is_target(n, false);
79 for (std::size_t k = 0; k < target.size(); ++k) {
80 if (target[k] >= n) throw InputError("dtmc_hitting_time: target index out of range");
81 is_target[target[k]] = true;
82 }
83 std::vector<std::size_t> nt;
84 for (std::size_t i = 0; i < n; ++i)
85 if (!is_target[i]) nt.push_back(i);
86 std::vector<T> h(n, num_traits<T>::from_int(0));
87 if (nt.empty()) return h;
88 const std::size_t m = nt.size();
90 for (std::size_t a = 0; a < m; ++a)
91 for (std::size_t b = 0; b < m; ++b)
92 A(a, b) = (a == b ? num_traits<T>::from_int(1) : num_traits<T>::from_int(0)) -
93 P(nt[a], nt[b]);
94 std::vector<T> b(m, num_traits<T>::from_int(1));
95 // A state from which the target set is unreachable makes I - P_TT singular
96 // there, and its hitting time is infinite rather than any finite solution.
97 std::vector<bool> reaches(m, false);
98 bool changed = true;
99 while (changed) {
100 changed = false;
101 for (std::size_t a = 0; a < m; ++a) {
102 if (reaches[a]) continue;
103 for (std::size_t j = 0; j < n; ++j) {
104 if (P(nt[a], j) == num_traits<T>::from_int(0)) continue;
105 bool ok = is_target[j];
106 if (!ok)
107 for (std::size_t c = 0; c < m; ++c)
108 if (nt[c] == j && reaches[c]) ok = true;
109 if (ok) {
110 reaches[a] = true;
111 changed = true;
112 break;
113 }
114 }
115 }
116 }
117 std::vector<std::size_t> keep;
118 for (std::size_t a = 0; a < m; ++a) {
119 if (reaches[a])
120 keep.push_back(a);
121 else
122 h[nt[a]] = num_traits<T>::from_double(std::numeric_limits<double>::infinity());
123 }
124 if (keep.empty()) return h;
125 Matrix<T> Ak(keep.size(), keep.size(), num_traits<T>::from_int(0));
126 std::vector<T> bk(keep.size(), num_traits<T>::from_int(1));
127 for (std::size_t a = 0; a < keep.size(); ++a)
128 for (std::size_t c = 0; c < keep.size(); ++c) Ak(a, c) = A(keep[a], keep[c]);
129 const std::vector<T> hk = solve(Ak, bk);
130 for (std::size_t a = 0; a < keep.size(); ++a) h[nt[keep[a]]] = hk[a];
131 return h;
132}
133
134/** Transient law of a DTMC through the uniformized generator of P. */
135template <class T>
136UniformizationResult<T> dtmc_uniformization(const std::vector<T>& pi0, const Matrix<T>& P,
137 const T& t, double tol = 1e-12, long maxiter = -1) {
138 return ctmc_uniformization(pi0, ctmc_makeinfgen(P), t, tol, maxiter);
139}
140
141} // namespace mc
142} // namespace line
143
144#endif
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
Steady-state distribution of a continuous-time Markov chain.
Transient distribution of a CTMC by uniformization (Jensen's method), and the time-averaged distribut...
The exception types the port throws.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
Matrix< T > ctmc_makeinfgen(const Matrix< T > &Q)
Set the diagonal so that every row sums to zero (ctmc_makeinfgen).
Definition ctmc_solve.h:58
UniformizationResult< T > ctmc_uniformization(const std::vector< T > &pi0, const Matrix< T > &Q, const T &t, double tol=1e-12, long maxiter=-1)
Transient distribution of a CTMC by uniformization (Jensen's method), and the time-averaged distribut...
std::vector< T > dtmc_hitting_time(const Matrix< T > &P, const std::vector< std::size_t > &target)
Expected number of steps to reach the target set, zero on the target set itself.
UniformizationResult< T > dtmc_uniformization(const std::vector< T > &pi0, const Matrix< T > &P, const T &t, double tol=1e-12, long maxiter=-1)
Transient law of a DTMC through the uniformized generator of P.
Matrix< T > dtmc_transient(const Matrix< T > &P, const std::vector< T > &pi0, std::size_t steps)
Trajectory of the law over steps transitions, row k holding pi0 P^k.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Definition lu.h:158
Number-type abstraction for the templated API port.