LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
ctmc_stationary.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_SOLVERS_CTMC_CTMC_STATIONARY_H
6
#define LINE_SOLVERS_CTMC_CTMC_STATIONARY_H
7
8
/**
9
* @file
10
* @ingroup line_solvers
11
* Port of `matlab/src/solvers/CTMC/ctmc_stationary.m`: the single entry point
12
* for the stationary distribution of a CTMC generated from a NetworkStruct.
13
*
14
* All the stationary mass of a reducible chain lives in its bottom strongly
15
* connected components, each weighted by the probability of being absorbed in
16
* it from the declared initial state; every other state is transient and
17
* carries zero. The block decomposition handles the irreducible case as the
18
* degenerate one BSCC / no transient states, so every solve goes through it and
19
* no dispatch can disagree with the algorithm about whether a chain is
20
* reducible.
21
*
22
* The reference's local `ctmc_initial_distribution` turns (StateSpace, sn) into
23
* the row of the initial state; here that job already belongs to
24
* `analyzer_detail::init_state_index`, so this file takes the INDEX and stays
25
* free of any `sn` dependency. `npos` means the initial state is absent from
26
* the enumerated space -- stochastic complementation may have removed it, an
27
* SPN whose immediate ENABLE states were eliminated being the usual case -- and
28
* makes the block decomposition start in the SCCs with no incoming transition.
29
*/
30
31
#include <cstddef>
32
#include <string>
33
#include <vector>
34
35
#include "
line/api/mc/ctmc_solve_reducible_blkdecomp.h
"
36
#include "
line/api/mc/stronglyconncomp.h
"
37
#include "
line/util/error.h
"
38
#include "
line/util/matrix.h
"
39
#include "
line/num/number.h
"
40
41
namespace
line
{
42
namespace
ctmc
{
43
44
/** Magnitude above which an off-diagonal generator entry counts as an arc. */
45
static
const
double
kArcTol
= 1e-12;
46
47
template
<
class
T>
48
struct
CtmcStationaryResult
{
49
std::vector<T>
pi
;
///< stationary distribution, length N
50
bool
seeded
=
false
;
///< true when a declared initial state selected the answer
51
std::size_t
nbscc
= 0;
///< number of closed communicating classes
52
/**
53
* Empty unless the chain is an UNSEEDED reducible mixture. The library
54
* never writes to stderr (see `util/error.h`), so the caller decides
55
* whether to surface it; the CLI prints it.
56
*/
57
std::string
warning
;
58
};
59
60
namespace
stationary_detail {
61
62
/**
63
* Port of the reference's `warn_if_unseeded_mixture`.
64
*
65
* Without a seed the block decomposition invents a start distribution -- here a
66
* uniform one over the SCCs with no incoming transition -- and no property of
67
* the model implies it: on a reducible chain the stationary distribution is
68
* fixed only by the initial state. Nor is it the product-form weighting, which
69
* weights the recurrent classes by their unnormalized Kelly mass. The invented
70
* start is order-independent and therefore looks more reproducible than the
71
* answer the declared initial state selects, but that reproducibility is bought
72
* by discarding the one input that makes the problem well posed.
73
*
74
* The fallback itself differs across codebases (python weights ALL the SCCs
75
* equally, MATLAB and this port the source SCCs), which is a second reason not
76
* to read the number as the model's answer.
77
*/
78
template
<
class
T>
79
std::size_t count_bscc(
const
Matrix<T>
& Q) {
80
const
std::size_t n = Q.
rows
();
81
if
(n < 2)
return
0;
82
const
T zero =
num_traits<T>::from_int
(0);
83
// SIGN IS NOT A CRITERION: an ME generator embeds genuinely negative
84
// off-diagonal entries, so the adjacency is taken on the MAGNITUDE.
85
Matrix<T>
A(n, n, zero);
86
for
(std::size_t i = 0; i < n; ++i)
87
for
(std::size_t j = 0; j < n; ++j) {
88
if
(i == j)
continue
;
89
const
double
a =
num_traits<T>::to_double
(Q(i, j));
90
if
((a < 0 ? -a : a) >
kArcTol
) A(i, j) =
num_traits<T>::from_int
(1);
91
}
92
const
mc::SccResult
s =
mc::stronglyconncomp
(A);
93
std::size_t nbscc = 0;
94
for
(std::size_t c = 0; c < s.
recurrent
.size(); ++c)
95
if
(s.
recurrent
[c]) ++nbscc;
96
return
nbscc;
97
}
98
99
}
// namespace stationary_detail
100
101
/**
102
* @param Q generator; the diagonal is recomputed by the block decomposition
103
* @param init_index row of the declared initial state, or npos when absent
104
*/
105
template
<
class
T>
106
CtmcStationaryResult<T>
ctmc_stationary
(
const
Matrix<T>
& Q,
107
std::size_t init_index =
static_cast<
std::size_t
>
(-1)) {
108
const
std::size_t npos =
static_cast<
std::size_t
>
(-1);
109
const
std::size_t n = Q.
rows
();
110
if
(Q.
cols
() != n)
throw
InputError
(
"ctmc_stationary: generator is not square"
);
111
112
CtmcStationaryResult<T>
out;
113
std::vector<T> pi0;
114
if
(init_index != npos) {
115
if
(init_index >= n)
throw
InputError
(
"ctmc_stationary: initial state index out of range"
);
116
pi0.assign(n,
num_traits<T>::from_int
(0));
117
pi0[init_index] =
num_traits<T>::from_int
(1);
118
out.
seeded
=
true
;
119
}
120
121
out.
nbscc
= stationary_detail::count_bscc(Q);
122
if
(!out.
seeded
&& out.
nbscc
> 1) {
123
out.
warning
=
124
"SolverCTMC: the generator has "
+ std::to_string(out.
nbscc
) +
125
" closed communicating classes and the declared initial state could not be located in "
126
"the enumerated state space, so the solve starts from a distribution the model never "
127
"stated (uniform over the SCCs with no incoming transition). On a reducible chain the "
128
"stationary distribution is determined only by the initial state, so this answer is "
129
"not the model's. Call setState on the stations so the class the model actually starts "
130
"in is the one solved."
;
131
}
132
133
out.
pi
=
mc::ctmc_solve_reducible_blkdecomp
(Q, pi0).pi;
134
return
out;
135
}
136
137
}
// namespace ctmc
138
}
// namespace line
139
140
#endif
// LINE_SOLVERS_CTMC_CTMC_STATIONARY_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::Matrix
Definition
matrix.h:56
line::Matrix::cols
std::size_t cols() const
Definition
matrix.h:90
line::Matrix::rows
std::size_t rows() const
Definition
matrix.h:89
ctmc_solve_reducible_blkdecomp.h
Limiting distribution of a reducible CTMC by direct block decomposition of the generator.
error.h
The exception types the port throws.
matrix.h
Dense matrix and non-owning view.
line::ctmc
Definition
ctmc_stationary.h:42
line::ctmc::ctmc_stationary
CtmcStationaryResult< T > ctmc_stationary(const Matrix< T > &Q, std::size_t init_index=static_cast< std::size_t >(-1))
Definition
ctmc_stationary.h:106
line::ctmc::kArcTol
static const double kArcTol
Magnitude above which an off-diagonal generator entry counts as an arc.
Definition
ctmc_stationary.h:45
line::mc::ctmc_solve_reducible_blkdecomp
BlkDecompResult< T > ctmc_solve_reducible_blkdecomp(const Matrix< T > &Qin, const std::vector< T > &pin, double reachTol=1e-15, double zeroColTol=1e-12)
Limiting distribution of a reducible CTMC by direct block decomposition of the generator.
Definition
ctmc_solve_reducible_blkdecomp.h:70
line::mc::stronglyconncomp
SccResult stronglyconncomp(const Matrix< T > &A)
Strongly connected components of a directed graph, and which of them are recurrent (closed under the ...
Definition
stronglyconncomp.h:63
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
stronglyconncomp.h
Strongly connected components of a directed graph, and which of them are recurrent (closed under the ...
line::ctmc::CtmcStationaryResult
Definition
ctmc_stationary.h:48
line::ctmc::CtmcStationaryResult::pi
std::vector< T > pi
stationary distribution, length N
Definition
ctmc_stationary.h:49
line::ctmc::CtmcStationaryResult::seeded
bool seeded
true when a declared initial state selected the answer
Definition
ctmc_stationary.h:50
line::ctmc::CtmcStationaryResult::warning
std::string warning
Empty unless the chain is an UNSEEDED reducible mixture.
Definition
ctmc_stationary.h:57
line::ctmc::CtmcStationaryResult::nbscc
std::size_t nbscc
number of closed communicating classes
Definition
ctmc_stationary.h:51
line::mc::SccResult
Definition
stronglyconncomp.h:46
line::mc::SccResult::recurrent
std::vector< bool > recurrent
recurrent[c-1] is true when component c has no edge leaving it.
Definition
stronglyconncomp.h:50
line::num_traits
Definition
number.h:111
include
line
solvers
ctmc
ctmc_stationary.h
Generated by
1.18.0