5#ifndef LINE_API_MAM_MFQ_LD_DISTR_H
6#define LINE_API_MAM_MFQ_LD_DISTR_H
97namespace mfq_ld_detail {
108std::vector<T> crp_solve(
const Matrix<T>& M) {
109 const std::size_t n = M.
rows();
110 if (M.
cols() != n)
throw InputError(
"crp_solve: matrix is not square");
115 for (std::size_t i = 0; i < n; ++i)
116 for (std::size_t j = 0; j < n; ++j) A(j, i) = (j == 0) ? one : M(i, j);
117 std::vector<T> rhs(n, zero);
119 return solve(A, rhs);
124double min_abs_eig(
const Matrix<T>& M) {
125 const std::size_t n = M.rows();
126 Matrix<double> Md(n, n, 0.0);
127 for (std::size_t i = 0; i < n; ++i)
128 for (std::size_t j = 0; j < n; ++j) Md(i, j) = num_traits<T>::to_double(M(i, j));
129 const std::vector<std::complex<double>> ev =
eig_values(Md);
130 double best = std::abs(ev[0]);
131 for (
const std::complex<double>& v : ev) {
132 const double a = std::abs(v);
133 if (a < best) best = a;
145Matrix<T> integ_exp_singular(
const Matrix<T>& M,
const T& L) {
147 const std::size_t n = M.rows();
148 const T one = num_traits<T>::from_int(1);
150 for (std::size_t i = 0; i < n; ++i)
151 for (std::size_t j = 0; j < n; ++j) Mt(i, j) = M(j, i);
152 std::vector<T> l = crp_solve(M);
153 std::vector<T> r = crp_solve(Mt);
154 T lr = num_traits<T>::from_int(0);
155 for (std::size_t i = 0; i < n; ++i) lr += l[i] * r[i];
156 if (lr == num_traits<T>::from_int(0))
157 throw NumericError(
"mfq_ld_distr: the null vectors of a regime exponent are orthogonal");
158 for (T& v : l) v /= lr;
161 for (std::size_t i = 0; i < n; ++i)
162 for (std::size_t j = 0; j < n; ++j) rl(i, j) = r[i] * l[j];
164 for (std::size_t i = 0; i < n; ++i)
165 for (std::size_t j = 0; j < n; ++j) S(i, j) -= rl(i, j);
167 Matrix<T> negS(n, n);
168 for (std::size_t i = 0; i < n; ++i)
169 for (std::size_t j = 0; j < n; ++j) negS(i, j) = -S(i, j);
170 const Matrix<T> E =
expm(S, L);
171 Matrix<T> ImE =
eye<T>(n);
172 for (std::size_t i = 0; i < n; ++i)
173 for (std::size_t j = 0; j < n; ++j) ImE(i, j) -= E(i, j);
175 const T w = L + exp(-L) - one;
176 for (std::size_t i = 0; i < n; ++i)
177 for (std::size_t j = 0; j < n; ++j) out(i, j) += rl(i, j) * w;
183Matrix<T> integ_exp_regular(
const Matrix<T>& M,
const T& L) {
184 const std::size_t n = M.rows();
185 Matrix<T> negM(n, n);
186 for (std::size_t i = 0; i < n; ++i)
187 for (std::size_t j = 0; j < n; ++j) negM(i, j) = -M(i, j);
188 const Matrix<T> E =
expm(M, L);
189 Matrix<T> ImE =
eye<T>(n);
190 for (std::size_t i = 0; i < n; ++i)
191 for (std::size_t j = 0; j < n; ++j) ImE(i, j) -= E(i, j);
201void integ_exp_pair(
const Matrix<T>& KA,
const Matrix<T>& KB,
const T& L, Matrix<T>& JA,
203 if (min_abs_eig(KA) > min_abs_eig(KB)) {
204 JA = integ_exp_regular(KA, L);
205 JB = integ_exp_singular(KB, L);
207 JA = integ_exp_singular(KA, L);
208 JB = integ_exp_regular(KB, L);
226 "mfq_ld_distr evaluates matrix exponentials");
227 using namespace mfq_ld_detail;
229 const std::size_t K = b.
Thr.size();
230 if (K == 0)
throw InputError(
"mfq_ld_distr: at least one regime is required");
231 if (b.
masses.size() != K + 1)
232 throw InputError(
"mfq_ld_distr: expected K+1 point-mass vectors");
233 if (b.
iniF.size() != K || b.
KF.size() != K || b.
cloF.size() != K || b.
iniB.size() != K ||
234 b.
KB.size() != K || b.
cloB.size() != K)
235 throw InputError(
"mfq_ld_distr: expected K forward and K backward blocks");
236 const std::size_t N = b.
masses[0].size();
238 std::vector<T> Tv(K + 1, zero);
239 for (std::size_t k = 0; k < K; ++k) Tv[k + 1] = b.
Thr[k];
242 std::vector<std::vector<T>> res;
243 res.reserve(points.size());
244 for (
const T& p : points) {
245 if (p < zero)
throw InputError(
"mfq_ld_distr: the evaluation points must be non-negative");
246 std::vector<T> pres(N, zero);
249 while (k < K && p >= Tv[k]) {
253 integ_exp_pair(b.
KF[k - 1], b.
KB[k - 1], T(Tv[k] - Tv[k - 1]), sF, sB);
254 const std::vector<T> vF =
256 const std::vector<T> vB =
258 for (std::size_t j = 0; j < N; ++j) pres[j] += vF[j] + vB[j];
262 for (std::size_t j = 0; j < N; ++j) pres[j] += b.
masses[k][j];
267 for (std::size_t j = 0; j < N; ++j) pres[j] += b.
masses[K][j];
269 const std::size_t kk = k - 1;
270 const T prem = p - Tv[kk];
271 const T Tk = Tv[kk + 1] - Tv[kk];
274 const std::vector<T> vF =
276 const std::vector<T> vB =
278 for (std::size_t j = 0; j < N; ++j) pres[j] = vF[j] + vB[j];
280 const std::vector<T> vF =
vecmul(
282 const std::vector<T> vB =
285 for (std::size_t j = 0; j < N; ++j) pres[j] = vF[j] - vB[j];
288 integ_exp_pair(b.
KF[kk], b.
KB[kk], prem, sF, sB);
290 const std::vector<T> vB =
vecmul(
292 for (std::size_t j = 0; j < N; ++j) pres[j] += vF[j] + vB[j];
NumericError(const std::string &what)
Eigenvalues and singular values, backed by LAPACK.
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
Stationary mean fluid level E[X] of a first- or second-order level-dependent (multi-regime) Markovian...
std::vector< std::vector< T > > mfq_ld_distr(const LevelDependentFluidBlocks< T > &b, FluidDistrKind what, const std::vector< T > &points)
Stationary density or distribution of a level-dependent fluid queue.
FluidDistrKind
Which functional of the stationary level law to evaluate.
@ Pdfd
derivative of the density
@ Cdf
P(X < p), excluding an atom sitting exactly at p.
@ Cdfm
P(X <= p), including it.
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
std::vector< std::complex< double > > eig_values(const Matrix< double > &A)
Eigenvalues of a general real square matrix, in LAPACK's order.
Matrix< T > eye(std::size_t n)
Identity of order n.
Matrix< T > expm(const Matrix< T > &A)
Matrix exponential exp(A).
Number-type abstraction for the templated API port.
The matrix-exponential building blocks of a multi-regime fluid queue, the output of mfq_ld_solve.
std::vector< std::vector< T > > masses
K+1 point-mass vectors of length N.
std::vector< T > Thr
K regime thresholds T(1)..T(K).
std::vector< Matrix< T > > KF
K forward matrix exponents.
std::vector< std::vector< T > > iniB
K backward initial vectors.
std::vector< Matrix< T > > KB
K backward matrix exponents.
std::vector< std::vector< T > > iniF
K forward initial vectors.
std::vector< Matrix< T > > cloF
K forward closing matrices.
std::vector< Matrix< T > > cloB
K backward closing matrices.