5#ifndef LINE_API_QSYS_QSYS_BMAPPHNN_RETRIAL_H
6#define LINE_API_QSYS_QSYS_BMAPPHNN_RETRIAL_H
160inline std::vector<std::vector<int>> retrial_compositions(
int n,
int M) {
161 std::vector<std::vector<int>> out;
162 if (M <= 0)
return out;
164 out.push_back(std::vector<int>(1, n));
167 for (
int m1 = n; m1 >= 0; --m1) {
168 const std::vector<std::vector<int>> sub = retrial_compositions(n - m1, M - 1);
169 for (std::size_t s = 0; s < sub.
size(); ++s) {
170 std::vector<int> row;
171 row.reserve(
static_cast<std::size_t
>(M));
173 row.insert(row.end(), sub[s].begin(), sub[s].end());
181inline int retrial_find(
const std::vector<std::vector<int>>& comps,
const std::vector<int>& key) {
182 for (std::size_t j = 0; j < comps.size(); ++j)
183 if (comps[j] == key)
return static_cast<int>(j);
190 std::vector<Matrix<T>> D;
194 int M = 0, N = 0, V = 0, K = 0;
196 std::vector<std::size_t> Tn;
199 std::vector<std::vector<std::vector<int>>> comps;
204std::size_t retrial_offset(
const RetrialCtx<T>& c,
int n) {
206 for (
int i = 0; i < n; ++i) off += c.Tn[
static_cast<std::size_t
>(i)];
212Matrix<T> retrial_L(
const RetrialCtx<T>& c,
int n) {
213 const T zero = num_traits<T>::from_int(0);
215 Matrix<T> L(c.Tn[
static_cast<std::size_t
>(n)], c.Tn[
static_cast<std::size_t
>(n - 1)], zero);
216 const std::vector<std::vector<int>>& cn = c.comps[
static_cast<std::size_t
>(n)];
217 const std::vector<std::vector<int>>& cm = c.comps[
static_cast<std::size_t
>(n - 1)];
218 for (std::size_t i = 0; i < cn.size(); ++i)
219 for (
int l = 0; l < c.M; ++l) {
220 if (cn[i][
static_cast<std::size_t
>(l)] <= 0)
continue;
221 std::vector<int> mp = cn[i];
222 --mp[
static_cast<std::size_t
>(l)];
223 const int j = retrial_find(cm, mp);
225 L(i,
static_cast<std::size_t
>(j)) +=
226 num_traits<T>::from_int(cn[i][
static_cast<std::size_t
>(l)]) *
227 c.S0[
static_cast<std::size_t
>(l)];
234Matrix<T> retrial_A(
const RetrialCtx<T>& c,
int n) {
235 const T zero = num_traits<T>::from_int(0);
236 if (n == 0)
return Matrix<T>(1, 1, zero);
237 Matrix<T> A(c.Tn[
static_cast<std::size_t
>(n)], c.Tn[
static_cast<std::size_t
>(n)], zero);
238 const std::vector<std::vector<int>>& cn = c.comps[
static_cast<std::size_t
>(n)];
239 for (std::size_t i = 0; i < cn.size(); ++i)
240 for (
int l = 0; l < c.M; ++l) {
241 if (cn[i][
static_cast<std::size_t
>(l)] <= 0)
continue;
242 for (
int lp = 0; lp < c.M; ++lp) {
243 if (lp == l)
continue;
244 if (!(c.S(
static_cast<std::size_t
>(l),
static_cast<std::size_t
>(lp)) > zero))
246 std::vector<int> mp = cn[i];
247 --mp[
static_cast<std::size_t
>(l)];
248 ++mp[
static_cast<std::size_t
>(lp)];
249 const int j = retrial_find(cn, mp);
251 A(i,
static_cast<std::size_t
>(j)) +=
252 num_traits<T>::from_int(cn[i][
static_cast<std::size_t
>(l)]) *
253 c.S(
static_cast<std::size_t
>(l),
static_cast<std::size_t
>(lp));
261Matrix<T> retrial_P(
const RetrialCtx<T>& c,
int n) {
262 const T zero = num_traits<T>::from_int(0);
264 Matrix<T>
P(c.Tn[
static_cast<std::size_t
>(n)], c.Tn[
static_cast<std::size_t
>(n + 1)], zero);
265 const std::vector<std::vector<int>>& cn = c.comps[
static_cast<std::size_t
>(n)];
266 const std::vector<std::vector<int>>& cp = c.comps[
static_cast<std::size_t
>(n + 1)];
267 for (std::size_t i = 0; i < cn.size(); ++i)
268 for (
int l = 0; l < c.M; ++l) {
269 if (!(c.beta[
static_cast<std::size_t
>(l)] > zero))
continue;
270 std::vector<int> mp = cn[i];
271 ++mp[
static_cast<std::size_t
>(l)];
272 const int j = retrial_find(cp, mp);
274 P(i,
static_cast<std::size_t
>(j)) += c.beta[
static_cast<std::size_t
>(l)];
281Matrix<T> retrial_Delta(
const RetrialCtx<T>& c,
int n) {
282 const T zero = num_traits<T>::from_int(0);
283 if (n == 0)
return Matrix<T>(1, 1, zero);
284 Matrix<T> D(c.Tn[
static_cast<std::size_t
>(n)], c.Tn[
static_cast<std::size_t
>(n)], zero);
285 const std::vector<std::vector<int>>& cn = c.comps[
static_cast<std::size_t
>(n)];
286 for (std::size_t i = 0; i < cn.size(); ++i) {
288 for (
int l = 0; l < c.M; ++l)
289 total += num_traits<T>::from_int(cn[i][
static_cast<std::size_t
>(l)]) *
290 T(-c.S(
static_cast<std::size_t
>(l),
static_cast<std::size_t
>(l)));
298Matrix<T> retrial_Gamma(
const RetrialCtx<T>& c,
int nu) {
299 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
300 Matrix<T> G(c.d, c.d, zero);
302 for (
int n = 0; n <= c.N; ++n) {
303 const std::size_t width = c.Tn[
static_cast<std::size_t
>(n)];
304 if (
static_cast<long>(n) > c.R[
static_cast<std::size_t
>(nu)])
305 for (std::size_t t = 0; t < width; ++t) G(off + t, off + t) = one;
313T retrial_G_scalar(
const RetrialCtx<T>& c,
int n,
int nu,
int nuPrime) {
314 const T zero = num_traits<T>::from_int(0);
315 if (n <= c.N - c.K)
return zero;
317 for (
int k = c.N - n + 1; k <= c.K; ++k)
319 total += c.D[
static_cast<std::size_t
>(k)](
static_cast<std::size_t
>(nu),
320 static_cast<std::size_t
>(nuPrime));
321 return T(c.p * total);
329Matrix<T> retrial_Pprod(
const RetrialCtx<T>& c,
const std::vector<Matrix<T>>& P,
int n,
int upto) {
330 Matrix<T> prod =
eye<T>(c.Tn[
static_cast<std::size_t
>(n)]);
331 for (
int j = n; j < upto; ++j)
332 if (j < c.N) prod =
matmul(prod, P[
static_cast<std::size_t
>(j)]);
338Matrix<T> retrial_B(
const RetrialCtx<T>& c,
const std::vector<Matrix<T>>& L,
339 const std::vector<Matrix<T>>& A,
const std::vector<Matrix<T>>& P,
340 const std::vector<Matrix<T>>& Delta,
int nu) {
341 const T zero = num_traits<T>::from_int(0);
342 Matrix<T> B(c.d, c.d, zero);
343 for (
int n = 0; n <= c.N; ++n) {
344 const std::size_t rs = retrial_offset(c, n);
345 const std::size_t w = c.Tn[
static_cast<std::size_t
>(n)];
346 const T g = retrial_G_scalar(c, n, nu, nu);
350 for (std::size_t a = 0; a < w; ++a)
351 for (std::size_t b = 0; b < w; ++b)
352 B(rs + a, rs + b) = A[
static_cast<std::size_t
>(n)](a, b) +
353 Delta[
static_cast<std::size_t
>(n)](a, b) +
357 const std::size_t cs = retrial_offset(c, n - 1);
358 for (std::size_t a = 0; a < w; ++a)
359 for (std::size_t b = 0; b < c.Tn[
static_cast<std::size_t
>(n - 1)]; ++b)
360 B(rs + a, cs + b) = L[
static_cast<std::size_t
>(n)](a, b);
362 for (
int k = 1; k <= c.K; ++k) {
363 if (n + k > c.N)
continue;
364 const std::size_t cs = retrial_offset(c, n + k);
365 const T dk = c.D[
static_cast<std::size_t
>(k)](
static_cast<std::size_t
>(nu),
366 static_cast<std::size_t
>(nu));
367 const Matrix<T> pp = retrial_Pprod(c, P, n, n + k);
368 for (std::size_t a = 0; a < w; ++a)
369 for (std::size_t b = 0; b < c.Tn[
static_cast<std::size_t
>(n + k)]; ++b)
370 B(rs + a, cs + b) = dk * pp(a, b);
378Matrix<T> retrial_Bbar(
const RetrialCtx<T>& c,
const std::vector<Matrix<T>>& P,
int nu) {
379 const T zero = num_traits<T>::from_int(0);
380 Matrix<T> B(c.d, c.d, zero);
381 const long cap = c.R[
static_cast<std::size_t
>(nu)] <
static_cast<long>(c.N - 1)
382 ? c.R[
static_cast<std::size_t
>(nu)]
383 : static_cast<long>(c.N - 1);
384 for (
long n = 0; n <= cap; ++n) {
385 const std::size_t rs = retrial_offset(c,
static_cast<int>(n));
386 const std::size_t cs = retrial_offset(c,
static_cast<int>(n) + 1);
387 const Matrix<T>& Pn =
P[
static_cast<std::size_t
>(n)];
388 for (std::size_t a = 0; a < Pn.rows(); ++a)
389 for (std::size_t b = 0; b < Pn.cols(); ++b) B(rs + a, cs + b) = Pn(a, b);
396Matrix<T> retrial_Btilde(
const RetrialCtx<T>& c,
const std::vector<Matrix<T>>& P,
int nu,
398 const T zero = num_traits<T>::from_int(0);
399 Matrix<T> B(c.d, c.d, zero);
400 for (
int n = 0; n <= c.N; ++n) {
401 const std::size_t rs = retrial_offset(c, n);
402 const std::size_t w = c.Tn[
static_cast<std::size_t
>(n)];
403 const T g = retrial_G_scalar(c, n, nu, nuPrime);
404 for (std::size_t a = 0; a < w; ++a) B(rs + a, rs + a) = g;
405 for (
int k = 1; k <= c.K; ++k) {
406 if (n + k > c.N)
continue;
407 const std::size_t cs = retrial_offset(c, n + k);
408 const T dk = c.D[
static_cast<std::size_t
>(k)](
static_cast<std::size_t
>(nu),
409 static_cast<std::size_t
>(nuPrime));
410 const Matrix<T> pp = retrial_Pprod(c, P, n, n + k);
411 for (std::size_t a = 0; a < w; ++a)
412 for (std::size_t b = 0; b < c.Tn[
static_cast<std::size_t
>(n + k)]; ++b)
413 B(rs + a, cs + b) = dk * pp(a, b);
425Matrix<T> retrial_C(
const RetrialCtx<T>& c,
const std::vector<Matrix<T>>& P,
int n,
int k,
int nu,
427 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
428 if (n < c.N - c.K + k)
return Matrix<T>();
430 const int batch = c.N - n + k;
431 if (batch < 1 || batch > c.K)
return Matrix<T>();
432 const T db = c.D[
static_cast<std::size_t
>(batch)](
static_cast<std::size_t
>(nu),
433 static_cast<std::size_t
>(nuPrime));
434 Matrix<T> pp = retrial_Pprod(c, P, n, c.N);
435 for (std::size_t a = 0; a < pp.rows(); ++a)
436 for (std::size_t b = 0; b < pp.cols(); ++b) pp(a, b) = T(one - c.p) * db * pp(a, b);
439 if (k < 1 || k > c.K)
return Matrix<T>();
440 const T dk = c.D[
static_cast<std::size_t
>(k)](
static_cast<std::size_t
>(nu),
441 static_cast<std::size_t
>(nuPrime));
442 Matrix<T> out(c.Tn[
static_cast<std::size_t
>(c.N)], c.Tn[
static_cast<std::size_t
>(c.N)], zero);
443 for (std::size_t a = 0; a < out.rows(); ++a) out(a, a) = T(one - c.p) * dk;
465 const std::vector<T>& beta,
const Matrix<T>& S,
466 int N,
const T& alpha,
const T& gamma,
const T& p,
467 const std::vector<long>& R,
470 if (D.size() < 2)
throw InputError(
"qsys_bmapphnn_retrial: the BMAP needs D0 and at least D1");
471 if (N < 1)
throw InputError(
"qsys_bmapphnn_retrial: at least one server is required");
472 const int V =
static_cast<int>(D[0].rows());
473 if (V < 1)
throw InputError(
"qsys_bmapphnn_retrial: empty BMAP");
474 for (std::size_t k = 0; k < D.size(); ++k)
475 if (D[k].rows() !=
static_cast<std::size_t
>(V) || D[k].cols() !=
static_cast<std::size_t
>(V))
476 throw InputError(
"qsys_bmapphnn_retrial: every BMAP matrix must be V x V");
477 const int M =
static_cast<int>(S.
rows());
478 if (M < 1 || S.
cols() !=
static_cast<std::size_t
>(M) ||
479 beta.size() !=
static_cast<std::size_t
>(M))
480 throw InputError(
"qsys_bmapphnn_retrial: the PH service is malformed");
481 if (R.empty())
throw InputError(
"qsys_bmapphnn_retrial: no admission threshold given");
487 const double sqrtFineTol = std::sqrt(fineTol);
488 Matrix<T> DsumV(
static_cast<std::size_t
>(V),
static_cast<std::size_t
>(V), zero);
489 for (std::size_t k = 0; k < D.size(); ++k)
490 for (std::size_t i = 0; i < D[k].rows(); ++i)
491 for (std::size_t j = 0; j < D[k].cols(); ++j) {
493 if (!std::isfinite(v))
494 throw InputError(
"qsys_bmapphnn_retrial: BMAP matrix D{" +
495 std::to_string(k + 1) +
496 "} contains NaN or Inf entries. The arrival process is "
497 "disabled or not phase-type representable.");
498 if (k >= 1 && v < -fineTol)
499 throw InputError(
"qsys_bmapphnn_retrial: BMAP arrival matrix D{" +
500 std::to_string(k + 1) +
"} must be non-negative.");
501 DsumV(i, j) += D[k](i, j);
503 for (std::size_t i = 0; i < D[0].rows(); ++i) {
505 throw InputError(
"qsys_bmapphnn_retrial: BMAP matrix D0 must have non-positive "
506 "diagonal entries.");
508 for (std::size_t j = 0; j < DsumV.
cols(); ++j) rs += DsumV(i, j);
510 throw InputError(
"qsys_bmapphnn_retrial: BMAP matrices are inconsistent: sum_k D_k "
511 "must have zero row sums.");
514 for (std::size_t i = 0; i < beta.size(); ++i) {
516 if (!std::isfinite(v))
517 throw InputError(
"qsys_bmapphnn_retrial: Phase-type service vector beta is empty or "
518 "contains NaN/Inf. The service distribution is disabled or not "
519 "phase-type representable.");
521 throw InputError(
"qsys_bmapphnn_retrial: Phase-type service vector beta must be "
522 "non-negative and sum to one.");
526 throw InputError(
"qsys_bmapphnn_retrial: Phase-type service vector beta must be "
527 "non-negative and sum to one.");
528 for (std::size_t i = 0; i < S.
rows(); ++i) {
530 for (std::size_t j = 0; j < S.
cols(); ++j) {
532 throw InputError(
"qsys_bmapphnn_retrial: Phase-type service subgenerator S "
533 "contains NaN or Inf entries. The service distribution is "
534 "disabled or not phase-type representable.");
537 if (!(S(i, i) < zero))
538 throw InputError(
"qsys_bmapphnn_retrial: Phase-type service subgenerator S must "
539 "have strictly negative diagonal entries.");
541 throw InputError(
"qsys_bmapphnn_retrial: Phase-type service subgenerator S must "
542 "have non-negative exit rates.");
546 if (!std::isfinite(ad) || ad < 0.0)
547 throw InputError(
"qsys_bmapphnn_retrial: Retrial rate alpha must be a finite "
548 "non-negative scalar.");
549 if (!std::isfinite(gd) || gd < 0.0)
550 throw InputError(
"qsys_bmapphnn_retrial: Orbit impatience rate gamma must be a finite "
551 "non-negative scalar.");
552 if (!std::isfinite(pd) || pd < 0.0 || pd > 1.0)
553 throw InputError(
"qsys_bmapphnn_retrial: Batch rejection probability p must lie in "
555 for (std::size_t i = 0; i < R.size(); ++i)
556 if (R[i] < 0 || R[i] >
static_cast<long>(N))
557 throw InputError(
"qsys_bmapphnn_retrial: Admission threshold R must lie in [0," +
558 std::to_string(N) +
"].");
561 detail::RetrialCtx<T> c;
568 c.K =
static_cast<int>(D.size()) - 1;
572 c.R.assign(
static_cast<std::size_t
>(V), R[0]);
574 if (R.size() !=
static_cast<std::size_t
>(V))
575 throw InputError(
"qsys_bmapphnn_retrial: R must be a scalar or one entry per BMAP "
581 c.S0.assign(
static_cast<std::size_t
>(M), zero);
582 for (
int i = 0; i < M; ++i) {
584 for (
int j = 0; j < M; ++j)
585 s += S(
static_cast<std::size_t
>(i),
static_cast<std::size_t
>(j));
586 c.S0[
static_cast<std::size_t
>(i)] = -s;
590 c.Tn.assign(
static_cast<std::size_t
>(N) + 1, 0);
591 c.comps.resize(
static_cast<std::size_t
>(N) + 1);
593 for (
int n = 0; n <= N; ++n) {
594 c.comps[
static_cast<std::size_t
>(n)] = detail::retrial_compositions(n, M);
595 c.Tn[
static_cast<std::size_t
>(n)] = c.comps[
static_cast<std::size_t
>(n)].size();
596 c.d += c.Tn[
static_cast<std::size_t
>(n)];
600 Matrix<T> Dsum(
static_cast<std::size_t
>(V),
static_cast<std::size_t
>(V), zero);
601 Matrix<T> DkSum(
static_cast<std::size_t
>(V),
static_cast<std::size_t
>(V), zero);
602 for (std::size_t k = 0; k < D.size(); ++k)
603 for (std::size_t i = 0; i < Dsum.
rows(); ++i)
604 for (std::size_t j = 0; j < Dsum.
cols(); ++j) {
605 Dsum(i, j) += D[k](i, j);
611 for (std::size_t j = 0; j < A.
cols(); ++j) A(A.
rows() - 1, j) = one;
612 std::vector<T> rhs(A.
rows(), zero);
613 rhs[A.
rows() - 1] = one;
614 const std::vector<T> theta =
solve(A, rhs);
616 for (std::size_t i = 0; i < theta.size(); ++i)
617 for (std::size_t j = 0; j < DkSum.
cols(); ++j) lambda += theta[i] * DkSum(i, j);
620 for (std::size_t i = 0; i < negS.
rows(); ++i)
621 for (std::size_t j = 0; j < negS.
cols(); ++j) negS(i, j) = -negS(i, j);
624 for (
int i = 0; i < M; ++i) b1 += beta[static_cast<std::size_t>(i)] * tau[
static_cast<std::size_t
>(i)];
625 if (!(b1 > zero))
throw InputError(
"qsys_bmapphnn_retrial: the mean service time must be positive");
627 const std::size_t Vd =
static_cast<std::size_t
>(V) * c.d;
631 if (
static_cast<double>(Vd) >
opt.maxBlockSize)
632 throw InputError(
"qsys_bmapphnn_retrial: Per-level block size V*d = " + std::to_string(Vd) +
633 " exceeds MaxBlockSize = " +
634 std::to_string(
static_cast<long long>(std::llround(
opt.maxBlockSize))) +
635 ". The service distribution has " + std::to_string(M) +
636 " phases and the station has " + std::to_string(N) +
637 " servers, which yields " + std::to_string(c.d) +
638 " service configurations. Reduce the phase-type order (e.g. fit the "
639 "service distribution with fewer phases), reduce the number of servers, "
640 "or raise maxBlockSize if the memory cost is acceptable.");
644 std::vector<
Matrix<T>> L(
static_cast<std::size_t
>(N) + 1), Am(
static_cast<std::size_t
>(N) + 1),
645 P(
static_cast<std::size_t
>(N) + 1), Delta(
static_cast<std::size_t
>(N) + 1);
646 for (
int n = 0; n <= N; ++n) {
647 L[
static_cast<std::size_t
>(n)] = detail::retrial_L(c, n);
648 Am[
static_cast<std::size_t
>(n)] = detail::retrial_A(c, n);
649 P[
static_cast<std::size_t
>(n)] = detail::retrial_P(c, n);
650 Delta[
static_cast<std::size_t
>(n)] = detail::retrial_Delta(c, n);
652 std::vector<Matrix<T>> B(
static_cast<std::size_t
>(V)), Bbar(
static_cast<std::size_t
>(V)),
653 Gam(
static_cast<std::size_t
>(V));
654 for (
int nu = 0; nu < V; ++nu) {
655 B[
static_cast<std::size_t
>(nu)] = detail::retrial_B(c, L, Am, P, Delta, nu);
656 Bbar[
static_cast<std::size_t
>(nu)] = detail::retrial_Bbar(c, P, nu);
657 Gam[
static_cast<std::size_t
>(nu)] = detail::retrial_Gamma(c, nu);
659 std::vector<std::vector<Matrix<T>>> Btilde(
660 static_cast<std::size_t
>(V), std::vector<
Matrix<T>>(
static_cast<std::size_t
>(V)));
661 for (
int nu = 0; nu < V; ++nu)
662 for (
int nup = 0; nup < V; ++nup)
663 if (nu != nup) Btilde[
static_cast<std::size_t
>(nu)][
static_cast<std::size_t
>(nup)] =
664 detail::retrial_Btilde(c, P, nu, nup);
666 bool clipped =
false;
672 auto solveAtLevel = [&](std::size_t truncLevel) ->
Matrix<T> {
673 const std::size_t total = (truncLevel + 1) * Vd;
676 for (std::size_t i = 0; i <= truncLevel; ++i) {
680 for (
int nu = 0; nu < V; ++nu) {
681 const std::size_t rs = i * Vd +
static_cast<std::size_t
>(nu) * c.d;
682 for (
int nup = 0; nup < V; ++nup) {
683 const std::size_t cs = i * Vd +
static_cast<std::size_t
>(nup) * c.d;
684 const T d0 = D[0](
static_cast<std::size_t
>(nu),
static_cast<std::size_t
>(nup));
686 for (std::size_t a = 0; a < c.d; ++a)
687 for (std::size_t b = 0; b < c.d; ++b) {
688 T v = B[
static_cast<std::size_t
>(nu)](a, b) +
689 wT * alpha * Gam[
static_cast<std::size_t
>(nu)](a, b);
690 if (a == b) v += d0 - iT * gamma - wT * alpha;
691 Q(rs + a, cs + b) = v;
695 Btilde[
static_cast<std::size_t
>(nu)][
static_cast<std::size_t
>(nup)];
696 for (std::size_t a = 0; a < c.d; ++a)
697 for (std::size_t b = 0; b < c.d; ++b)
698 Q(rs + a, cs + b) = bt(a, b) + (a == b ? d0 : zero);
704 for (
int nu = 0; nu < V; ++nu) {
705 const std::size_t rs = i * Vd +
static_cast<std::size_t
>(nu) * c.d;
706 const std::size_t cs = (i - 1) * Vd +
static_cast<std::size_t
>(nu) * c.d;
707 for (std::size_t a = 0; a < c.d; ++a)
708 for (std::size_t b = 0; b < c.d; ++b) {
709 T v = wT * alpha * Bbar[
static_cast<std::size_t
>(nu)](a, b);
710 if (a == b) v += iT * gamma;
711 Q(rs + a, cs + b) = v;
716 for (
int k = 1; k <= c.K; ++k) {
717 const std::size_t j = i +
static_cast<std::size_t
>(k);
718 if (j > truncLevel)
break;
719 for (
int nu = 0; nu < V; ++nu) {
720 const std::size_t rs = i * Vd +
static_cast<std::size_t
>(nu) * c.d;
721 for (
int nup = 0; nup < V; ++nup) {
722 const std::size_t cs = j * Vd +
static_cast<std::size_t
>(nup) * c.d;
723 const std::size_t ncol = detail::retrial_offset(c, N);
724 for (
int n = 0; n <= N; ++n) {
725 const Matrix<T> Cnk = detail::retrial_C(c, P, n, k, nu, nup);
726 if (Cnk.
rows() == 0)
continue;
727 if (Cnk.
cols() != c.Tn[
static_cast<std::size_t
>(N)])
continue;
728 const std::size_t nrow = detail::retrial_offset(c, n);
729 for (std::size_t a = 0; a < Cnk.
rows(); ++a)
730 for (std::size_t b = 0; b < Cnk.
cols(); ++b)
731 Q(rs + nrow + a, cs + ncol + b) += Cnk(a, b);
739 for (std::size_t i = 0; i < total; ++i) {
741 for (std::size_t j = 0; j < total; ++j) s += Q(i, j);
742 Q(i, i) = Q(i, i) - s;
746 for (std::size_t i = 0; i < total; ++i) Q(i, total - 1) = one;
748 std::vector<T> rhs2(total, zero);
749 rhs2[total - 1] = one;
750 std::vector<T> pi =
solve(QT, rhs2);
756 for (std::size_t i = 0; i < total; ++i) {
757 if (pi[i] < warnTol) clipped =
true;
758 if (pi[i] < zero) pi[i] = zero;
761 if (!(mass > zero))
throw NumericError(
ErrorKind{
"Degenerate"},
"qsys_bmapphnn_retrial: the solution has no mass");
763 for (std::size_t i = 0; i <= truncLevel; ++i)
764 for (std::size_t j = 0; j < Vd; ++j) pim(i, j) = pi[i * Vd + j] / mass;
770 auto orbitTruncationError = [&](
const Matrix<T>& pim, std::size_t truncLevel) -> T {
771 T lorbit = zero, top = zero;
772 for (std::size_t i = 0; i <= truncLevel; ++i) {
774 for (std::size_t j = 0; j < Vd; ++j) lv += pim(i, j);
776 if (i == truncLevel) top = lv;
779 const T denom = lorbit > realmin ? lorbit : realmin;
785 std::size_t truncLevel =
opt.maxLevel;
788 bool converged =
true;
789 if (truncLevel > 0) {
790 pim = solveAtLevel(truncLevel);
791 truncError = orbitTruncationError(pim, truncLevel);
794 const double capped = rho < 0.99 ? rho : 0.99;
795 const double lv = std::ceil(50.0 / (1.0 - capped));
796 truncLevel = lv > 100.0 ?
static_cast<std::size_t
>(lv) :
static_cast<std::size_t
>(100);
799 pim = solveAtLevel(truncLevel);
800 truncError = orbitTruncationError(pim, truncLevel);
805 const std::size_t nextLevel = 2 * truncLevel;
806 if (
static_cast<double>(nextLevel + 1) *
static_cast<double>(Vd) >
opt.maxDim)
break;
807 truncLevel = nextLevel;
819 for (std::size_t i = 1; i <= truncLevel; ++i) {
821 for (std::size_t j = 0; j < Vd; ++j) lv += r.
pi(i, j);
826 for (std::size_t i = 0; i <= truncLevel; ++i)
827 for (
int nu = 0; nu < V; ++nu) {
828 const std::size_t base =
static_cast<std::size_t
>(nu) * c.d;
829 for (
int n = 0; n <= N; ++n) {
830 const std::size_t off = base + detail::retrial_offset(c, n);
831 for (std::size_t t = 0; t < c.Tn[
static_cast<std::size_t
>(n)]; ++t)
839 for (
int nu = 0; nu < V; ++nu) r.
P_empty_system += r.
pi(0,
static_cast<std::size_t
>(nu) * c.d);
841 for (std::size_t j = 0; j < Vd; ++j) r.
topLevelMass += r.
pi(truncLevel, j);
854 const std::vector<T>& beta,
const Matrix<T>& S,
855 int N,
const T& alpha,
const T& gamma,
const T& p,
NumericError(const std::string &what)
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
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.
RetrialPolicy
Retrial admission policy (RetrialPolicy in MATLAB).
@ CONSTANT
the orbit retries at rate alpha whenever it is non-empty
@ LINEAR
each orbiting customer retries at rate alpha (orbit rate i*alpha)
BmapPhNnRetrialResult< T > qsys_bmapphnn_retrial(const std::vector< Matrix< T > > &D, const std::vector< T > &beta, const Matrix< T > &S, int N, const T &alpha, const T &gamma, const T &p, const std::vector< long > &R, const BmapPhNnRetrialOptions &opt)
The BMAP/PH/N/N bufferless retrial queue.
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.
std::vector< T > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
std::vector< T > ones(std::size_t n)
Column vector of ones, the ubiquitous e in MAP algebra.
Matrix< T > eye(std::size_t n)
Identity of order n.
Number-type abstraction for the templated API port.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
static constexpr double FineTol
Options of qsys_bmapphnn_retrial (the name-value pairs of the .m file).
double tolerance
'Tolerance' (parsed by the reference and, like there, not consumed).
std::size_t maxLevel
Fixed orbit truncation level ('MaxLevel'); 0 selects the reference's adaptive choice,...
double maxDim
'MaxDim': cap on the total generator dimension of the refinement.
double tailTolerance
'TailTolerance': relative orbit-truncation error target.
RetrialPolicy retrialPolicy
'RetrialPolicy'.
double maxBlockSize
'MaxBlockSize': cap on the per-level block size V*d; exceeding it is an error.
Result of qsys_bmapphnn_retrial.
bool converged
The adaptive refinement reached tailTolerance (always true for a fixed maxLevel).
T P_empty_orbit
probability that the orbit is empty
T L_orbit
mean number of customers in orbit
std::size_t truncLevel
truncation level used
T utilization
N_server / N.
T throughput
N_server / b1.
T P_idle
probability that every server is idle
T truncError
relative orbit-truncation error estimate at truncLevel
Matrix< T > pi
stationary law, (truncLevel + 1) x (V d)
T topLevelMass
mass at the highest retained level; see defect 2
T P_empty_system
probability that the system is empty
T L_system
L_orbit + N_server.
T N_server
mean number of busy servers
bool clipped
a negative probability had to be clipped to zero