333 using namespace mg1psdetail;
334 if (!(lambda > 0.0) || !std::isfinite(lambda))
335 throw InputError(
"qsys_mg1_ps: lambda must be a finite positive scalar");
336 if (
opt.nterms % 2 == 0 ||
opt.nterms < 11)
337 throw InputError(
"qsys_mg1_ps: nterms must be an odd integer of at least 11");
339 const std::size_t n = alpha.size();
340 if (n == 0 || Tmat.
rows() != n || Tmat.
cols() != n)
341 throw InputError(
"qsys_mg1_ps: T must be n x n to match alpha");
343 for (std::size_t i = 0; i < n; ++i) {
344 if (alpha[i] < -1e-12)
throw InputError(
"qsys_mg1_ps: alpha must be a probability vector");
347 if (std::fabs(asum - 1.0) > 1e-8)
348 throw InputError(
"qsys_mg1_ps: alpha must be a probability vector");
349 std::vector<double> exitrate(n, 0.0);
350 for (std::size_t i = 0; i < n; ++i) {
352 for (std::size_t j = 0; j < n; ++j) row += Tmat(i, j);
354 if (exitrate[i] < -1e-10 || Tmat(i, i) >= 0.0)
355 throw InputError(
"qsys_mg1_ps: T must be a proper phase-type subgenerator");
360 for (std::size_t i = 0; i < n; ++i)
361 for (std::size_t j = 0; j < n; ++j) negT(i, j) = -Tmat(i, j);
362 const std::vector<double>
ones(n, 1.0);
363 const std::vector<double> u1 =
solve(negT,
ones);
364 const std::vector<double> u2 =
solve(negT, u1);
365 double m1 = 0.0, m2 = 0.0;
366 for (std::size_t i = 0; i < n; ++i) {
367 m1 += alpha[i] * u1[i];
368 m2 += 2.0 * alpha[i] * u2[i];
370 const double rho = lambda * m1;
372 throw InputError(
"qsys_mg1_ps: the system is unstable, the utilization is at least one");
376 std::vector<double> db(n + 1, 0.0), nb(n, 0.0);
379 for (std::size_t i = 0; i < n; ++i) Mk(i, i) = 1.0;
380 for (std::size_t k = 1; k <= n; ++k) {
382 for (std::size_t i = 0; i < n; ++i) {
384 for (std::size_t j = 0; j < n; ++j) row += Mk(i, j) * exitrate[j];
389 for (std::size_t i = 0; i < n; ++i)
390 for (std::size_t j = 0; j < n; ++j) {
392 for (std::size_t l = 0; l < n; ++l) acc += Tmat(i, l) * Mk(l, j);
396 for (std::size_t i = 0; i < n; ++i)
tr += TM(i, i);
397 db[k] = -
tr /
static_cast<double>(k);
399 for (std::size_t i = 0; i < n; ++i) Mk(i, i) += db[k];
402 auto bhat = [db, nb](
const Cplx& tau) {
return polyval(nb, tau) / polyval(db, tau); };
403 auto bpdf = [&Tmat, &alpha, &exitrate, n](
double y) {
405 for (std::size_t i = 0; i < n; ++i)
406 for (std::size_t j = 0; j < n; ++j) E(i, j) = Tmat(i, j) * y;
409 for (std::size_t i = 0; i < n; ++i) {
411 for (std::size_t j = 0; j < n; ++j) row += Ey(i, j) * exitrate[j];
420 auto denom = [db, nb, n, lambda, rho](
const Cplx& s,
double x,
double* scale_out) {
421 std::vector<double> dm(db.size(), 0.0);
423 for (std::size_t i = 1; i < db.size(); ++i) dm[i] = db[i] - nb[i - 1];
427 std::vector<Cplx> Pc, Ac;
429 std::vector<Cplx> lin;
430 lin.push_back(
Cplx(1.0, 0.0));
431 lin.push_back(-(s +
Cplx(lambda, 0.0)));
432 std::vector<Cplx> dbz(db.size());
433 for (std::size_t i = 0; i < db.size(); ++i) dbz[i] =
Cplx(db[i], 0.0);
434 Pc.assign(lin.size() + dbz.size() - 1,
Cplx(0.0, 0.0));
435 for (std::size_t i = 0; i < lin.size(); ++i)
436 for (std::size_t j = 0; j < dbz.size(); ++j) Pc[i + j] += lin[i] * dbz[j];
438 for (std::size_t i = 0; i < nb.size(); ++i)
439 Pc[Pc.size() - nb.size() + i] +=
Cplx(lambda * nb[i], 0.0);
441 std::vector<Cplx> q1;
442 q1.push_back(
Cplx(1.0 - rho, 0.0));
443 q1.push_back(s *
Cplx(rho, 0.0));
444 q1.push_back(
Cplx(0.0, 0.0));
445 std::vector<Cplx> t1(q1.size() + dbz.size() - 1,
Cplx(0.0, 0.0));
446 for (std::size_t i = 0; i < q1.size(); ++i)
447 for (std::size_t j = 0; j < dbz.size(); ++j) t1[i + j] += q1[i] * dbz[j];
449 std::vector<Cplx> q2;
450 q2.push_back(
Cplx(1.0 - rho, 0.0));
452 std::vector<Cplx> ldm(dm.size());
453 for (std::size_t i = 0; i < dm.size(); ++i) ldm[i] =
Cplx(lambda * dm[i], 0.0);
454 std::vector<Cplx> t2(q2.size() + ldm.size() - 1,
Cplx(0.0, 0.0));
455 for (std::size_t i = 0; i < q2.size(); ++i)
456 for (std::size_t j = 0; j < ldm.size(); ++j) t2[i + j] += q2[i] * ldm[j];
458 Ac.assign(t1.size(),
Cplx(0.0, 0.0));
459 for (std::size_t i = 0; i < t1.size(); ++i) Ac[i] = t1[i];
460 for (std::size_t i = 0; i < t2.size(); ++i) Ac[Ac.size() - t2.size() + i] -= t2[i];
465 for (std::size_t i = 0; i < Ac.size(); ++i) nrmA = std::max(nrmA, std::abs(Ac[i]));
466 const double tailA = std::abs(Ac[Ac.size() - 2]) + std::abs(Ac[Ac.size() - 1]);
467 if (tailA > 1e-6 * std::max(1.0, nrmA))
468 throw InputError(
"qsys_mg1_ps: the double pole at the origin did not cancel");
473 std::vector<Cplx> Ahat(Ac.begin(), Ac.begin() +
static_cast<long>(n + 1));
478 std::vector<Cplx> mon(Pc.size());
479 for (std::size_t i = 0; i < Pc.size(); ++i) mon[i] = Pc[i] / Pc[0];
480 const std::size_t deg = mon.size() - 1;
481 r.assign(deg,
Cplx(0.0, 0.0));
482 const Cplx seed(0.4, 0.9);
484 for (std::size_t i = 0; i < deg; ++i) {
488 for (
int it = 0; it < 2000; ++it) {
490 for (std::size_t i = 0; i < deg; ++i) {
492 for (std::size_t j = 0; j < deg; ++j)
493 if (j != i) den *= (r[i] - r[j]);
494 if (std::abs(den) < 1e-300)
continue;
495 const Cplx step = polyval(mon, r[i]) / den;
497 move = std::max(move, std::abs(step));
499 if (move < 1e-14)
break;
502 double scale = -std::numeric_limits<double>::infinity();
503 for (std::size_t i = 0; i < r.size(); ++i) scale = std::max(scale, r[i].real());
506 double maxr = 0.0, minsep = std::numeric_limits<double>::infinity();
507 for (std::size_t i = 0; i < r.size(); ++i) maxr = std::max(maxr, std::abs(r[i]));
508 for (std::size_t i = 0; i < r.size(); ++i)
509 for (std::size_t j = 0; j < r.size(); ++j)
510 if (i != j) minsep = std::min(minsep, std::abs(r[i] - r[j]));
512 if (r.size() >= 2 && minsep > 1e-7 * std::max(1.0, maxr)) {
513 std::vector<Cplx> dP(Pc.size() - 1,
Cplx(0.0, 0.0));
514 for (std::size_t i = 0; i + 1 < Pc.size(); ++i)
515 dP[i] = Pc[i] *
static_cast<double>(Pc.size() - 1 - i);
517 for (std::size_t i = 0; i < r.size(); ++i) {
518 const Cplx coef = polyval(Ahat, r[i]) / polyval(dP, r[i]);
519 val += std::exp((r[i] -
Cplx(scale, 0.0)) * x) * coef;
526 const std::size_t d = Pc.size() - 1;
528 for (std::size_t j = 0; j < d; ++j) Acomp(0, j) = -Pc[j + 1] / Pc[0];
529 for (std::size_t i = 1; i < d; ++i) Acomp(i, i - 1) =
Cplx(1.0, 0.0);
530 for (std::size_t i = 0; i < d; ++i) Acomp(i, i) -=
Cplx(scale, 0.0);
532 for (std::size_t i = 0; i < d; ++i)
533 for (std::size_t j = 0; j < d; ++j) Ax(i, j) = Acomp(i, j) * x;
536 for (std::size_t j = 0; j < d && j < Ahat.size(); ++j) val += (Ahat[j] / Pc[0]) * E(j, 0);
547 auto lst_cond = [denom, rho](
const Cplx& s,
double x,
bool excess) {
548 if (x == 0.0)
return Cplx(1.0, 0.0);
550 const Cplx val = denom(s, x, &scale);
551 const Cplx expo = (excess ? s :
Cplx(0.0, 0.0)) -
Cplx(scale, 0.0);
552 return Cplx(1.0 - rho, 0.0) * std::exp(expo * x) / val;
554 res.
lstCond = [lst_cond](
Cplx s,
double x) {
return lst_cond(s, x,
false); };
555 res.
lstExcess = [lst_cond](
Cplx s,
double x) {
return lst_cond(s, x,
true); };
558 const int maxit = std::max(200,
static_cast<int>(std::ceil(
559 3.0 * std::log(1e-15) / std::log(std::max(rho, 1e-3)))));
560 for (
int it = 0; it < maxit; ++it) {
561 const Cplx nx = s +
Cplx(lambda, 0.0) * (
Cplx(1.0, 0.0) - bhat(tau));
562 if (std::abs(nx - tau) <= 1e-14 * std::max(1.0, std::abs(nx)))
return nx;
565 throw InputError(
"qsys_mg1_ps: the dominant root iteration did not converge");
570 double mineig = std::numeric_limits<double>::infinity();
571 for (std::size_t i = 0; i < n; ++i) mineig = std::min(mineig, -Tmat(i, i));
572 const double ymax = std::max(40.0 * m1, 40.0 / std::max(mineig, 1e-12));
573 std::vector<double> yq, wq, bq;
574 quad_nodes(ymax, &yq, &wq);
575 bq.resize(yq.size());
576 for (std::size_t k = 0; k < yq.size(); ++k) bq[k] =
opt.pdf ?
opt.pdf(yq[k]) : bpdf(yq[k]);
579 for (std::size_t k = 0; k < yq.size(); ++k)
580 v += wq[k] * bq[k] * lst_cond(s, yq[k],
false);
588 for (std::size_t i = 0; i < res.
x.size(); ++i)
589 for (std::size_t j = 0; j < res.
s.size(); ++j)
592 for (std::size_t j = 0; j < res.
s.size(); ++j)
597 res.
m2Cond.assign(res.
x.size(), 0.0);
598 res.
varCond.assign(res.
x.size(), 0.0);
599 for (std::size_t i = 0; i < res.
x.size(); ++i) {
600 res.
atomCond[i] = (1.0 - rho) * std::exp(-lambda * res.
x[i]);
601 res.
meanCond[i] = res.
x[i] / (1.0 - rho);
602 const double xi = res.
x[i];
612 for (std::size_t k = 0; k < yq.size(); ++k) {
613 const double yk = yq[k];
614 m2u += wq[k] * bq[k] *
615 second_moment([&res, yk](
Cplx u) {
return res.
lstCond(u, yk); },
623 for (std::size_t i = 0; i < res.
x.size(); ++i) {
624 const double atom = res.
atomCond[i], xi = res.
x[i];
627 auto gpdf = [&res, xi, atom](
Cplx u) {
return res.
lstExcess(u, xi) -
Cplx(atom, 0.0); };
628 auto gcdf = [gpdf](
Cplx u) {
return gpdf(u) / u; };
629 for (std::size_t j = 0; j < res.
t.size(); ++j) {
630 const double tj = res.
t[j];
631 if (tj < xi)
continue;
636 res.
cdfCond(i, j) = ilt(gcdf, tj - xi,
opt.nterms).real() + atom;
637 const double ratio = tj / xi;
638 if (std::fabs(ratio - std::floor(ratio + 0.5)) < 1e-9)
639 res.
pdfCond(i, j) = std::numeric_limits<double>::quiet_NaN();
641 res.
pdfCond(i, j) = ilt(gpdf, tj - xi,
opt.nterms).real();
647 for (std::size_t j = 0; j < res.
t.size(); ++j) {
648 if (res.
t[j] <= 0.0)
continue;
651 ilt([&res](
Cplx u) {
return res.
lstUncond(u) / u; }, res.
t[j],
opt.nterms).real();