5#ifndef LINE_API_MAM_MMAPPH1PRIO_H
6#define LINE_API_MAM_MMAPPH1PRIO_H
63namespace prio_detail {
65enum class Measure { StMoms, StDistr, NcMoms, NcDistr };
68Matrix<T> zeros(std::size_t r, std::size_t c) {
73Matrix<T> eye(std::size_t n) {
74 Matrix<T> I = zeros<T>(n, n);
75 for (std::size_t i = 0; i < n; ++i) I(i, i) = num_traits<T>::from_int(1);
80Matrix<T> ones(std::size_t r, std::size_t c) {
81 return Matrix<T>(r, c, num_traits<T>::from_int(1));
85Matrix<T> add(
const Matrix<T>& A,
const Matrix<T>& B) {
87 for (std::size_t i = 0; i < A.rows(); ++i)
88 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) += B(i, j);
93Matrix<T> sub(
const Matrix<T>& A,
const Matrix<T>& B) {
95 for (std::size_t i = 0; i < A.rows(); ++i)
96 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) -= B(i, j);
101Matrix<T> scal(
const Matrix<T>& A,
const T& s) {
103 for (std::size_t i = 0; i < A.rows(); ++i)
104 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) = T(C(i, j) * s);
109Matrix<T> mul(
const Matrix<T>& A,
const Matrix<T>& B) {
110 if (A.rows() == 0 || B.cols() == 0 || A.cols() == 0)
return zeros<T>(A.rows(), B.cols());
115Matrix<T> mul(
const Matrix<T>& A,
const Matrix<T>& B,
const Matrix<T>& C) {
116 return mul(mul(A, B), C);
121Matrix<T> ninv(
const Matrix<T>& A) {
122 return inverse(scal(A, T(num_traits<T>::from_int(-1))));
126Matrix<T> mpow(
const Matrix<T>& A, std::size_t n) {
127 Matrix<T> P = eye<T>(A.rows());
128 for (std::size_t i = 0; i < n; ++i) P = mul(P, A);
133void setblk(Matrix<T>& A, std::size_t r0, std::size_t c0,
const Matrix<T>& B) {
134 for (std::size_t i = 0; i < B.rows(); ++i)
135 for (std::size_t j = 0; j < B.cols(); ++j) A(r0 + i, c0 + j) = B(i, j);
139Matrix<T> getblk(
const Matrix<T>& A, std::size_t r0, std::size_t c0, std::size_t nr,
141 Matrix<T> B = zeros<T>(nr, nc);
142 for (std::size_t i = 0; i < nr; ++i)
143 for (std::size_t j = 0; j < nc; ++j) B(i, j) = A(r0 + i, c0 + j);
148Matrix<T> hcat(
const Matrix<T>& A,
const Matrix<T>& B) {
149 const std::size_t r = A.cols() > 0 ? A.rows() : B.rows();
150 Matrix<T> C = zeros<T>(r, A.cols() + B.cols());
152 setblk(C, 0, A.cols(), B);
157Matrix<T> vcat(
const Matrix<T>& A,
const Matrix<T>& B) {
158 const std::size_t c = A.rows() > 0 ? A.cols() : B.cols();
159 Matrix<T> C = zeros<T>(A.rows() + B.rows(), c);
161 setblk(C, A.rows(), 0, B);
166Matrix<T> blkdiag(
const Matrix<T>& A,
const Matrix<T>& B) {
167 Matrix<T> C = zeros<T>(A.rows() + B.rows(), A.cols() + B.cols());
169 setblk(C, A.rows(), A.cols(), B);
175Matrix<T> rowsum(
const Matrix<T>& A) {
176 Matrix<T> s = zeros<T>(A.rows(), 1);
177 for (std::size_t i = 0; i < A.rows(); ++i)
178 for (std::size_t j = 0; j < A.cols(); ++j) s(i, 0) += A(i, j);
184T total(
const Matrix<T>& A) {
185 T s = num_traits<T>::from_int(0);
186 for (std::size_t i = 0; i < A.rows(); ++i)
187 for (std::size_t j = 0; j < A.cols(); ++j) s += A(i, j);
192Matrix<T> rowvec(
const std::vector<T>& v) {
193 Matrix<T> r = zeros<T>(1, v.size());
194 for (std::size_t j = 0; j < v.size(); ++j) r(0, j) = v[j];
199T factorial(std::size_t n) {
200 T f = num_traits<T>::from_int(1);
201 for (std::size_t i = 2; i <= n; ++i) f = T(f * num_traits<T>::from_int((
int)i));
207std::vector<T> moms_from_factorial_moms(
const std::vector<T>& fm) {
208 std::vector<T> m(fm.size());
209 if (fm.empty())
return m;
211 for (std::size_t i = 2; i <= fm.size(); ++i) {
213 std::vector<double> c(i + 1, 0.0);
215 for (std::size_t r = 0; r < i; ++r) {
216 std::vector<double> nc(i + 1, 0.0);
217 for (std::size_t j = 0; j <= r; ++j) {
219 nc[j] -=
static_cast<double>(r) * c[j];
224 for (std::size_t j = 1; j < i; ++j) v -= T(num_traits<T>::from_double(c[j]) * m[j - 1]);
233 std::size_t N = 0, K = 0;
235 std::vector<Matrix<T>> D;
236 std::vector<Matrix<T>> sigma;
237 std::vector<Matrix<T>> S;
238 std::vector<Matrix<T>> s;
239 std::vector<std::size_t> M;
241 std::size_t erl = 200;
243 std::size_t msum(std::size_t a, std::size_t b)
const {
245 for (std::size_t i = a; i < b; ++i) t += M[i];
251PrioInput<T> prepare(
const Mmap<T>& arrival,
const std::vector<PhService<T>>& svc,
252 const PrioQueueOptions& opt,
const char* who) {
253 static_assert(num_traits<T>::has_transcendental,
254 "the MMAP/PH/1 priority analyzers run tolerance-terminated Riccati iterations");
257 if (in.K == 0)
throw InputError(std::string(who) +
": at least one class is required");
258 if (arrival.classes() != in.K)
260 ": the arrival MMAP and the service list disagree on the number of classes");
261 if (opt.erl_max_order < 1)
throw InputError(std::string(who) +
": erl_max_order must be >= 1");
262 in.N = arrival.order();
267 for (std::size_t k = 0; k < in.K; ++k) in.sD = add(in.sD, in.D[k]);
268 for (std::size_t k = 0; k < in.K; ++k) {
269 const std::size_t m = svc[k].S.rows();
270 if (m == 0 || svc[k].S.cols() != m || svc[k].sigma.size() != m)
271 throw InputError(std::string(who) +
": class " + std::to_string(k + 1) +
272 " has a malformed PH service representation");
274 in.sigma.push_back(rowvec(svc[k].sigma));
275 in.S.push_back(svc[k].S);
276 in.s.push_back(scal(rowsum(svc[k].S), T(num_traits<T>::from_int(-1))));
278 in.prec = num_traits<T>::from_double(opt.precision);
279 in.erl = opt.erl_max_order;
284FluidFundamental<T> fluid(
const PrioInput<T>& in,
const Matrix<T>& Fpp,
const Matrix<T>& Fpm,
285 const Matrix<T>& Fmp,
const Matrix<T>& Fmm) {
292 Matrix<T> Qspp, Qspm, Qsmp, Qsmm, inis, Psis;
297std::vector<Matrix<T>> st_moment_chain(
const SojournModel<T>& sm, std::size_t n) {
298 const Matrix<T> A = add(sm.Qspp, mul(sm.Psis, sm.Qsmp));
299 const Matrix<T> B = add(sm.Qsmm, mul(sm.Qsmp, sm.Psis));
300 const SylvesterFactor<T> F(A, B);
301 std::vector<Matrix<T>> Pn{sm.Psis};
302 std::vector<Matrix<T>> QP{mul(sm.Qsmp, sm.Psis)};
303 for (std::size_t m = 1; m <= n; ++m) {
304 Matrix<T> C = scal(Pn[m - 1], T(num_traits<T>::from_int(-2 * (
int)m)));
305 T bino = num_traits<T>::from_int(1);
306 for (std::size_t i = 1; i + 1 <= m; ++i) {
307 bino = T(bino * num_traits<T>::from_int((
int)(m - i + 1)) /
308 num_traits<T>::from_int((
int)i));
309 C = add(C, scal(mul(Pn[i], QP[m - i]), bino));
311 Pn.push_back(F.solve_lyap(C));
312 QP.push_back(mul(sm.Qsmp, Pn.back()));
323std::vector<T> erlang_terms(
const PrioInput<T>& in,
const SojournModel<T>& sm,
const T& lambda) {
324 const std::size_t L = in.erl;
325 const Matrix<T> Ip = eye<T>(sm.Qspp.rows()), Im = eye<T>(sm.Qsmm.rows());
326 const Matrix<T> Qpp = sub(sm.Qspp, scal(Ip, lambda));
327 const Matrix<T> Qmm = sub(sm.Qsmm, scal(Im, lambda));
328 const Matrix<T> Psie = fluid(in, Qpp, sm.Qspm, sm.Qsmp, Qmm).Psi;
330 out.push_back(total(mul(sm.inis, Psie)));
331 const Matrix<T> A = add(Qpp, mul(Psie, sm.Qsmp));
332 const Matrix<T> B = add(Qmm, mul(sm.Qsmp, Psie));
333 const SylvesterFactor<T> F(A, B);
334 std::vector<Matrix<T>> Pn{Psie};
335 std::vector<Matrix<T>> QP{mul(sm.Qsmp, Psie)};
336 const T two_l = T(num_traits<T>::from_int(2) * lambda);
337 for (std::size_t n = 1; n + 1 <= L; ++n) {
338 Matrix<T> C = scal(Pn[n - 1], two_l);
339 for (std::size_t i = 1; i + 1 <= n; ++i) C = add(C, mul(Pn[i], QP[n - i]));
340 Pn.push_back(F.solve_lyap(C));
341 QP.push_back(mul(sm.Qsmp, Pn.back()));
342 out.push_back(total(mul(sm.inis, Pn.back())));
349std::vector<T> random_time_moms(
const PrioInput<T>& in, std::size_t k,
350 const std::vector<Matrix<T>>& QLDPn, std::size_t n) {
352 const Matrix<T> pi = rowvec(piv);
353 const T lambdak = total(mul(pi, in.D[k]));
354 const Matrix<T> iTerm =
inverse(sub(mul(ones<T>(in.N, 1), pi), in.sD));
355 const Matrix<T> dk1 = rowsum(in.D[k]);
356 std::vector<Matrix<T>> QLPn{pi};
357 std::vector<T> ql(n);
358 for (std::size_t m = 1; m <= n; ++m) {
359 const T mm = num_traits<T>::from_int((
int)m);
360 const Matrix<T> a = sub(QLDPn[m - 1], scal(mul(QLPn[m - 1], in.D[k]), T(num_traits<T>::from_int(1) / lambdak)));
361 const T sumP = T(total(QLDPn[m]) + mm * total(mul(a, iTerm, dk1)));
362 const Matrix<T> b = sub(mul(QLPn[m - 1], in.D[k]), scal(QLDPn[m - 1], lambdak));
363 const Matrix<T> P = add(scal(pi, sumP), scal(mul(b, iTerm), mm));
365 ql[m - 1] = total(P);
367 return moms_from_factorial_moms(ql);
372std::vector<T> random_time_probs(
const PrioInput<T>& in, std::size_t k,
373 const std::vector<Matrix<T>>& dql) {
375 const T lambdak = total(mul(pi, in.D[k]));
376 const Matrix<T> iTerm = ninv(sub(in.sD, in.D[k]));
378 Matrix<T> q = mul(scal(dql[0], lambdak), iTerm);
379 out.push_back(total(q));
380 for (std::size_t n = 1; n < dql.size(); ++n) {
381 q = mul(add(mul(q, in.D[k]), scal(sub(dql[n], dql[n - 1]), lambdak)), iTerm);
382 out.push_back(total(q));
389std::vector<T> qbd_measure(
const Matrix<T>& R, Matrix<T> p0, Measure what, std::size_t n) {
390 const Matrix<T> IR = sub(eye<T>(R.rows()), R);
391 const Matrix<T> iIR =
inverse(IR);
392 p0 = scal(p0, T(num_traits<T>::from_int(1) / total(mul(p0, iIR))));
394 if (what == Measure::NcMoms) {
395 for (std::size_t i = 1; i <= n; ++i)
396 out.push_back(total(scal(mul(p0, mpow(R, i), mpow(iIR, i + 1)), factorial<T>(i))));
397 return moms_from_factorial_moms(out);
400 out.push_back(total(v));
401 for (std::size_t i = 1; i < n; ++i) {
403 out.push_back(total(v));
409Matrix<T> qbd_R_of(
const PrioInput<T>& in,
const Matrix<T>& B,
const Matrix<T>& L,
410 const Matrix<T>& F) {
416std::vector<T> ph_sojourn(
const Matrix<T>& zeta,
const Matrix<T>& Z,
const Matrix<T>& z,
417 Measure what, std::size_t n,
const std::vector<T>& pts) {
418 const Matrix<T> iZ = ninv(Z);
420 if (what == Measure::StMoms) {
421 for (std::size_t i = 1; i <= n; ++i)
422 out.push_back(T(factorial<T>(i) * total(mul(zeta, mpow(iZ, i + 1), z))));
424 const Matrix<T> I = eye<T>(Z.rows());
425 for (
const T& t : pts)
426 out.push_back(total(mul(mul(zeta, iZ), sub(I,
expm(scal(Z, t))), z)));
434std::vector<T> prpr_class(
const PrioInput<T>& in, std::size_t k, Measure what, std::size_t n,
435 const std::vector<T>& pts) {
436 const std::size_t N = in.N, K = in.K;
437 const Matrix<T>& I = in.I;
439 const std::size_t sM = in.msum(k, K);
440 Matrix<T> Qwmm = in.D0;
441 for (std::size_t i = 0; i < k; ++i) Qwmm = add(Qwmm, in.D[i]);
442 Matrix<T> Qwpm = zeros<T>(N * sM, N), Qwmp = zeros<T>(N, N * sM), Qwpp = zeros<T>(N * sM, N * sM);
444 for (std::size_t i = k; i < K; ++i) {
445 setblk(Qwmp, 0, kix,
kron(in.D[i], in.sigma[i]));
446 setblk(Qwpm, kix, 0,
kron(I, in.s[i]));
447 setblk(Qwpp, kix, kix,
kron(I, in.S[i]));
450 const FluidFundamental<T> fw = fluid(in, Qwpp, Qwpm, Qwmp, Qwmm);
451 const Matrix<T>& Kw = fw.K;
452 const Matrix<T> iKw = ninv(Kw);
454 add(ones<T>(N, 1), scal(rowsum(mul(Qwmp, iKw)), T(num_traits<T>::from_int(2))));
455 Matrix<T> UwUa = hcat(fw.U, Ua);
456 Matrix<T> At = zeros<T>(N + 1, N);
457 for (std::size_t i = 0; i < N; ++i)
458 for (std::size_t j = 0; j <= N; ++j) At(j, i) = UwUa(i, j);
459 std::vector<T> rhs(N + 1, num_traits<T>::from_int(0));
460 rhs[N] = num_traits<T>::from_int(1);
461 const Matrix<T> pm = rowvec(mfq_detail::normal_equations_solve(At, rhs));
463 Matrix<T> Bw = zeros<T>(N * sM, N);
464 setblk(Bw, 0, 0,
kron(I, in.s[k]));
465 const Matrix<T> pmQ = mul(pm, Qwmp);
466 const Matrix<T> kappa = scal(pmQ, T(num_traits<T>::from_int(1) / total(mul(pmQ, iKw, Bw))));
469 if (what == Measure::StMoms || what == Measure::StDistr)
470 return ph_sojourn(kappa, Kw, rowsum(Bw), what, n, pts);
471 const std::size_t Mk = in.M[k];
472 const Matrix<T> IM = eye<T>(Mk);
473 const Matrix<T> sDk = sub(in.sD, in.D[k]);
474 const Matrix<T> L = add(
kron(sDk, IM),
kron(I, in.S[k]));
475 const Matrix<T> B =
kron(I, mul(in.s[k], in.sigma[k]));
476 const Matrix<T> F =
kron(in.D[k], IM);
477 const Matrix<T> L0 =
kron(sDk, IM);
478 const Matrix<T> R = qbd_R_of(in, B, L, F);
480 return qbd_measure(R, p0, what, n);
486 for (std::size_t i = 0; i <= k; ++i) sm.Qsmm = add(sm.Qsmm, in.D[i]);
487 const std::size_t Np = Kw.rows(), ext = N * in.msum(k + 1, K);
488 sm.Qspm = zeros<T>(Np + ext, N);
489 sm.Qsmp = zeros<T>(N, Np + ext);
490 sm.Qspp = zeros<T>(Np + ext, Np + ext);
491 setblk(sm.Qspp, 0, 0, Kw);
492 setblk(sm.Qspm, 0, 0, Bw);
494 for (std::size_t i = k + 1; i < K; ++i) {
495 setblk(sm.Qsmp, 0, kix,
kron(in.D[i], in.sigma[i]));
496 setblk(sm.Qspm, kix, 0,
kron(I, in.s[i]));
497 setblk(sm.Qspp, kix, kix,
kron(I, in.S[i]));
500 sm.inis = hcat(kappa, zeros<T>(1, ext));
501 sm.Psis = fluid(in, sm.Qspp, sm.Qspm, sm.Qsmp, sm.Qsmm).Psi;
504 if (what == Measure::StMoms) {
505 const std::vector<Matrix<T>> Pn = st_moment_chain(sm, n);
506 for (std::size_t m = 1; m <= n; ++m) {
507 T v = T(total(mul(sm.inis, Pn[m])) / num_traits<T>::from_double(std::pow(2.0, (
double)m)));
508 if (m % 2 == 1) v = T(-v);
511 }
else if (what == Measure::StDistr) {
512 for (
const T& t : pts) {
513 const T lambda = T(num_traits<T>::from_int((
int)in.erl) / t / num_traits<T>::from_int(2));
514 T pr = num_traits<T>::from_int(0);
515 for (
const T& v : erlang_terms(in, sm, lambda)) pr += v;
518 }
else if (what == Measure::NcMoms) {
519 const Matrix<T> A = add(sm.Qspp, mul(sm.Psis, sm.Qsmp));
520 const Matrix<T> B = add(sm.Qsmm, mul(sm.Qsmp, sm.Psis));
521 const SylvesterFactor<T> F(A, B);
522 std::vector<Matrix<T>> P{sm.Psis};
523 std::vector<Matrix<T>> QP{mul(sm.Qsmp, sm.Psis)};
524 for (std::size_t m = 1; m <= n; ++m) {
525 Matrix<T> C = scal(mul(P[m - 1], in.D[k]), T(num_traits<T>::from_int((
int)m)));
526 T bino = num_traits<T>::from_int(1);
527 for (std::size_t i = 1; i + 1 <= m; ++i) {
528 bino = T(bino * num_traits<T>::from_int((
int)(m - i + 1)) /
529 num_traits<T>::from_int((
int)i));
530 C = add(C, scal(mul(P[i], QP[m - i]), bino));
532 P.push_back(F.solve_lyap(C));
533 QP.push_back(mul(sm.Qsmp, P.back()));
535 std::vector<Matrix<T>> QLDPn;
536 for (
const Matrix<T>& p : P) QLDPn.push_back(mul(sm.inis, p));
537 out = random_time_moms(in, k, QLDPn, n);
539 Matrix<T> sDk = in.D0;
540 for (std::size_t i = 0; i < k; ++i) sDk = add(sDk, in.D[i]);
541 const Matrix<T> Psid = fluid(in, sm.Qspp, sm.Qspm, sm.Qsmp, sDk).Psi;
542 const Matrix<T> A = add(sm.Qspp, mul(Psid, sm.Qsmp));
543 const Matrix<T> B = add(sDk, mul(sm.Qsmp, Psid));
544 const SylvesterFactor<T> F(A, B);
545 std::vector<Matrix<T>> P{Psid};
546 std::vector<Matrix<T>> QP{mul(sm.Qsmp, Psid)};
547 std::vector<Matrix<T>> dql{mul(sm.inis, Psid)};
548 for (std::size_t m = 1; m < n; ++m) {
549 Matrix<T> C = mul(P[m - 1], in.D[k]);
550 for (std::size_t i = 1; i + 1 <= m; ++i) C = add(C, mul(P[i], QP[m - i]));
551 P.push_back(F.solve_lyap(C));
552 QP.push_back(mul(sm.Qsmp, P.back()));
553 dql.push_back(mul(sm.inis, P.back()));
555 out = random_time_probs(in, k, dql);
565 std::vector<Matrix<T>> Psiw, Qwmp, Qwzp, Qwpp, Qwmz, Qwpz, Qwzz, Qwmm, Qwpm, Qwzm;
566 std::vector<Matrix<T>> q0, qL;
567 std::vector<T> lambda;
571NpprModel<T> nppr_build(
const PrioInput<T>& in) {
572 const std::size_t N = in.N, K = in.K;
573 const Matrix<T>& I = in.I;
574 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
578 const std::size_t sM = in.msum(0, K);
579 Matrix<T> Qwpp = zeros<T>(N * sM, N * sM), Qwmp = zeros<T>(N, N * sM),
580 Qwpm = zeros<T>(N * sM, N);
582 for (std::size_t i = 0; i < K; ++i) {
583 setblk(Qwpp, kix, kix,
kron(I, in.S[i]));
584 setblk(Qwmp, 0, kix,
kron(in.D[i], in.sigma[i]));
585 setblk(Qwpm, kix, 0,
kron(I, in.s[i]));
588 const FluidFundamental<T> fw = fluid(in, Qwpp, Qwpm, Qwmp, in.D0);
589 const Matrix<T> iKw = ninv(fw.K);
590 const Matrix<T> Ua = add(ones<T>(N, 1), scal(rowsum(mul(Qwmp, iKw)), T(num_traits<T>::from_int(2))));
591 const Matrix<T> UwUa = hcat(fw.U, Ua);
592 Matrix<T> At = zeros<T>(N + 1, N);
593 for (std::size_t i = 0; i < N; ++i)
594 for (std::size_t j = 0; j <= N; ++j) At(j, i) = UwUa(i, j);
595 std::vector<T> rhs(N + 1, zero);
597 md.pm = rowvec(mfq_detail::normal_equations_solve(At, rhs));
598 const T spm = total(md.pm);
599 const T half_idle = T((one - spm) / num_traits<T>::from_int(2));
600 const T ro = T(half_idle / (spm + half_idle));
601 const Matrix<T> kappa = scal(md.pm, T(one / spm));
605 for (std::size_t i = 0; i < K; ++i) md.lambda[i] = total(mul(pi, in.D[i]));
608 for (std::size_t k = 0; k < K; ++k) {
609 const std::size_t Mlo = in.msum(0, k), Mhi = in.msum(k, K);
610 const std::size_t p = N * Mlo * Mhi + N * Mhi, z = N * Mlo;
611 Matrix<T> Qkwpp = zeros<T>(p, p), Qkwpz = zeros<T>(p, z), Qkwpm = zeros<T>(p, N);
612 Matrix<T> Qkwmz = zeros<T>(N, z), Qkwmp = zeros<T>(N, p);
613 Matrix<T> Dlo = in.D0;
614 for (std::size_t i = 0; i < k; ++i) Dlo = add(Dlo, in.D[i]);
615 Matrix<T> Qkwzp = zeros<T>(z, p), Qkwzm = zeros<T>(z, N), Qkwzz = zeros<T>(z, z);
617 for (std::size_t i = k; i < K; ++i) {
618 std::size_t kix2 = 0;
619 for (std::size_t j = 0; j < k; ++j) {
620 const Matrix<T> IMj = eye<T>(in.M[j]);
621 setblk(Qkwpp, kix, kix,
kron(I,
kron(IMj, in.S[i])));
622 setblk(Qkwpz, kix, kix2,
kron(I,
kron(IMj, in.s[i])));
623 setblk(Qkwzp, kix2, kix,
kron(in.D[i],
kron(IMj, in.sigma[i])));
624 kix += N * in.M[j] * in.M[i];
628 for (std::size_t i = k; i < K; ++i) {
629 setblk(Qkwpp, kix, kix,
kron(I, in.S[i]));
630 setblk(Qkwpm, kix, 0,
kron(I, in.s[i]));
631 setblk(Qkwmp, 0, kix,
kron(in.D[i], in.sigma[i]));
635 for (std::size_t j = 0; j < k; ++j) {
636 setblk(Qkwzz, kix, kix, add(
kron(Dlo, eye<T>(in.M[j])),
kron(I, in.S[j])));
637 setblk(Qkwzm, kix, 0,
kron(I, in.s[j]));
640 Matrix<T> Fpp = Qkwpp, Fpm = Qkwpm;
642 const Matrix<T> iZ = ninv(Qkwzz);
643 Fpp = add(Fpp, mul(Qkwpz, iZ, Qkwzp));
644 Fpm = add(Fpm, mul(Qkwpz, iZ, Qkwzm));
646 md.Psiw.push_back(fluid(in, Fpp, Fpm, Qkwmp, Dlo).Psi);
647 md.Qwzp.push_back(Qkwzp);
648 md.Qwmp.push_back(Qkwmp);
649 md.Qwpp.push_back(Qkwpp);
650 md.Qwmz.push_back(Qkwmz);
651 md.Qwpz.push_back(Qkwpz);
652 md.Qwzz.push_back(Qkwzz);
653 md.Qwmm.push_back(Dlo);
654 md.Qwpm.push_back(Qkwpm);
655 md.Qwzm.push_back(Qkwzm);
660 for (
const T& l : md.lambda) lambdaS += l;
661 const Matrix<T> iD0 = ninv(in.D0);
662 std::vector<Matrix<T>> phi{scal(mul(kappa, scal(in.D0, T(-one))), T((one - ro) / lambdaS))};
663 md.q0.assign(K, Matrix<T>());
664 md.qL.assign(K, Matrix<T>());
665 for (std::size_t k = 1; k < K; ++k) {
666 Matrix<T> sDk = in.D0;
667 for (std::size_t j = 0; j < k; ++j) sDk = add(sDk, in.D[j]);
669 for (std::size_t j = 0; j < k; ++j) lk += md.lambda[j];
670 const T pk = T(lk / lambdaS - (one - ro) * total(mul(kappa, rowsum(sDk))) / lambdaS);
671 const Matrix<T>& Qwzpk = md.Qwzp[k];
672 std::vector<Matrix<T>> Ak(k), Gi(k);
674 for (std::size_t ii = 0; ii < k; ++ii) {
675 const std::size_t bs = N * in.M[ii];
676 const Matrix<T> V1 = getblk(Qwzpk, vix, 0, bs, Qwzpk.cols());
677 Gi[ii] = ninv(add(
kron(sDk, eye<T>(in.M[ii])),
kron(I, in.S[ii])));
678 Ak[ii] = mul(mul(
kron(I, in.sigma[ii]), Gi[ii]),
679 add(
kron(I, in.s[ii]), mul(V1, md.Psiw[k])));
682 const Matrix<T>
Bk = mul(md.Qwmp[k], md.Psiw[k]);
683 Matrix<T> ztag = mul(phi[0], add(sub(mul(iD0, in.D[k - 1], Ak[k - 1]), Ak[0]), mul(iD0, Bk)));
684 for (std::size_t i = 0; i + 1 < k; ++i)
685 ztag = add(ztag, add(mul(phi[i + 1], sub(Ak[i], Ak[i + 1])),
686 mul(mul(phi[0], iD0), mul(in.D[i], Ak[i]))));
687 Matrix<T> Mx = sub(eye<T>(N), Ak[k - 1]);
688 for (std::size_t i = 0; i < N; ++i) Mx(i, 0) = one;
689 Matrix<T> lhs = ztag;
691 phi.push_back(mul(lhs,
inverse(Mx)));
692 md.q0[k] = mul(phi[0], iD0);
694 for (std::size_t ii = 0; ii < k; ++ii) {
695 const Matrix<T> a = add(sub(phi[ii + 1], phi[ii]), mul(phi[0], iD0, in.D[ii]));
696 const Matrix<T> q = mul(mul(a,
kron(I, in.sigma[ii])), Gi[ii]);
697 qL = (ii == 0) ? q : hcat(qL, q);
705std::vector<T> nppr_class(
const PrioInput<T>& in,
const NpprModel<T>& md, std::size_t k,
706 Measure what, std::size_t n,
const std::vector<T>& pts) {
707 const std::size_t N = in.N, K = in.K;
708 const Matrix<T>& I = in.I;
709 const T one = num_traits<T>::from_int(1);
710 Matrix<T> sD0k = in.D0;
711 for (std::size_t i = 0; i < k; ++i) sD0k = add(sD0k, in.D[i]);
712 const bool hasz = md.Qwzz[k].rows() > 0;
713 const Matrix<T> iZz = hasz ? ninv(md.Qwzz[k]) : Matrix<T>();
714 Matrix<T> Kw = add(md.Qwpp[k], mul(md.Psiw[k], md.Qwmp[k]));
715 if (hasz) Kw = add(Kw, mul(md.Qwpz[k], iZz, md.Qwzp[k]));
718 if (what == Measure::StMoms || what == Measure::StDistr) {
719 Matrix<T> AM, BM, CM;
720 for (std::size_t i = 0; i < k; ++i) {
721 const Matrix<T> am =
kron(ones<T>(N, 1),
kron(eye<T>(in.M[i]), in.s[k]));
722 AM = (i == 0) ? am : blkdiag(AM, am);
723 BM = (i == 0) ? in.S[i] : blkdiag(BM, in.S[i]);
724 CM = (i == 0) ? in.s[i] : vcat(CM, in.s[i]);
726 const Matrix<T> AMz = vcat(AM, zeros<T>(N * in.M[k], AM.cols()));
727 const Matrix<T> Z = vcat(hcat(Kw, AMz), hcat(zeros<T>(BM.rows(), Kw.cols()), BM));
729 vcat(vcat(zeros<T>(AM.rows(), 1),
kron(ones<T>(N, 1), in.s[k])), CM);
730 const Matrix<T> iniw =
731 hcat(add(mul(md.q0[k], md.Qwmp[k]), mul(md.qL[k], md.Qwzp[k])),
732 zeros<T>(1, BM.rows()));
733 const Matrix<T> zeta = scal(iniw, T(one / total(mul(iniw, ninv(Z), z))));
734 return ph_sojourn(zeta, Z, z, what, n, pts);
736 const std::size_t sM = in.msum(0, K), c0 = N * in.msum(0, k);
737 Matrix<T> L = zeros<T>(N * sM, N * sM), B = zeros<T>(N * sM, N * sM),
738 F = zeros<T>(N * sM, N * sM);
740 for (std::size_t i = 0; i < K; ++i) {
741 const Matrix<T> IMi = eye<T>(in.M[i]);
742 setblk(F, kix, kix,
kron(in.D[k], IMi));
743 setblk(L, kix, kix, add(
kron(sD0k, IMi),
kron(I, in.S[i])));
744 const Matrix<T> blk =
kron(I, mul(in.s[i], in.sigma[k]));
746 setblk(L, kix, c0, blk);
748 setblk(B, kix, c0, blk);
751 const Matrix<T> R = qbd_R_of(in, B, L, F);
752 const Matrix<T> p0 = hcat(md.qL[k], mul(md.q0[k],
kron(I, in.sigma[k])));
753 return qbd_measure(R, p0, what, n);
757 Matrix<T> BM, CM, DM;
758 for (std::size_t i = 0; i < k; ++i) {
759 const Matrix<T> bm =
kron(I, in.S[i]), cm =
kron(I, in.s[i]),
760 dm =
kron(in.D[k], eye<T>(in.M[i]));
761 BM = (i == 0) ? bm : blkdiag(BM, bm);
762 CM = (i == 0) ? cm : vcat(CM, cm);
763 DM = (i == 0) ? dm : blkdiag(DM, dm);
765 Matrix<T> Kwu = Kw, Bwu = mul(md.Psiw[k], in.D[k]), iniw, pwu;
767 const Matrix<T> top =
768 mul(add(md.Qwpz[k], mul(md.Psiw[k], md.Qwmz[k])), iZz, DM);
769 Kwu = vcat(hcat(Kw, top), hcat(zeros<T>(BM.rows(), Kw.cols()), BM));
771 iniw = hcat(add(mul(md.q0[k], md.Qwmp[k]), mul(md.qL[k], md.Qwzp[k])), mul(md.qL[k], DM));
772 pwu = mul(md.q0[k], in.D[k]);
774 iniw = mul(md.pm, md.Qwmp[k]);
775 pwu = mul(md.pm, in.D[k]);
777 const T nrm = T(total(pwu) + total(mul(iniw, ninv(Kwu), Bwu)));
778 pwu = scal(pwu, T(one / nrm));
779 iniw = scal(iniw, T(one / nrm));
783 const std::size_t KN = Kwu.rows(), ext = N * in.msum(k + 1, K);
784 sm.Qspp = zeros<T>(KN + ext, KN + ext);
785 sm.Qspm = zeros<T>(KN + ext, N);
786 sm.Qsmp = zeros<T>(N, KN + ext);
787 sm.Qsmm = add(sD0k, in.D[k]);
788 std::size_t kix = KN;
789 for (std::size_t i = k + 1; i < K; ++i) {
790 setblk(sm.Qspp, kix, kix,
kron(I, in.S[i]));
791 setblk(sm.Qspm, kix, 0,
kron(I, in.s[i]));
792 setblk(sm.Qsmp, 0, kix,
kron(in.D[i], in.sigma[i]));
795 setblk(sm.Qspp, 0, 0, Kwu);
796 setblk(sm.Qspm, 0, 0, Bwu);
797 sm.inis = hcat(iniw, zeros<T>(1, ext));
798 sm.Psis = fluid(in, sm.Qspp, sm.Qspm, sm.Qsmp, sm.Qsmm).Psi;
800 const Matrix<T>& sig = in.sigma[k];
801 const Matrix<T>& Sk = in.S[k];
802 const std::size_t Mk = in.M[k];
804 if (what == Measure::StMoms) {
805 const std::vector<Matrix<T>> Pn = st_moment_chain(sm, n);
806 const Matrix<T> iSk = ninv(Sk);
807 Matrix<T> Pnr = scal(sig, total(mul(sm.inis, Pn[0])));
808 for (std::size_t m = 1; m <= n; ++m) {
809 T w = T(total(mul(sm.inis, Pn[m])) / num_traits<T>::from_double(std::pow(2.0, (
double)m)));
810 if (m % 2 == 1) w = T(-w);
812 add(scal(mul(Pnr, iSk), num_traits<T>::from_int((
int)m)), scal(sig, w));
814 out.push_back(T(total(P) + total(pwu) * factorial<T>(m) * total(mul(sig, mpow(iSk, m)))));
816 }
else if (what == Measure::StDistr) {
817 const std::size_t L = in.erl;
818 for (
const T& t : pts) {
819 const T lambdae = T(num_traits<T>::from_int((
int)L) / t / num_traits<T>::from_int(2));
820 const std::vector<T> terms = erlang_terms(in, sm, lambdae);
822 const Matrix<T> G =
inverse(sub(eye<T>(Mk), scal(Sk, T(one / (num_traits<T>::from_int(2) * lambdae)))));
823 std::vector<T> tail(L + 1, num_traits<T>::from_int(0));
825 for (std::size_t m = 1; m <= L; ++m) {
827 tail[m] = T(one - total(v));
829 T pr = T((total(pwu) + terms[0]) * tail[L]);
830 for (std::size_t m = 1; m + 1 <= L; ++m) pr += T(terms[m] * tail[L - m]);
834 const Matrix<T> IMk = eye<T>(Mk);
835 const Matrix<T> G = ninv(add(
kron(sub(in.sD, in.D[k]), IMk),
kron(I, Sk)));
836 const Matrix<T> W = mul(G,
kron(in.D[k], IMk));
837 const Matrix<T> iW =
inverse(sub(eye<T>(W.rows()), W));
838 const Matrix<T> w =
kron(I, sig);
839 const Matrix<T> omega = mul(G,
kron(I, in.s[k]));
840 if (what == Measure::NcMoms) {
841 const Matrix<T> A = add(sm.Qspp, mul(sm.Psis, sm.Qsmp));
842 const Matrix<T> B = add(sm.Qsmm, mul(sm.Qsmp, sm.Psis));
843 const SylvesterFactor<T> F(A, B);
844 std::vector<Matrix<T>> Psii{sm.Psis};
845 std::vector<Matrix<T>> QP{mul(sm.Qsmp, sm.Psis)};
846 std::vector<Matrix<T>> QLDPn{mul(mul(sm.inis, sm.Psis), w, iW)};
847 for (std::size_t m = 1; m <= n; ++m) {
848 Matrix<T> C = scal(mul(Psii[m - 1], in.D[k]), T(num_traits<T>::from_int((
int)m)));
850 for (std::size_t i = 1; i + 1 <= m; ++i) {
851 bino = T(bino * num_traits<T>::from_int((
int)(m - i + 1)) /
852 num_traits<T>::from_int((
int)i));
853 C = add(C, scal(mul(Psii[i], QP[m - i]), bino));
855 Psii.push_back(F.solve_lyap(C));
856 QP.push_back(mul(sm.Qsmp, Psii.back()));
857 QLDPn.push_back(add(scal(mul(QLDPn[m - 1], iW, W), num_traits<T>::from_int((
int)m)),
858 mul(mul(sm.inis, Psii.back()), w, iW)));
860 for (std::size_t m = 0; m <= n; ++m)
861 QLDPn[m] = mul(add(QLDPn[m], mul(mul(pwu, w), mpow(iW, m + 1), mpow(W, m))), omega);
863 out = random_time_moms(in, k, QLDPn, n);
865 const Matrix<T> Psid = fluid(in, sm.Qspp, sm.Qspm, sm.Qsmp, sD0k).Psi;
866 const Matrix<T> A = add(sm.Qspp, mul(Psid, sm.Qsmp));
867 const Matrix<T> B = add(sD0k, mul(sm.Qsmp, Psid));
868 const SylvesterFactor<T> F(A, B);
869 std::vector<Matrix<T>> P{Psid};
870 std::vector<Matrix<T>> QP{mul(sm.Qsmp, Psid)};
871 Matrix<T> XDn = mul(sm.inis, Psid, w);
872 const Matrix<T> pw = mul(pwu, w);
873 std::vector<Matrix<T>> dql{mul(add(XDn, pw), omega)};
874 Matrix<T> Wn = eye<T>(W.rows());
875 for (std::size_t m = 1; m < n; ++m) {
876 Matrix<T> C = mul(P[m - 1], in.D[k]);
877 for (std::size_t i = 1; i + 1 <= m; ++i) C = add(C, mul(P[i], QP[m - i]));
878 P.push_back(F.solve_lyap(C));
879 QP.push_back(mul(sm.Qsmp, P.back()));
880 XDn = add(mul(XDn, W), mul(sm.inis, P.back(), w));
882 dql.push_back(mul(add(XDn, mul(pw, Wn)), omega));
884 out = random_time_probs(in, k, dql);
891std::vector<std::vector<T>> run(
bool preemptive,
const Mmap<T>& arrival,
892 const std::vector<PhService<T>>& svc, Measure what,
893 std::size_t n,
const std::vector<T>& pts,
894 const PrioQueueOptions& opt) {
895 const char* who = preemptive ?
"mmapph1prpr" :
"mmapph1nppr";
896 const PrioInput<T> in = prepare(arrival, svc, opt, who);
897 if (what == Measure::StDistr) {
898 for (
const T& t : pts)
899 if (!(t > num_traits<T>::from_int(0)))
901 ": sojourn CDF points must be strictly positive (the Erlangization "
904 throw InputError(std::string(who) +
": at least one moment or level is required");
906 std::vector<std::vector<T>> out(in.K);
908 for (std::size_t k = 0; k < in.K; ++k) out[k] = prpr_class(in, k, what, n, pts);
911 throw InputError(
"mmapph1nppr: at least two classes are required (with one class the "
912 "queue is MMAPPH1FCFS)");
913 const NpprModel<T> md = nppr_build(in);
914 for (std::size_t k = 0; k < in.K; ++k) out[k] = nppr_class(in, md, k, what, n, pts);
927 return prio_detail::run(
true, arrival, svc, prio_detail::Measure::NcMoms, n, std::vector<T>(),
opt);
936 return prio_detail::run(
true, arrival, svc, prio_detail::Measure::NcDistr, nmax, std::vector<T>(),
opt);
945 return prio_detail::run(
true, arrival, svc, prio_detail::Measure::StMoms, n, std::vector<T>(),
opt);
952 const std::vector<T>& points,
954 return prio_detail::run(
true, arrival, svc, prio_detail::Measure::StDistr, 0, points,
opt);
963 return prio_detail::run(
false, arrival, svc, prio_detail::Measure::NcMoms, n, std::vector<T>(),
opt);
972 return prio_detail::run(
false, arrival, svc, prio_detail::Measure::NcDistr, nmax, std::vector<T>(),
opt);
981 return prio_detail::run(
false, arrival, svc, prio_detail::Measure::StMoms, n, std::vector<T>(),
opt);
988 const std::vector<T>& points,
990 return prio_detail::run(
false, arrival, svc, prio_detail::Measure::StDistr, 0, points,
opt);
Steady-state distribution of a continuous-time Markov chain.
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.
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...
The MMAP[K]/PH[K]/1 FCFS queue: per-class mean number in system and per-class queue-length distributi...
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 > > mmapph1prpr_ncmoms(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t n, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class moments 1..n of the number of jobs, MMAP[K]/PH[K]/1 preemptive resume priority.
std::vector< std::vector< T > > mmapph1prpr_stdistr(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, const std::vector< T > &points, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class sojourn-time CDF at the (strictly positive) points, preemptive resume priority.
std::vector< std::vector< T > > mmapph1nppr_stdistr(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, const std::vector< T > &points, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class sojourn-time CDF at the (strictly positive) points, non-preemptive priority.
std::vector< std::vector< T > > mmapph1nppr_ncdistr(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t nmax, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class P(number of jobs = 0..nmax-1), non-preemptive priority.
Matrix< T > qbd_R_logred(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, unsigned iter_max, const T &tol)
R by logarithmic reduction (qbd_R_logred.m).
std::vector< std::vector< T > > mmapph1prpr_stmoms(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t n, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class sojourn-time moments 1..n, preemptive resume priority.
std::vector< std::vector< T > > mmapph1prpr_ncdistr(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t nmax, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class P(number of jobs = 0..nmax-1), preemptive resume priority.
std::vector< std::vector< T > > mmapph1nppr_stmoms(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t n, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class sojourn-time moments 1..n, non-preemptive priority.
std::vector< std::vector< T > > mmapph1nppr_ncmoms(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, std::size_t n, const PrioQueueOptions &opt=PrioQueueOptions())
Per-class moments 1..n of the number of jobs, MMAP[K]/PH[K]/1 non-preemptive priority.
std::vector< T > ctmc_solve(const Matrix< T > &Qin)
Steady-state distribution of a continuous-time Markov chain.
@ Bk
Birman-Kogan saddle point with bottleneck detection.
Conservation laws of a layered queueing network, enumerated from its structure.
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.
Matrix< T > expm(const Matrix< T > &A)
Matrix exponential exp(A).
Number-type abstraction for the templated API port.
Quasi-birth-death processes: the rate matrix R, the fundamental matrix G, the caudal characteristic,...
An MMAP: the underlying MAP plus the per-class arrival matrices.
One class's phase-type service law, He's (sigma_k, S_k).
Options shared by both priority analyzers (the BUTools 'erlMaxOrder' and 'prec').
std::size_t erl_max_order
Erlang order of the sojourn-time CDF inversion.
double precision
tolerance of the fluid and QBD fundamental matrices
The Sylvester equation A X + X B = C, and MATLAB's lyap(A,B,C).