LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
mfq_sojourn.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_MAM_MFQ_SOJOURN_H
6#define LINE_API_MAM_MFQ_SOJOURN_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * Sojourn-time distribution of a Markov-modulated fluid queue, as a
12 * matrix-exponential or phase-type representation (alpha, A).
13 *
14 * Port of matlab/src/api/mam/mfq_sojourn.m and the BUTools FluidQueueSTD it
15 * wraps. A background chain with generator Q modulates an input fluid rate
16 * matrix Rin and an output (service) rate matrix Rout, both diagonal. The
17 * quantity returned is the law of the time a fluid DROP spends in the queue.
18 *
19 * CONSTRUCTION. The net drift is Rin - Rout, so mfq_general_solve returns the
20 * stationary level law as a point mass mass0 at zero plus ini exp(K x) clo. The
21 * arrival rate of fluid is
22 *
23 * lambda = sum( mass0 Rin + ini (-K)^-1 clo Rin ),
24 *
25 * the mass at zero and the density integrated over the level each weighted by
26 * the input rate. A drop that arrives when the level is x and the background
27 * state is j leaves after the queue has drained x at the state-dependent rate,
28 * so its sojourn time is the first passage of a level-dependent process whose
29 * generator on the product space (background state, K-phase) is
30 *
31 * A = B ( kron(Q', I) + kron(Rout, K) ) B^-1 (ME form)
32 * A = kron(Rout, Delta^-1 K' Delta) + kron(Q, I) (PH form)
33 *
34 * of order N * Np, with N the background order and Np the number of up-drift
35 * states. The two forms are the same operator in two different bases: the ME
36 * form uses the similarity that maps the closing vector to a vector of ones,
37 * the PH form the diagonal similarity Delta = diag(ini (-K)^-1)/lambda, which
38 * makes the representation a genuine phase type (non-negative generator, so it
39 * can be sampled and fed to any PH consumer) at the cost of requiring that
40 * diagonal to be positive.
41 *
42 * WHICH FORM TO ASK FOR. The ME form always exists; the PH form exists only
43 * when Delta is invertible, and neither the reference nor this port can promise
44 * that in advance. Ask for PH when the result must be sampled or handed to a
45 * PH-only routine, ME otherwise. The two give the SAME distribution and the
46 * tests assert exactly that, by comparing their first three moments and their
47 * CDFs -- which is a real check, since the two are computed through disjoint
48 * code paths.
49 *
50 * ARITHMETIC. Gated on num_traits<T>::has_transcendental through
51 * mfq_general_solve, whose Riccati iteration terminates on a tolerance.
52 * Everything on top of it is finite exact linear algebra.
53 */
54
55#include <cstddef>
56#include <vector>
57
60#include "line/num/number.h"
61#include "line/util/error.h"
62#include "line/util/linalg.h"
63#include "line/util/matrix.h"
64
65namespace line {
66namespace mam {
67
68/** A matrix-exponential or phase-type representation (alpha, A). */
69template <class T>
71 std::vector<T> alpha; ///< initial row vector
72 Matrix<T> A; ///< generator-like matrix exponent
73};
74
75/**
76 * Sojourn time of a drop in a Markov-modulated fluid queue.
77 *
78 * @param Q generator of the background chain, N x N
79 * @param Rin diagonal input fluid rate matrix, N x N
80 * @param Rout diagonal output (service) fluid rate matrix, N x N
81 * @param Q0 level-zero generator; an empty matrix means Q0 = Q
82 * @param transToPH true for a phase-type representation, false for ME
83 * @param prec tolerance handed to mfq_general_solve
84 */
85template <class T>
87 const Matrix<T>& Q0, bool transToPH, const T& prec) {
89 "mfq_sojourn requires transcendental arithmetic");
90 using namespace mfq_detail;
91 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
92 const std::size_t N = Q.rows();
93 if (Q.cols() != N || Rin.rows() != N || Rin.cols() != N || Rout.rows() != N ||
94 Rout.cols() != N)
95 throw InputError("mfq_sojourn: Q, Rin and Rout must be square and of equal order");
96
97 const Matrix<T> Rnet = sub(Rin, Rout);
98 const GeneralFluidSolution<T> s = mfq_general_solve(Q, Rnet, Q0, prec);
99 const std::size_t Np = s.ini.size();
100 if (Np == 0) throw NumericError("mfq_sojourn: the fluid model has no up-drift states");
101
102 const Matrix<T> negKinv = inverse(scale(s.K, T(-one)));
103 const std::vector<T> iniKi = vecmul(s.ini, negKinv); // ini (-K)^-1
104
105 // lambda = sum( mass0 Rin + iniKi clo Rin ).
106 T lambda = zero;
107 {
108 const std::vector<T> a = vecmul(s.mass0, Rin);
109 const std::vector<T> b = vecmul(vecmul(iniKi, s.clo), Rin);
110 for (std::size_t j = 0; j < N; ++j) lambda += a[j] + b[j];
111 }
112 if (lambda <= zero) throw NumericError("mfq_sojourn: non-positive fluid arrival rate");
113
115 const std::size_t n = N * Np;
116 if (transToPH) {
117 std::vector<T> delta(Np);
118 for (std::size_t i = 0; i < Np; ++i) {
119 delta[i] = iniKi[i] / lambda;
120 if (delta[i] == zero)
121 throw NumericError(
122 "mfq_sojourn: the PH similarity is singular on this model; ask for the ME "
123 "representation instead");
124 }
125 // alpha = reshape(clo Rin, 1, N Np) * kron(I_N, Delta), column-major:
126 // index (j, i) -> j Np + i.
127 const Matrix<T> cloRin = matmul(s.clo, Rin);
128 out.alpha.assign(n, zero);
129 for (std::size_t j = 0; j < N; ++j)
130 for (std::size_t i = 0; i < Np; ++i) out.alpha[j * Np + i] = cloRin(i, j) * delta[i];
131 // A = kron(Rout, Delta^-1 K' Delta) + kron(Q, I_Np).
132 Matrix<T> Mi(Np, Np, zero);
133 for (std::size_t i = 0; i < Np; ++i)
134 for (std::size_t k = 0; k < Np; ++k) Mi(i, k) = s.K(k, i) * delta[k] / delta[i];
135 out.A = Matrix<T>(n, n, zero);
136 for (std::size_t j1 = 0; j1 < N; ++j1)
137 for (std::size_t j2 = 0; j2 < N; ++j2)
138 for (std::size_t i1 = 0; i1 < Np; ++i1)
139 for (std::size_t i2 = 0; i2 < Np; ++i2) {
140 T v = Rout(j1, j2) * Mi(i1, i2);
141 if (i1 == i2) v += Q(j1, j2);
142 out.A(j1 * Np + i1, j2 * Np + i2) = v;
143 }
144 } else {
145 // B maps the closing vector reshape((-K)^-1 clo Rin, N Np, 1) to ones.
146 const Matrix<T> W = matmul(matmul(negKinv, s.clo), Rin); // Np x N
147 std::vector<T> clovec(n, zero);
148 for (std::size_t j = 0; j < N; ++j)
149 for (std::size_t i = 0; i < Np; ++i) clovec[j * Np + i] = W(i, j);
150 const Matrix<T> B = mfq_transform_to_ones(clovec);
151 const Matrix<T> Bi = inverse(B);
152 // alpha = kron(ones(1,N), ini/lambda) * B^-1.
153 std::vector<T> rep(n, zero);
154 for (std::size_t j = 0; j < N; ++j)
155 for (std::size_t i = 0; i < Np; ++i) rep[j * Np + i] = s.ini[i] / lambda;
156 out.alpha = vecmul(rep, Bi);
157 // A = B ( kron(Q', I_Np) + kron(Rout, K) ) B^-1.
158 Matrix<T> Mid(n, n, zero);
159 for (std::size_t j1 = 0; j1 < N; ++j1)
160 for (std::size_t j2 = 0; j2 < N; ++j2)
161 for (std::size_t i1 = 0; i1 < Np; ++i1)
162 for (std::size_t i2 = 0; i2 < Np; ++i2) {
163 T v = Rout(j1, j2) * s.K(i1, i2);
164 if (i1 == i2) v += Q(j2, j1);
165 Mid(j1 * Np + i1, j2 * Np + i2) = v;
166 }
167 out.A = matmul(matmul(B, Mid), Bi);
168 }
169 return out;
170}
171
172/** mfq_sojourn with the regular boundary, an ME representation and prec = 1e-14. */
173template <class T>
174MeRepresentation<T> mfq_sojourn(const Matrix<T>& Q, const Matrix<T>& Rin, const Matrix<T>& Rout) {
175 return mfq_sojourn(Q, Rin, Rout, Matrix<T>(0, 0), false,
177}
178
179} // namespace mam
180} // namespace line
181
182#endif // LINE_API_MAM_MFQ_SOJOURN_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 linear algebra over the templated number type: products, identity, inverse, and powers.
Dense matrix and non-owning view.
Core of the Markovian fluid queue: the fundamental matrices Psi, K, U and the matrix-exponential stat...
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
MeRepresentation< T > mfq_sojourn(const Matrix< T > &Q, const Matrix< T > &Rin, const Matrix< T > &Rout, const Matrix< T > &Q0, bool transToPH, const T &prec)
Sojourn time of a drop in a Markov-modulated fluid queue.
Definition mfq_sojourn.h:86
GeneralFluidSolution< T > mfq_general_solve(const Matrix< T > &Q, const Matrix< T > &R, const Matrix< T > &Q0, const T &prec)
Stationary law of a general Markovian fluid model, pi(x) = ini exp(K x) clo above level zero plus the...
Definition mfq_solve.h:299
Matrix< T > mfq_transform_to_ones(const std::vector< T > &v)
The similarity transformation B with B v = e, for a non-negative column vector v.
Definition mfq_solve.h:506
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Definition linalg.h:50
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Definition linalg.h:72
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
Definition linalg.h:36
Number-type abstraction for the templated API port.
Stationary matrix-exponential solution of a general Markovian fluid model.
Definition mfq_solve.h:280
Matrix< T > K
matrix exponent of the density, Np x Np
Definition mfq_solve.h:283
std::vector< T > mass0
P(level 0, state j), length N.
Definition mfq_solve.h:281
std::vector< T > ini
initial vector of the density, length Np
Definition mfq_solve.h:282
Matrix< T > clo
closing matrix of the density, Np x N
Definition mfq_solve.h:284
A matrix-exponential or phase-type representation (alpha, A).
Definition mfq_sojourn.h:70
Matrix< T > A
generator-like matrix exponent
Definition mfq_sojourn.h:72
std::vector< T > alpha
initial row vector
Definition mfq_sojourn.h:71