LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
ctmc_relsolve.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_CTMC_RELSOLVE_H
6#define LINE_API_MC_CTMC_RELSOLVE_H
7
8/**
9 * @file
10 * @ingroup api_mc
11 * Equilibrium distribution relative to a reference state.
12 *
13 * Templated port of matlab/lib/kpctoolbox/mc/ctmc_relsolve.m. The balance
14 * equations are closed with p(refstate) = 1 instead of sum(p) = 1, so the
15 * result is NOT a probability vector: it is the stationary measure scaled so
16 * that the reference state carries weight one. Dividing by its sum recovers
17 * ctmc_solve. Keeping the unnormalized form is what makes the ratios usable
18 * when the normalizing constant itself overflows.
19 *
20 * On a reducible generator the reference-state closure is meaningless across
21 * components, so the reducible case falls back to solving each weakly connected
22 * component and renormalizing globally, exactly as MATLAB does.
23 */
24
25#include <cstddef>
26#include <vector>
27
29#include "line/num/number.h"
30#include "line/util/error.h"
31#include "line/util/lu.h"
32#include "line/util/matrix.h"
33
34namespace line {
35namespace mc {
36
37/** Stationary measure scaled so that entry refstate equals one. */
38template <class T>
39std::vector<T> ctmc_relsolve(const Matrix<T>& Qin, std::size_t refstate) {
40 const std::size_t n = Qin.rows();
41 if (Qin.cols() != n) throw InputError("ctmc_relsolve: Q is not square");
42 if (n == 0) throw InputError("ctmc_relsolve: Q is empty");
43 if (refstate >= n) throw InputError("ctmc_relsolve: refstate out of range");
44 const T zero = num_traits<T>::from_int(0);
45 if (n == 1) return std::vector<T>(1, num_traits<T>::from_int(1));
46 const Matrix<T> Q = ctmc_makeinfgen(Qin);
47 const std::vector<std::vector<std::size_t>> comps = detail::weak_components(Q);
48 if (comps.size() > 1) {
49 std::vector<T> p(n, zero);
50 for (std::size_t c = 0; c < comps.size(); ++c) {
51 const Matrix<T> Qc = ctmc_makeinfgen(detail::submatrix(Q, comps[c]));
52 const std::vector<T> pc = ctmc_solve(Qc);
53 for (std::size_t k = 0; k < comps[c].size(); ++k) p[comps[c][k]] = pc[k];
54 }
55 T s = zero;
56 for (std::size_t i = 0; i < n; ++i) s += p[i];
57 for (std::size_t i = 0; i < n; ++i) p[i] = p[i] / s;
58 return p;
59 }
60 bool allzero = true;
61 for (std::size_t i = 0; i < n && allzero; ++i)
62 for (std::size_t j = 0; j < n; ++j)
63 if (Q(i, j) != zero) {
64 allzero = false;
65 break;
66 }
67 if (allzero)
68 return std::vector<T>(n, num_traits<T>::from_int(1) /
69 num_traits<T>::from_int(static_cast<int>(n)));
70 // The last balance equation is redundant, so it is replaced by p(refstate) = 1.
71 Matrix<T> A(n, n, zero);
72 for (std::size_t i = 0; i < n; ++i)
73 for (std::size_t j = 0; j + 1 < n; ++j) A(j, i) = Q(i, j);
74 A(n - 1, refstate) = num_traits<T>::from_int(1);
75 std::vector<T> b(n, zero);
76 b[n - 1] = num_traits<T>::from_int(1);
77 return solve(A, b);
78}
79
80/** Reference state 1 in the MATLAB numbering, i.e. index 0 here. */
81template <class T>
82std::vector<T> ctmc_relsolve(const Matrix<T>& Q) {
83 return ctmc_relsolve(Q, static_cast<std::size_t>(0));
84}
85
86} // namespace mc
87} // namespace line
88
89#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.
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
std::vector< T > ctmc_solve(const Matrix< T > &Qin)
Steady-state distribution of a continuous-time Markov chain.
Definition ctmc_solve.h:122
std::vector< T > ctmc_relsolve(const Matrix< T > &Qin, std::size_t refstate)
Stationary measure scaled so that entry refstate equals one.
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.