47 check_percentiles(o.percentiles,
"qsys_mh21");
48 if (!(lambda > 0.0) || !(mu1 > 0.0) || !(mu2 > 0.0))
49 throw Error(
"qsys_mh21: lambda, mu1 and mu2 must be positive scalars");
50 if (!(p1 >= 0.0) || p1 > 1.0)
throw Error(
"qsys_mh21: p1 must lie in [0, 1]");
51 const double p2 = 1.0 - p1;
52 const double rho = lambda * (p1 / mu1 + p2 / mu2);
53 if (rho >= 1.0) throw_unstable(
"qsys_mh21",
"rho", rho);
54 const double a1 = lambda / mu1, a2 = lambda / mu2;
56 const double qa = a1 * a2, qb = -(a1 + a2 + a1 * a2), qc = 1.0 + a1 + a2 - rho;
58 const double disc = qb * qb - 4.0 * qa * qc;
60 const double sq = std::sqrt(disc);
61 const double qq = -0.5 * (qb + (qb >= 0.0 ? sq : -sq));
64 if (z1 > z2) std::swap(z1, z2);
66 z1 = z2 = -qb / (2.0 * qa);
68 if (std::fabs(z1 - z2) < 1e-10 * std::max(1.0, std::fabs(z2)))
70 "qsys_mh21: the two roots coincide; use qsys_mg1 or qsys_mm1 for this service law");
71 const double C1 = (z1 - 1.0) * (1.0 - rho * z2) / (z1 - z2);
72 const double C2 = (z2 - 1.0) * (1.0 - rho * z1) / (z2 - z1);
73 const double mua = lambda * (z1 - 1.0), mub = lambda * (z2 - 1.0);
74 const double pia = C1 * z1 / (z1 - 1.0), pib = C2 * z2 / (z2 - 1.0);
75 auto hp = [&](
double s) {
76 return 1.0 - lambda * (p1 * mu1 / ((mu1 + s) * (mu1 + s)) + p2 * mu2 / ((mu2 + s) * (mu2 + s)));
78 const double wa = -(1.0 - rho) / hp(-mua), wb = -(1.0 - rho) / hp(-mub);
80 const double ES = p1 / mu1 + p2 / mu2;
81 const double ES2 = 2.0 * (p1 / (mu1 * mu1) + p2 / (mu2 * mu2));
82 const double ES3 = 6.0 * (p1 / (mu1 * mu1 * mu1) + p2 / (mu2 * mu2 * mu2));
83 QsysRes res =
qsys_mg1_res(lambda, 1.0 / ES, std::sqrt(std::max(ES2 / (ES * ES) - 1.0, 0.0)), ES3);
86 if (nmax < 0) nmax = std::max(1L,
static_cast<long>(std::ceil(std::log(1e-14) / std::log(1.0 / z1))));
87 std::vector<double> P(
static_cast<std::size_t
>(nmax + 1));
88 for (
long n = 0; n <= nmax; ++n)
89 P[
static_cast<std::size_t
>(n)] = C1 * std::pow(z1, -
static_cast<double>(n)) + C2 * std::pow(z2, -
static_cast<double>(n));
90 res.
set(
"queueLengthDist", P);
91 res.
set(
"roots", std::vector<double>{z1, z2});
92 res.
set(
"sojournRates", std::vector<double>{mua, mub});
93 res.
set(
"sojournWeights", std::vector<double>{pia, pib});
94 res.
set(
"waitingWeights", std::vector<double>{wa, wb});
95 const std::function<double(
double)> Wccdf = [=](
double t) {
96 return t < 0.0 ? 1.0 : wa * std::exp(-mua * t) + wb * std::exp(-mub * t);
98 const std::function<double(
double)> Tccdf = [=](
double t) {
99 return t < 0.0 ? 1.0 : pia * std::exp(-mua * t) + pib * std::exp(-mub * t);
101 if (!o.tPoints.empty()) {
102 res.
set(
"tPoints", o.tPoints);
103 res.
set(
"waitingTimeCdf", cdf_of(Wccdf, o.tPoints));
104 res.
set(
"sojournTimeCdf", cdf_of(Tccdf, o.tPoints));
106 if (!o.percentiles.empty()) {
107 const double hi = 50.0 / std::min(mua, mub);
108 res.
set(
"percentiles", o.percentiles);
109 res.
set(
"waitingTimePercentile", pct_of(Wccdf, o.percentiles, hi));
110 res.
set(
"sojournTimePercentile", pct_of(Tccdf, o.percentiles, hi));