5#ifndef LINE_API_MAM_MMAPPH1FCFS_H
6#define LINE_API_MAM_MMAPPH1FCFS_H
76namespace mmapph1_detail {
88 std::vector<Matrix<T>> Dk;
89 std::vector<std::size_t> Nsk;
90 std::size_t N = 0, Ns = 0;
96Matrix<T> blkdiag(
const std::vector<PhService<T>>& svc, std::size_t Ns) {
100 for (
const PhService<T>& s : svc) {
101 for (std::size_t i = 0; i < s.S.rows(); ++i)
102 for (std::size_t j = 0; j < s.S.cols(); ++j) Sa(off + i, off + j) = s.S(i, j);
120std::vector<T> ph_equilibrium(
const PhService<T>& s) {
121 const T zero = num_traits<T>::from_int(0);
122 const std::size_t n = s.S.rows();
123 Matrix<T> G(n, n, zero);
124 for (std::size_t i = 0; i < n; ++i) {
126 for (std::size_t j = 0; j < n; ++j) rowsum += s.S(i, j);
127 for (std::size_t j = 0; j < n; ++j) G(i, j) = s.S(i, j) - rowsum * s.sigma[j];
134AgeProcess<T> build(
const Mmap<T>& arrival,
const std::vector<PhService<T>>& svc) {
135 static_assert(num_traits<T>::has_transcendental,
136 "mmapph1fcfs runs the ADDA doubling iteration of mfq_fundamental");
137 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
138 const std::size_t K = svc.size();
139 if (arrival.classes() != K)
140 throw InputError(
"mmapph1fcfs: the arrival MMAP and the service list disagree on the "
141 "number of classes");
143 a.N = arrival.order();
147 for (std::size_t k = 0; k < K; ++k)
148 for (std::size_t i = 0; i < a.N; ++i)
149 for (std::size_t j = 0; j < a.N; ++j) a.Da(i, j) += a.Dk[k](i, j);
151 Matrix<T> Q(a.N, a.N, zero);
152 for (std::size_t i = 0; i < a.N; ++i)
153 for (std::size_t j = 0; j < a.N; ++j) Q(i, j) = a.D0(i, j) + a.Da(i, j);
158 for (std::size_t k = 0; k < K; ++k) {
159 a.Nsk[k] = svc[k].S.rows();
162 std::vector<T> lambda(K, zero), mu(K, zero);
163 std::vector<std::vector<T>> beta(K);
165 for (std::size_t k = 0; k < K; ++k) {
166 const std::vector<T> td =
vecmul(theta, a.Dk[k]);
167 for (
const T& v : td) lambda[k] += v;
168 beta[k] = ph_equilibrium(svc[k]);
169 for (std::size_t i = 0; i < a.Nsk[k]; ++i)
170 for (std::size_t j = 0; j < a.Nsk[k]; ++j) mu[k] -= beta[k][i] * svc[k].S(i, j);
171 if (!(mu[k] > zero))
throw NumericError(
"mmapph1fcfs: a service law has zero rate");
172 a.rho += lambda[k] / mu[k];
175 const Matrix<T> Sa = blkdiag(svc, a.Ns);
176 const Matrix<T> Ia =
eye<T>(a.N);
181 std::vector<std::vector<T>> sa(K, std::vector<T>(a.Ns, zero));
182 std::vector<std::vector<T>> ba(K, std::vector<T>(a.Ns, zero));
184 for (std::size_t k = 0; k < K; ++k) {
185 for (std::size_t i = 0; i < a.Nsk[k]; ++i) {
186 sa[k][off + i] = svc[k].sigma[i];
187 ba[k][off + i] = beta[k][i];
193 const Matrix<T> Fpp =
kron(Ia, Sa);
194 Matrix<T> sexit(a.Ns, 1, zero);
195 for (std::size_t i = 0; i < a.Ns; ++i) {
197 for (std::size_t j = 0; j < a.Ns; ++j) s += Sa(i, j);
200 const Matrix<T> Fpm =
kron(Ia, sexit);
201 Matrix<T> iVec(a.N, a.N * a.Ns, zero);
202 for (std::size_t k = 0; k < K; ++k) {
203 Matrix<T> row(1, a.Ns, zero);
204 for (std::size_t i = 0; i < a.Ns; ++i) row(0, i) = sa[k][i];
205 const Matrix<T> blk =
kron(a.Dk[k], row);
206 for (std::size_t i = 0; i < iVec.rows(); ++i)
207 for (std::size_t j = 0; j < iVec.cols(); ++j) iVec(i, j) += blk(i, j);
210 const FluidFundamental<T> ff =
211 mfq_fundamental(Fpp, Fpm, iVec, a.D0, T(num_traits<T>::from_double(1e-14)), 150u,
213 const Matrix<T> Y0 = ff.Psi;
216 for (std::size_t i = 0; i < a.T_.rows(); ++i)
217 for (std::size_t j = 0; j < a.T_.cols(); ++j) a.T_(i, j) += Fpp(i, j);
219 a.pi0.assign(a.N * a.Ns, zero);
220 for (std::size_t k = 0; k < K; ++k) {
221 const std::vector<T> tD =
vecmul(theta, a.Dk[k]);
222 for (std::size_t i = 0; i < a.N; ++i)
223 for (std::size_t j = 0; j < a.Ns; ++j)
224 a.pi0[i * a.Ns + j] += tD[i] * T(ba[k][j] / mu[k]);
226 const std::vector<T> tmp =
vecmul(a.pi0, a.T_);
227 for (std::size_t i = 0; i < a.pi0.size(); ++i) a.pi0[i] = -tmp[i];
234Matrix<T> class_indicator(
const AgeProcess<T>& a, std::size_t k,
bool complement) {
235 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
236 Matrix<T> jm(a.Ns, 1, complement ? one : zero);
238 for (std::size_t q = 0; q < k; ++q) off += a.Nsk[q];
239 for (std::size_t i = 0; i < a.Nsk[k]; ++i) jm(off + i, 0) = complement ? zero : one;
245std::vector<T> lift(
const AgeProcess<T>& a,
const Matrix<T>& jm) {
246 std::vector<T> out(a.N * a.Ns);
247 for (std::size_t i = 0; i < a.N; ++i)
248 for (std::size_t j = 0; j < a.Ns; ++j) out[i * a.Ns + j] = jm(j, 0);
254T contract(
const std::vector<T>& pi0,
const Matrix<T>& M,
const std::vector<T>& v) {
255 const std::vector<T> row =
vecmul(pi0, M);
256 T s = num_traits<T>::from_int(0);
257 for (std::size_t i = 0; i < row.size(); ++i) s += row[i] * v[i];
263Matrix<T> class_exit(
const AgeProcess<T>& a,
const std::vector<PhService<T>>& svc, std::size_t k) {
264 const T zero = num_traits<T>::from_int(0);
265 Matrix<T> sv(a.Ns, 1, zero);
267 for (std::size_t q = 0; q < k; ++q) off += a.Nsk[q];
268 for (std::size_t i = 0; i < a.Nsk[k]; ++i) {
270 for (std::size_t j = 0; j < a.Nsk[k]; ++j) s += svc[k].S(i, j);
304 double precision = 1e-14) {
305 using namespace mmapph1_detail;
307 const AgeProcess<T> a = build(arrival, svc);
308 const std::size_t K = svc.size(), n = a.N * a.Ns;
311 for (std::size_t i = 0; i < n; ++i)
312 for (std::size_t j = 0; j < n; ++j) negT(i, j) = -a.T_(i, j);
314 const std::vector<T> vv =
vecmul(a.pi0, iT);
318 std::vector<std::size_t> nz;
319 for (std::size_t i = 0; i < n; ++i)
322 throw NumericError(
"mmapph1fcfs_stdistr_ph: the age density has empty support");
324 std::vector<StDistrPh<T>> out(K);
325 for (std::size_t k = 0; k < K; ++k) {
326 const std::vector<T> clo =
mulvec(iT, lift(a, class_exit(a, svc, k)));
328 for (std::size_t i = 0; i < n; ++i) norm += a.pi0[i] * clo[i];
330 throw NumericError(
"mmapph1fcfs_stdistr_ph: class " + std::to_string(k + 1) +
331 " carries no sojourn-time mass");
333 const std::vector<T> Tclo =
mulvec(a.T_, clo);
334 std::vector<T> cl(n);
335 for (std::size_t i = 0; i < n; ++i) cl[i] = T(-Tclo[i] / norm);
337 out[k].alpha.assign(nz.size(), zero);
338 for (std::size_t i = 0; i < nz.size(); ++i) out[k].alpha[i] = T(cl[nz[i]] * vv[nz[i]]);
339 out[k].A =
Matrix<T>(nz.size(), nz.size(), zero);
340 for (std::size_t i = 0; i < nz.size(); ++i)
341 for (std::size_t j = 0; j < nz.size(); ++j)
343 out[k].A(i, j) = T(a.T_(nz[j], nz[i]) * vv[nz[j]] / vv[nz[i]]);
356 using namespace mmapph1_detail;
358 const AgeProcess<T> a = build(arrival, svc);
359 const std::size_t K = svc.size();
362 for (std::size_t i = 0; i < a.N; ++i)
363 for (std::size_t j = 0; j < a.N; ++j) QA(i, j) = a.D0(i, j) + a.Da(i, j);
367 std::vector<T> out(K, zero);
368 for (std::size_t k = 0; k < K; ++k) {
372 const std::vector<T> row =
vecmul(a.pi0, EL2);
374 for (
const T& v : row) s += v;
375 const Matrix<T> jm = class_indicator(a, k,
false);
376 out[k] = T(s + contract(a.pi0, EL1, lift(a, jm)));
391 std::size_t levels) {
392 using namespace mmapph1_detail;
394 if (levels == 0)
throw InputError(
"mmapph1fcfs_ncdistr: at least one level is required");
395 const AgeProcess<T> a = build(arrival, svc);
396 const std::size_t K = svc.size();
400 std::vector<std::vector<T>> out(K, std::vector<T>(levels, zero));
401 for (std::size_t k = 0; k < K; ++k) {
405 for (std::size_t i = 0; i < a.N; ++i)
406 for (std::size_t j = 0; j < a.N; ++j)
407 B(i, j) = a.D0(i, j) + a.Da(i, j) - a.Dk[k](i, j);
410 const std::vector<T> ejm = lift(a, class_indicator(a, k,
false));
411 const std::vector<T> ejmc = lift(a, class_indicator(a, k,
true));
414 out[k][0] = T(one - a.rho + contract(a.pi0, LmCurr, ejmc));
415 for (std::size_t i = 1; i < levels; ++i) {
418 out[k][i] = T(contract(a.pi0, LmCurr, ejmc) + contract(a.pi0, LmPrev, ejm));
NumericError(const std::string &what)
The Kronecker operator of a FIXED (A,B) pair, factorized once.
Matrix< T > solve_lyap(const Matrix< T > &C) const
Solve A X + X B + C = 0, MATLAB's lyap(A,B,C).
Steady-state distribution of a continuous-time Markov chain.
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...
std::vector< T > mmapph1fcfs_ncmean(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc)
Per-class mean number of customers in the system, BUTools' 'ncMoms', 1.
Matrix< T > kron(const Matrix< T > &A, const Matrix< T > &B)
Kronecker product.
FluidFundamental< T > mfq_fundamental(const Matrix< T > &Fpp, const Matrix< T > &Fpm, const Matrix< T > &Fmp, const Matrix< T > &Fmm, const T &precision, unsigned maxNumIt, RiccatiMethod method)
Psi, K and U of a fluid queue whose drifts have been normalized to +-1.
std::vector< std::vector< T > > mmapph1fcfs_ncdistr(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t levels)
Per-class queue-length distribution, BUTools' 'ncDistr', n: P(N_k = 0..n-1).
std::vector< StDistrPh< T > > mmapph1fcfs_stdistr_ph(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, double precision=1e-14)
Per-class SOJOURN TIME as a continuous phase-type law, BUTools' 'stDistrPH'.
std::vector< T > ctmc_solve(const Matrix< T > &Qin)
Steady-state distribution of a continuous-time Markov chain.
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 > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
Matrix< T > eye(std::size_t n)
Identity of order n.
Number-type abstraction for the templated API port.
An MMAP: the underlying MAP plus the per-class arrival matrices.
One class's phase-type service law, He's (sigma_k, S_k).
Matrix< T > S
transient generator
std::vector< T > sigma
initial probability row vector
A phase-type law (alpha, A) as BUTools' 'stDistrPH' returns it.
The Sylvester equation A X + X B = C, and MATLAB's lyap(A,B,C).