5#ifndef LINE_API_QSYS_QSYS_MG1_PS_H
6#define LINE_API_QSYS_QSYS_MG1_PS_H
88using Cplx = std::complex<double>;
92 std::vector<double>
x;
93 std::vector<double>
s;
94 std::vector<double>
t;
97 std::function<double(
double)>
pdf;
104 double m2 = std::numeric_limits<double>::quiet_NaN();
109 std::vector<double>
x,
s,
t;
116 double m2Uncond = std::numeric_limits<double>::quiet_NaN();
117 double varUncond = std::numeric_limits<double>::quiet_NaN();
122namespace mg1psdetail {
125inline std::vector<double> conv(
const std::vector<double>& a,
const std::vector<double>& b) {
126 if (a.empty() || b.empty())
return std::vector<double>();
127 std::vector<double> c(a.size() + b.size() - 1, 0.0);
128 for (std::size_t i = 0; i < a.size(); ++i)
129 for (std::size_t j = 0; j < b.size(); ++j) c[i + j] += a[i] * b[j];
134template <
class C,
class U>
135U polyval(
const std::vector<C>& p,
const U& z) {
137 for (std::size_t i = 0; i < p.size(); ++i) v = v * z + U(p[i]);
142inline std::vector<double> polyder(
const std::vector<double>& p) {
143 if (p.size() <= 1)
return std::vector<double>(1, 0.0);
144 const std::size_t n = p.size() - 1;
145 std::vector<double> d(n, 0.0);
146 for (std::size_t i = 0; i < n; ++i) d[i] = p[i] * static_cast<double>(n - i);
157inline std::vector<Cplx> poly_roots(
const std::vector<double>& p_in) {
158 std::vector<double> p = p_in;
160 std::size_t lead = 0;
161 while (lead + 1 < p.size() && p[lead] == 0.0) ++lead;
162 p.erase(p.begin(), p.begin() +
static_cast<long>(lead));
163 if (p.size() <= 1)
return std::vector<Cplx>();
164 const std::size_t n = p.size() - 1;
165 std::vector<double> mon(p.size());
166 for (std::size_t i = 0; i < p.size(); ++i) mon[i] = p[i] / p[0];
170 std::vector<Cplx> r(n);
171 const Cplx seed(0.4, 0.9);
173 for (std::size_t i = 0; i < n; ++i) {
177 for (
int it = 0; it < 1000; ++it) {
179 for (std::size_t i = 0; i < n; ++i) {
181 for (std::size_t j = 0; j < n; ++j)
182 if (j != i) den *= (r[i] - r[j]);
183 if (std::abs(den) < 1e-300)
continue;
184 const Cplx step = polyval(mon, r[i]) / den;
186 move = std::max(move, std::abs(step));
188 if (move < 1e-14)
break;
202inline Matrix<Cplx> expm_cplx(
const Matrix<Cplx>& A) {
203 const std::size_t n = A.rows();
205 for (std::size_t i = 0; i < n; ++i) {
207 for (std::size_t j = 0; j < n; ++j) row += std::abs(A(i, j));
208 nrm = std::max(nrm, row);
215 const double sc = std::pow(2.0, -
static_cast<double>(sq));
216 Matrix<Cplx> B(n, n,
Cplx(0.0, 0.0)), E(n, n,
Cplx(0.0, 0.0)), Tk(n, n,
Cplx(0.0, 0.0));
217 for (std::size_t i = 0; i < n; ++i) {
218 E(i, i) =
Cplx(1.0, 0.0);
219 Tk(i, i) =
Cplx(1.0, 0.0);
220 for (std::size_t j = 0; j < n; ++j) B(i, j) = A(i, j) * sc;
222 for (
int k = 1; k <= 30; ++k) {
223 Matrix<Cplx>
P(n, n,
Cplx(0.0, 0.0));
224 for (std::size_t i = 0; i < n; ++i)
225 for (std::size_t j = 0; j < n; ++j) {
227 for (std::size_t l = 0; l < n; ++l) acc += Tk(i, l) * B(l, j);
228 P(i, j) = acc /
static_cast<double>(k);
231 for (std::size_t i = 0; i < n; ++i)
232 for (std::size_t j = 0; j < n; ++j) E(i, j) += Tk(i, j);
234 for (
int t = 0; t < sq; ++t) {
235 Matrix<Cplx>
P(n, n,
Cplx(0.0, 0.0));
236 for (std::size_t i = 0; i < n; ++i)
237 for (std::size_t j = 0; j < n; ++j) {
239 for (std::size_t l = 0; l < n; ++l) acc += E(i, l) * E(l, j);
252Cplx ilt(Fn fun,
double t, std::size_t nterms) {
253 const long ne =
static_cast<long>((nterms - 1) / 2);
254 std::vector<double> eta(
static_cast<std::size_t
>(2 * ne + 1), 0.0);
256 for (
long i = 1; i <= ne; ++i) eta[static_cast<std::size_t>(i)] = 1.0;
257 eta[
static_cast<std::size_t
>(2 * ne)] = std::pow(2.0, -
static_cast<double>(ne));
258 for (
long k = 1; k <= ne - 1; ++k)
259 eta[
static_cast<std::size_t
>(2 * ne - k)] =
260 eta[
static_cast<std::size_t
>(2 * ne - k + 1)] +
261 std::exp(std::lgamma(
static_cast<double>(ne) + 1.0) -
262 static_cast<double>(ne) * std::log(2.0) -
263 std::lgamma(
static_cast<double>(k) + 1.0) -
264 std::lgamma(
static_cast<double>(ne - k) + 1.0));
266 const double pref = std::pow(10.0,
static_cast<double>(ne) / 3.0);
267 for (
long k = 0; k <= 2 * ne; ++k) {
268 const Cplx beta(
static_cast<double>(ne) * std::log(10.0) / 3.0,
269 M_PI *
static_cast<double>(k));
270 const double sgn = (k % 2 == 0) ? 1.0 : -1.0;
271 const double e = pref * sgn * eta[
static_cast<std::size_t
>(k)];
272 const Cplx bj = beta / t;
273 g += 0.5 * e * (fun(bj) + fun(std::conj(bj)));
284inline void quad_nodes(
double ymax, std::vector<double>* y, std::vector<double>* w,
285 std::size_t npanel = 8, std::size_t ng = 32) {
288 std::vector<double> edges;
289 edges.push_back(0.0);
290 for (
long k = -
static_cast<long>(npanel); k <= 0; ++k)
291 edges.push_back(ymax * std::pow(2.0,
static_cast<double>(k)));
292 for (std::size_t k = 0; k + 1 < edges.size(); ++k) {
293 std::vector<double> xg, wg;
294 pfqn::detail::gauss_legendre<double>(ng, edges[k], edges[k + 1], xg, wg);
295 for (std::size_t i = 0; i < xg.size(); ++i) {
311double second_moment(Fn lst,
double meanref,
double meanscale) {
312 if (!(meanref > 0.0))
return 0.0;
313 const double h = std::min(1e-2 / meanref, 1.0 / meanscale);
314 auto d2 = [&lst](
double hh) {
316 for (
int j = 0; j < 6; ++j) f[j] = std::real(lst(Cplx(static_cast<double>(j) * hh, 0.0)));
317 return (45.0 * f[0] - 154.0 * f[1] + 214.0 * f[2] - 156.0 * f[3] + 61.0 * f[4] -
321 return (16.0 * d2(h / 2.0) - d2(h)) / 15.0;
334inline Mg1PsResult mg1ps_finish(
double lambda,
double rho,
double m1,
double m2,
335 const std::function<
Cplx(
const Cplx&)>& bhat,
336 const std::function<
Cplx(
const Cplx&,
double,
double*)>& denom,
337 const std::function<
double(
double)>& bpdf,
double ymax,
bool isPH,
338 const Mg1PsOptions& opt) {
339 const bool hasPdf =
static_cast<bool>(bpdf);
340 const double nan = std::numeric_limits<double>::quiet_NaN();
346 res.meanUncond = m1 / (1.0 - rho);
347 res.atomUncond = (1.0 - rho) * bhat(
Cplx(lambda, 0.0)).real();
349 auto lst_cond = [denom, rho](
const Cplx& s,
double x,
bool excess) {
350 if (x == 0.0)
return Cplx(1.0, 0.0);
352 const Cplx val = denom(s, x, &scale);
353 const Cplx expo = (excess ? s :
Cplx(0.0, 0.0)) -
Cplx(scale, 0.0);
354 return Cplx(1.0 - rho, 0.0) * std::exp(expo * x) / val;
356 res.lstCond = [lst_cond](
Cplx s,
double x) {
return lst_cond(s, x,
false); };
357 res.lstExcess = [lst_cond](
Cplx s,
double x) {
return lst_cond(s, x,
true); };
358 res.dominantRoot = [lambda, bhat, rho](
Cplx s) {
360 const int maxit = std::max(200,
static_cast<int>(std::ceil(
361 3.0 * std::log(1e-15) / std::log(std::max(rho, 1e-3)))));
362 for (
int it = 0; it < maxit; ++it) {
363 const Cplx nx = s +
Cplx(lambda, 0.0) * (
Cplx(1.0, 0.0) - bhat(tau));
364 if (std::abs(nx - tau) <= 1e-14 * std::max(1.0, std::abs(nx)))
return nx;
367 throw InputError(
"qsys_mg1_ps: the dominant root iteration did not converge");
372 std::vector<double> yq, wq, bq;
373 quad_nodes(ymax, &yq, &wq);
375 bq.resize(yq.size());
376 for (std::size_t k = 0; k < yq.size(); ++k) bq[k] = bpdf(yq[k]);
378 res.lstUncond = [lst_cond, yq, wq, bq](
Cplx s) {
380 throw InputError(
"qsys_mg1_ps: the service density is required to remove the "
381 "conditioning, pass it as the pdf option");
383 for (std::size_t k = 0; k < yq.size(); ++k)
384 v += wq[k] * bq[k] * lst_cond(s, yq[k],
false);
391 res.lstCondVal = Matrix<double>(res.x.size(), res.s.size(), 0.0);
392 for (std::size_t i = 0; i < res.x.size(); ++i)
393 for (std::size_t j = 0; j < res.s.size(); ++j)
394 res.lstCondVal(i, j) = res.lstCond(
Cplx(res.s[j], 0.0), res.x[i]).real();
395 res.lstUncondVal.assign(res.s.size(), nan);
397 for (std::size_t j = 0; j < res.s.size(); ++j)
398 res.lstUncondVal[j] = res.lstUncond(
Cplx(res.s[j], 0.0)).real();
400 res.atomCond.assign(res.x.size(), 0.0);
401 res.meanCond.assign(res.x.size(), 0.0);
402 res.m2Cond.assign(res.x.size(), 0.0);
403 res.varCond.assign(res.x.size(), 0.0);
404 for (std::size_t i = 0; i < res.x.size(); ++i) {
405 res.atomCond[i] = (1.0 - rho) * std::exp(-lambda * res.x[i]);
406 res.meanCond[i] = res.x[i] / (1.0 - rho);
407 const double xi = res.x[i];
408 res.m2Cond[i] = second_moment([&res, xi](
Cplx u) {
return res.lstCond(u, xi); },
409 res.meanCond[i], res.meanUncond);
410 res.varCond[i] = res.m2Cond[i] - res.meanCond[i] * res.meanCond[i];
420 for (std::size_t k = 0; k < yq.size(); ++k) {
421 const double yk = yq[k];
422 m2u += wq[k] * bq[k] *
423 second_moment([&res, yk](
Cplx u) {
return res.lstCond(u, yk); },
424 yk / (1.0 - rho), res.meanUncond);
427 res.varUncond = m2u - res.meanUncond * res.meanUncond;
430 if (!res.t.empty() && !isPH)
431 throw InputError(
"qsys_mg1_ps: the sojourn time distribution needs phase-type service, "
432 "since inverting a numerically inverted transform is unstable in double "
433 "precision; with a transform handle only the LST and its moments are "
435 res.pdfCond = Matrix<double>(res.x.size(), res.t.size(), 0.0);
436 res.cdfCond = Matrix<double>(res.x.size(), res.t.size(), 0.0);
437 for (std::size_t i = 0; i < res.x.size(); ++i) {
438 const double atom = res.atomCond[i], xi = res.x[i];
441 auto gpdf = [&res, xi, atom](
Cplx u) {
return res.lstExcess(u, xi) -
Cplx(atom, 0.0); };
442 auto gcdf = [gpdf](
Cplx u) {
return gpdf(u) / u; };
443 for (std::size_t j = 0; j < res.t.size(); ++j) {
444 const double tj = res.t[j];
445 if (tj < xi)
continue;
447 res.cdfCond(i, j) = atom;
450 res.cdfCond(i, j) = ilt(gcdf, tj - xi, opt.nterms).real() + atom;
451 const double ratio = tj / xi;
452 if (std::fabs(ratio - std::floor(ratio + 0.5)) < 1e-9)
453 res.pdfCond(i, j) = std::numeric_limits<double>::quiet_NaN();
455 res.pdfCond(i, j) = ilt(gpdf, tj - xi, opt.nterms).real();
459 res.pdfUncond.assign(res.t.size(), nan);
460 res.cdfUncond.assign(res.t.size(), nan);
462 for (std::size_t j = 0; j < res.t.size(); ++j) {
463 if (res.t[j] <= 0.0) {
464 res.pdfUncond[j] = 0.0;
465 res.cdfUncond[j] = 0.0;
468 res.pdfUncond[j] = ilt(res.lstUncond, res.t[j], opt.nterms).real();
470 ilt([&res](
Cplx u) {
return res.lstUncond(u) / u; }, res.t[j], opt.nterms).real();
487 using namespace mg1psdetail;
488 if (!(lambda > 0.0) || !std::isfinite(lambda))
489 throw InputError(
"qsys_mg1_ps: lambda must be a finite positive scalar");
490 if (
opt.nterms % 2 == 0 ||
opt.nterms < 11)
491 throw InputError(
"qsys_mg1_ps: nterms must be an odd integer of at least 11");
493 const std::size_t n = alpha.size();
494 if (n == 0 || Tmat.
rows() != n || Tmat.
cols() != n)
495 throw InputError(
"qsys_mg1_ps: T must be n x n to match alpha");
497 for (std::size_t i = 0; i < n; ++i) {
498 if (alpha[i] < -1e-12)
throw InputError(
"qsys_mg1_ps: alpha must be a probability vector");
501 if (std::fabs(asum - 1.0) > 1e-8)
502 throw InputError(
"qsys_mg1_ps: alpha must be a probability vector");
503 std::vector<double> exitrate(n, 0.0);
504 for (std::size_t i = 0; i < n; ++i) {
506 for (std::size_t j = 0; j < n; ++j) row += Tmat(i, j);
508 if (exitrate[i] < -1e-10 || Tmat(i, i) >= 0.0)
509 throw InputError(
"qsys_mg1_ps: T must be a proper phase-type subgenerator");
514 for (std::size_t i = 0; i < n; ++i)
515 for (std::size_t j = 0; j < n; ++j) negT(i, j) = -Tmat(i, j);
516 const std::vector<double>
ones(n, 1.0);
517 const std::vector<double> u1 =
solve(negT,
ones);
518 const std::vector<double> u2 =
solve(negT, u1);
519 double m1 = 0.0, m2 = 0.0;
520 for (std::size_t i = 0; i < n; ++i) {
521 m1 += alpha[i] * u1[i];
522 m2 += 2.0 * alpha[i] * u2[i];
524 const double rho = lambda * m1;
526 throw InputError(
"qsys_mg1_ps: the system is unstable, the utilization is at least one");
530 std::vector<double> db(n + 1, 0.0), nb(n, 0.0);
533 for (std::size_t i = 0; i < n; ++i) Mk(i, i) = 1.0;
534 for (std::size_t k = 1; k <= n; ++k) {
536 for (std::size_t i = 0; i < n; ++i) {
538 for (std::size_t j = 0; j < n; ++j) row += Mk(i, j) * exitrate[j];
543 for (std::size_t i = 0; i < n; ++i)
544 for (std::size_t j = 0; j < n; ++j) {
546 for (std::size_t l = 0; l < n; ++l) acc += Tmat(i, l) * Mk(l, j);
550 for (std::size_t i = 0; i < n; ++i)
tr += TM(i, i);
551 db[k] = -
tr /
static_cast<double>(k);
553 for (std::size_t i = 0; i < n; ++i) Mk(i, i) += db[k];
556 auto bhat = [db, nb](
const Cplx& tau) {
return polyval(nb, tau) / polyval(db, tau); };
557 auto bpdf = [&Tmat, &alpha, &exitrate, n](
double y) {
559 for (std::size_t i = 0; i < n; ++i)
560 for (std::size_t j = 0; j < n; ++j) E(i, j) = Tmat(i, j) * y;
563 for (std::size_t i = 0; i < n; ++i) {
565 for (std::size_t j = 0; j < n; ++j) row += Ey(i, j) * exitrate[j];
574 auto denom = [db, nb, n, lambda, rho](
const Cplx& s,
double x,
double* scale_out) {
575 std::vector<double> dm(db.size(), 0.0);
577 for (std::size_t i = 1; i < db.size(); ++i) dm[i] = db[i] - nb[i - 1];
581 std::vector<Cplx> Pc, Ac;
583 std::vector<Cplx> lin;
584 lin.push_back(
Cplx(1.0, 0.0));
585 lin.push_back(-(s +
Cplx(lambda, 0.0)));
586 std::vector<Cplx> dbz(db.size());
587 for (std::size_t i = 0; i < db.size(); ++i) dbz[i] =
Cplx(db[i], 0.0);
588 Pc.assign(lin.size() + dbz.size() - 1,
Cplx(0.0, 0.0));
589 for (std::size_t i = 0; i < lin.size(); ++i)
590 for (std::size_t j = 0; j < dbz.size(); ++j) Pc[i + j] += lin[i] * dbz[j];
592 for (std::size_t i = 0; i < nb.size(); ++i)
593 Pc[Pc.size() - nb.size() + i] +=
Cplx(lambda * nb[i], 0.0);
595 std::vector<Cplx> q1;
596 q1.push_back(
Cplx(1.0 - rho, 0.0));
597 q1.push_back(s *
Cplx(rho, 0.0));
598 q1.push_back(
Cplx(0.0, 0.0));
599 std::vector<Cplx> t1(q1.size() + dbz.size() - 1,
Cplx(0.0, 0.0));
600 for (std::size_t i = 0; i < q1.size(); ++i)
601 for (std::size_t j = 0; j < dbz.size(); ++j) t1[i + j] += q1[i] * dbz[j];
603 std::vector<Cplx> q2;
604 q2.push_back(
Cplx(1.0 - rho, 0.0));
606 std::vector<Cplx> ldm(dm.size());
607 for (std::size_t i = 0; i < dm.size(); ++i) ldm[i] =
Cplx(lambda * dm[i], 0.0);
608 std::vector<Cplx> t2(q2.size() + ldm.size() - 1,
Cplx(0.0, 0.0));
609 for (std::size_t i = 0; i < q2.size(); ++i)
610 for (std::size_t j = 0; j < ldm.size(); ++j) t2[i + j] += q2[i] * ldm[j];
612 Ac.assign(t1.size(),
Cplx(0.0, 0.0));
613 for (std::size_t i = 0; i < t1.size(); ++i) Ac[i] = t1[i];
614 for (std::size_t i = 0; i < t2.size(); ++i) Ac[Ac.size() - t2.size() + i] -= t2[i];
619 for (std::size_t i = 0; i < Ac.size(); ++i) nrmA = std::max(nrmA, std::abs(Ac[i]));
620 const double tailA = std::abs(Ac[Ac.size() - 2]) + std::abs(Ac[Ac.size() - 1]);
621 if (tailA > 1e-6 * std::max(1.0, nrmA))
622 throw InputError(
"qsys_mg1_ps: the double pole at the origin did not cancel");
627 std::vector<Cplx> Ahat(Ac.begin(), Ac.begin() +
static_cast<long>(n + 1));
632 std::vector<Cplx> mon(Pc.size());
633 for (std::size_t i = 0; i < Pc.size(); ++i) mon[i] = Pc[i] / Pc[0];
634 const std::size_t deg = mon.size() - 1;
635 r.assign(deg,
Cplx(0.0, 0.0));
636 const Cplx seed(0.4, 0.9);
638 for (std::size_t i = 0; i < deg; ++i) {
642 for (
int it = 0; it < 2000; ++it) {
644 for (std::size_t i = 0; i < deg; ++i) {
646 for (std::size_t j = 0; j < deg; ++j)
647 if (j != i) den *= (r[i] - r[j]);
648 if (std::abs(den) < 1e-300)
continue;
649 const Cplx step = polyval(mon, r[i]) / den;
651 move = std::max(move, std::abs(step));
653 if (move < 1e-14)
break;
656 double scale = -std::numeric_limits<double>::infinity();
657 for (std::size_t i = 0; i < r.size(); ++i) scale = std::max(scale, r[i].real());
660 double maxr = 0.0, minsep = std::numeric_limits<double>::infinity();
661 for (std::size_t i = 0; i < r.size(); ++i) maxr = std::max(maxr, std::abs(r[i]));
662 for (std::size_t i = 0; i < r.size(); ++i)
663 for (std::size_t j = 0; j < r.size(); ++j)
664 if (i != j) minsep = std::min(minsep, std::abs(r[i] - r[j]));
666 if (r.size() >= 2 && minsep > 1e-7 * std::max(1.0, maxr)) {
667 std::vector<Cplx> dP(Pc.size() - 1,
Cplx(0.0, 0.0));
668 for (std::size_t i = 0; i + 1 < Pc.size(); ++i)
669 dP[i] = Pc[i] *
static_cast<double>(Pc.size() - 1 - i);
671 for (std::size_t i = 0; i < r.size(); ++i) {
672 const Cplx coef = polyval(Ahat, r[i]) / polyval(dP, r[i]);
673 val += std::exp((r[i] -
Cplx(scale, 0.0)) * x) * coef;
680 const std::size_t d = Pc.size() - 1;
682 for (std::size_t j = 0; j < d; ++j) Acomp(0, j) = -Pc[j + 1] / Pc[0];
683 for (std::size_t i = 1; i < d; ++i) Acomp(i, i - 1) =
Cplx(1.0, 0.0);
684 for (std::size_t i = 0; i < d; ++i) Acomp(i, i) -=
Cplx(scale, 0.0);
686 for (std::size_t i = 0; i < d; ++i)
687 for (std::size_t j = 0; j < d; ++j) Ax(i, j) = Acomp(i, j) * x;
690 for (std::size_t j = 0; j < d && j < Ahat.size(); ++j) val += (Ahat[j] / Pc[0]) * E(j, 0);
698 double mineig = std::numeric_limits<double>::infinity();
700 const std::vector<Cplx> ev = poly_roots(db);
701 for (std::size_t i = 0; i < ev.size(); ++i) mineig = std::min(mineig, -ev[i].real());
702 if (ev.empty() || !std::isfinite(mineig))
703 for (std::size_t i = 0; i < n; ++i) mineig = std::min(mineig, -Tmat(i, i));
705 const double ymax = std::max(40.0 * m1, 40.0 / std::max(mineig, 1e-12));
707 std::function<
Cplx(
const Cplx&,
double,
double*)> denom_fn = denom;
708 std::function<
Cplx(
const Cplx&)> bhat_fn = bhat;
709 std::function<double(
double)> bpdf_fn =
opt.pdf ?
opt.pdf : std::function<double(double)>(bpdf);
710 return mg1psdetail::mg1ps_finish(lambda, rho, m1, m2, bhat_fn, denom_fn, bpdf_fn, ymax,
true,
733 using namespace mg1psdetail;
734 if (!(lambda > 0.0) || !std::isfinite(lambda))
735 throw InputError(
"qsys_mg1_ps: lambda must be a finite positive scalar");
736 if (
opt.nterms % 2 == 0 ||
opt.nterms < 11)
737 throw InputError(
"qsys_mg1_ps: nterms must be an odd integer of at least 11");
738 if (!bhat)
throw InputError(
"qsys_mg1_ps: the service transform handle is empty");
739 if (!std::isfinite(m1) || m1 <= 0.0)
740 throw InputError(
"qsys_mg1_ps: the mean service time must be a finite positive scalar");
741 const double rho = lambda * m1;
743 throw InputError(
"qsys_mg1_ps: the system is unstable, the utilization is at least one");
745 throw InputError(
"qsys_mg1_ps: the sojourn time distribution needs phase-type service, "
746 "since inverting a numerically inverted transform is unstable in double "
747 "precision; with a transform handle only the LST and its moments are "
751 auto rootfun = [lambda, bhat, rho](
const Cplx& s) {
753 const int maxit = std::max(200,
static_cast<int>(std::ceil(
754 3.0 * std::log(1e-15) / std::log(std::max(rho, 1e-3)))));
755 for (
int it = 0; it < maxit; ++it) {
756 const Cplx nx = s +
Cplx(lambda, 0.0) * (
Cplx(1.0, 0.0) - bhat(tau));
757 if (std::abs(nx - tau) <= 1e-14 * std::max(1.0, std::abs(nx)))
return nx;
760 throw InputError(
"qsys_mg1_ps: the dominant root iteration did not converge");
762 const double shift = 0.25 * (1.0 - rho) / m1;
763 const std::size_t nterms =
opt.nterms;
766 std::function<
Cplx(
const Cplx&,
double,
double*)> denom =
767 [lambda, rho, bhat, rootfun, shift, nterms](
const Cplx& s,
double x,
double* scale_out) {
768 const double scale = rootfun(s).real() + shift;
770 auto f = [&](
const Cplx& u) {
771 const Cplx tau = u +
Cplx(scale, 0.0);
772 const Cplx bh = bhat(tau);
773 const Cplx one_m_bh =
Cplx(1.0, 0.0) - bh;
774 const Cplx num =
Cplx(1.0 - rho, 0.0) * tau * tau -
775 Cplx((1.0 - rho) * lambda, 0.0) * one_m_bh * tau +
776 s *
Cplx(rho, 0.0) * tau - s *
Cplx(lambda, 0.0) * one_m_bh;
777 return num / (tau * tau * (tau - s -
Cplx(lambda, 0.0) * one_m_bh));
779 return ilt(f, x, nterms);
781 const double ymax = 60.0 * m1;
782 return mg1ps_finish(lambda, rho, m1, std::numeric_limits<double>::quiet_NaN(), bhat, denom,
783 opt.pdf, ymax,
false,
opt);
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
Mg1PsResult qsys_mg1_ps(double lambda, const std::vector< double > &alpha, const Matrix< double > &Tmat, const Mg1PsOptions &opt)
Sojourn-time distribution of the M/G/1 processor-sharing queue.
std::complex< double > Cplx
Conservation laws of a layered queueing network, enumerated from its structure.
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 > expm(const Matrix< T > &A)
Matrix exponential exp(A).
Number-type abstraction for the templated API port.
Shared scalar machinery for the integration / asymptotic members of the pfqn family (pfqn_le,...
Options of qsys_mg1_ps, mirroring the reference's name-value pairs.
std::function< double(double)> pdf
Service density, needed to remove the conditioning on a callback path.
std::vector< double > s
transform arguments to tabulate the LST at
std::vector< double > t
times to evaluate the sojourn distribution at
std::size_t nterms
function evaluations per numerical inversion, ODD
std::vector< double > x
service requirements to condition on
Everything qsys_mg1_ps returns.
Matrix< double > lstCondVal
(|x| x |s|)
std::function< Cplx(Cplx)> lstUncond
s -> E[exp(-s V)]
std::vector< double > lstUncondVal
std::vector< double > meanCond
double atomUncond
(1-rho) bhat(lambda)
Matrix< double > cdfCond
(|x| x |t|)
std::function< Cplx(Cplx, double)> lstCond
(s,x) -> E[exp(-s V(x))]
std::function< Cplx(Cplx, double)> lstExcess
(s,x) -> E[exp(-s (V(x)-x))]
std::vector< double > cdfUncond
std::vector< double > m2Cond
std::vector< double > varCond
std::vector< double > pdfUncond
std::function< Cplx(Cplx)> dominantRoot
s -> tau*(s)
std::vector< double > atomCond
(1-rho) exp(-lambda x), at t = x