LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
ctmc_isfeasible.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_ISFEASIBLE_H
6#define LINE_API_MC_CTMC_ISFEASIBLE_H
7
8/**
9 * @file
10 * @ingroup api_mc
11 * Feasibility predicates for generators and stochastic matrices.
12 *
13 * Templated port of matlab/src/api/mc/ctmc_isfeasible.m and
14 * matlab/lib/kpctoolbox/mc/dtmc_isfeasible.m.
15 *
16 * The two return different things and that difference is deliberate. The CTMC
17 * predicate is a BOOLEAN at a caller-supplied tolerance. The DTMC one returns a
18 * PRECISION LEVEL: the largest tol in 1..15 at which the row sums are within
19 * 10^-tol of one and the entries are above -10^-tol, and 0 when no level holds.
20 * A caller that reads the DTMC result as a boolean is right by accident, since
21 * any nonzero level is truthy, but a caller that compares it to 1 is wrong.
22 */
23
24#include <cstddef>
25
26#include "line/num/number.h"
27#include "line/util/error.h"
28#include "line/util/matrix.h"
29
30namespace line {
31namespace mc {
32
33/** True when Q is square, has nonnegative off-diagonals, nonpositive diagonal and zero row sums. */
34template <class T>
35bool ctmc_isfeasible(const Matrix<T>& Q, const T& tol) {
36 const std::size_t n = Q.rows();
37 if (n == 0 || Q.cols() != n) return false;
38 for (std::size_t i = 0; i < n; ++i) {
39 T rowsum = num_traits<T>::from_int(0);
40 for (std::size_t j = 0; j < n; ++j) {
41 if (i != j && Q(i, j) < -tol) return false;
42 rowsum += Q(i, j);
43 }
44 if (Q(i, i) > tol) return false;
45 if (num_abs(rowsum) > tol) return false;
46 }
47 return true;
48}
49
50/** Default tolerance 1e-10, matching the MATLAB signature. */
51template <class T>
52bool ctmc_isfeasible(const Matrix<T>& Q) {
54}
55
56/** Largest precision level 1..15 at which P is stochastic, or 0 when none holds. */
57template <class T>
59 const std::size_t n = P.rows();
60 if (n == 0) return 0;
61 T minsum = num_traits<T>::from_int(0), maxsum = num_traits<T>::from_int(0);
62 T minel = P(0, 0);
63 for (std::size_t i = 0; i < n; ++i) {
65 for (std::size_t j = 0; j < P.cols(); ++j) {
66 s += P(i, j);
67 if (P(i, j) < minel) minel = P(i, j);
68 }
69 if (i == 0 || s < minsum) minsum = s;
70 if (i == 0 || s > maxsum) maxsum = s;
71 }
72 int res = 0;
73 const T one = num_traits<T>::from_int(1);
74 T eps = num_traits<T>::from_int(1);
75 const T ten = num_traits<T>::from_int(10);
76 for (int tol = 1; tol <= 15; ++tol) {
77 eps = eps / ten;
78 if (minsum > one - eps && maxsum < one + eps && minel > -eps) res = tol;
79 }
80 return res;
81}
82
83} // namespace mc
84} // namespace line
85
86#endif
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
The exception types the port throws.
Dense matrix and non-owning view.
int dtmc_isfeasible(const Matrix< T > &P)
Largest precision level 1..15 at which P is stochastic, or 0 when none holds.
bool ctmc_isfeasible(const Matrix< T > &Q, const T &tol)
True when Q is square, has nonnegative off-diagonals, nonpositive diagonal and zero row sums.
T num_abs(const T &v)
Definition number.h:172
Number-type abstraction for the templated API port.