LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
dtmc_stochcomp.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_STOCHCOMP_H
6#define LINE_API_MC_DTMC_STOCHCOMP_H
7
8/**
9 * @file
10 * @ingroup api_mc
11 * Stochastic complement of a DTMC partition, a port of
12 * matlab/lib/kpctoolbox/mc/dtmc_stochcomp.m.
13 *
14 * For a row-stochastic P partitioned into a retained set I and its complement
15 * Ic, the stochastic complement over I is
16 * S = P11 + P12 (Id - P22)^-1 P21,
17 * the routing seen by an observer who watches only the states in I, censoring
18 * the excursions through Ic. It is the same construction `NetworkStruct`'s
19 * `station_routing` performs over the stateful nodes, exposed here as a free
20 * function so the cacheqn driver can complement a rtnodes matrix it has
21 * rewritten in place (relabelling a Cache as a class switch) without going back
22 * through `route_eff`.
23 *
24 * ARITHMETIC: field. The only operation is the linear solve (Id - P22) X = P21
25 * by Gaussian elimination with partial pivoting, so it is exact under Rational.
26 */
27
28#include <cmath>
29#include <cstddef>
30#include <vector>
31
32#include "line/num/number.h"
33#include "line/util/error.h"
34#include "line/util/matrix.h"
35
36namespace line {
37namespace mc {
38
39/**
40 * @brief Stochastic complement of a DTMC partition, a port of
41 * matlab/lib/kpctoolbox/mc/dtmc_stochcomp.m.
42 *
43 * @param P (n x n) row-stochastic transition matrix
44 * @param keep the 0-based indices of the states to retain (the set I)
45 * @return the (|I| x |I|) stochastic complement S over the retained states, in
46 * the order given by `keep`
47 */
48template <class T>
49Matrix<T> dtmc_stochcomp(const Matrix<T>& P, const std::vector<std::size_t>& keep) {
50 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
51 const std::size_t n = P.rows();
52 if (P.cols() != n) throw InputError("dtmc_stochcomp: the matrix is not square");
53
54 std::vector<bool> kept(n, false);
55 for (std::size_t i : keep) {
56 if (i >= n) throw InputError("dtmc_stochcomp: a retained index is out of range");
57 kept[i] = true;
58 }
59 std::vector<std::size_t> drop;
60 for (std::size_t i = 0; i < n; ++i)
61 if (!kept[i]) drop.push_back(i);
62
63 const std::size_t nk = keep.size(), nd = drop.size();
64 Matrix<T> P11(nk, nk, zero);
65 for (std::size_t a = 0; a < nk; ++a)
66 for (std::size_t b = 0; b < nk; ++b) P11(a, b) = P(keep[a], keep[b]);
67 if (nd == 0) return P11;
68
69 Matrix<T> P12(nk, nd, zero), P21(nd, nk, zero), A(nd, nd, zero);
70 for (std::size_t a = 0; a < nk; ++a)
71 for (std::size_t b = 0; b < nd; ++b) P12(a, b) = P(keep[a], drop[b]);
72 for (std::size_t a = 0; a < nd; ++a) {
73 for (std::size_t b = 0; b < nk; ++b) P21(a, b) = P(drop[a], keep[b]);
74 for (std::size_t b = 0; b < nd; ++b) A(a, b) = T((a == b ? one : zero) - P(drop[a], drop[b]));
75 }
76
77 // X = (Id - P22)^-1 P21 by Gaussian elimination with partial pivoting.
78 Matrix<T> X = P21;
79 for (std::size_t col = 0; col < nd; ++col) {
80 std::size_t best = col;
81 double bv = std::fabs(num_traits<T>::to_double(A(col, col)));
82 for (std::size_t r = col + 1; r < nd; ++r) {
83 const double v = std::fabs(num_traits<T>::to_double(A(r, col)));
84 if (v > bv) { bv = v; best = r; }
85 }
86 if (best != col) {
87 for (std::size_t b = 0; b < nd; ++b) std::swap(A(col, b), A(best, b));
88 for (std::size_t b = 0; b < nk; ++b) std::swap(X(col, b), X(best, b));
89 }
90 if (A(col, col) == zero)
91 throw NumericError("dtmc_stochcomp: the complement block is singular");
92 for (std::size_t r = 0; r < nd; ++r) {
93 if (r == col) continue;
94 const T f = T(A(r, col) / A(col, col));
95 if (f == zero) continue;
96 for (std::size_t b = 0; b < nd; ++b) A(r, b) = T(A(r, b) - f * A(col, b));
97 for (std::size_t b = 0; b < nk; ++b) X(r, b) = T(X(r, b) - f * X(col, b));
98 }
99 }
100 for (std::size_t r = 0; r < nd; ++r)
101 for (std::size_t b = 0; b < nk; ++b) X(r, b) = T(X(r, b) / A(r, r));
102
103 Matrix<T> S = P11;
104 for (std::size_t a = 0; a < nk; ++a)
105 for (std::size_t b = 0; b < nk; ++b) {
106 T acc = zero;
107 for (std::size_t d = 0; d < nd; ++d) acc = T(acc + P12(a, d) * X(d, b));
108 S(a, b) = T(S(a, b) + acc);
109 }
110 return S;
111}
112
113} // namespace mc
114} // namespace line
115
116#endif // LINE_API_MC_DTMC_STOCHCOMP_H
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
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Dense matrix and non-owning view.
Matrix< T > dtmc_stochcomp(const Matrix< T > &P, const std::vector< std::size_t > &keep)
Stochastic complement of a DTMC partition, a port of matlab/lib/kpctoolbox/mc/dtmc_stochcomp....
Number-type abstraction for the templated API port.