59 using Cplx = std::complex<double>;
60 check_percentiles(o.percentiles,
"qsys_mer1");
61 if (!(lambda > 0.0) || !(mu > 0.0))
throw Error(
"qsys_mer1: lambda and mu must be positive scalars");
62 if (r < 1)
throw Error(
"qsys_mer1: r must be a positive integer");
63 const double rho = lambda * r / mu;
64 if (rho >= 1.0) throw_unstable(
"qsys_mer1",
"rho", rho);
67 std::vector<double> poly(r + 2, 0.0);
69 poly[1] = -(lambda + mu);
71 std::vector<Cplx> all = mg1psdetail::poly_roots(poly);
74 for (
int it = 0; it < 4; ++it) {
75 Cplx f(0.0, 0.0), df(0.0, 0.0);
76 for (
double c : poly) {
80 if (std::abs(df) < 1e-300)
break;
85 for (
const Cplx& z : all)
86 if (std::abs(z) < 1.0 - 1e-10) x.push_back(z);
89 std::snprintf(buf,
sizeof buf,
"qsys_mer1: found %d roots inside the unit disk, expected %d",
90 static_cast<int>(x.size()),
static_cast<int>(r));
93 std::vector<Cplx> ck(r);
94 for (
unsigned k = 0; k < r; ++k) {
96 for (
unsigned j = 0; j < r; ++j)
97 if (j != k) prod *= (1.0 - x[j] / x[k]);
98 ck[k] = (1.0 - rho) / prod;
100 const double rr =
static_cast<double>(r);
101 const double ES = rr / mu, ES2 = rr * (rr + 1.0) / (mu * mu), ES3 = rr * (rr + 1.0) * (rr + 2.0) / (mu * mu * mu);
103 out.
res =
qsys_mg1_res(lambda, 1.0 / ES, std::sqrt(ES2 / (ES * ES) - 1.0), ES3);
105 double xmax = 0.0, ckmax = 0.0;
106 for (
const Cplx& z : x) xmax = std::max(xmax, std::abs(z));
107 for (
const Cplx& c : ck) ckmax = std::max(ckmax, std::abs(c));
110 long nph =
static_cast<long>(std::ceil(std::log(1e-14 * (1.0 - xmax) / ckmax) / std::log(xmax))) + r;
111 nph = std::max(nph,
static_cast<long>(r));
112 nlev =
static_cast<long>(std::ceil(
static_cast<double>(nph) / rr));
116 const long nph = nlev *
static_cast<long>(r);
117 std::vector<double> p(
static_cast<std::size_t
>(nph + 1));
118 for (
long n = 0; n <= nph; ++n) {
120 for (
unsigned k = 0; k < r; ++k) s += ck[k] * std::pow(x[k], static_cast<double>(n));
121 p[
static_cast<std::size_t
>(n)] = std::max(s.real(), 0.0);
123 std::vector<double> q(
static_cast<std::size_t
>(nlev + 1), 0.0);
125 for (
long i = 1; i <= nlev; ++i)
126 for (
long j = (i - 1) * r + 1; j <= i * static_cast<long>(r); ++j) q[
static_cast<std::size_t
>(i)] += p[
static_cast<std::size_t
>(j)];
128 out.
res.
set(
"queueLengthDist", q);
129 out.
res.
set(
"phaseDist", p);
132 const std::function<double(
double)> Wccdf = [=](
double t) {
133 if (t < 0.0)
return 1.0;
135 for (
unsigned k = 0; k < r; ++k) s += ck[k] * x[k] / (1.0 - x[k]) * std::exp(-mu * (1.0 - x[k]) * t);
138 const std::function<double(
double)> Tccdf = [=](
double t) {
139 if (t < 0.0)
return 1.0;
141 for (
long n = 0; n <= nph; ++n)
142 s += p[
static_cast<std::size_t
>(n)] * gammainc_upper(mu * t,
static_cast<double>(n) + rr);
145 if (!o.tPoints.empty()) {
146 out.
res.
set(
"tPoints", o.tPoints);
147 out.
res.
set(
"waitingTimeCdf", cdf_of(Wccdf, o.tPoints));
148 out.
res.
set(
"sojournTimeCdf", cdf_of(Tccdf, o.tPoints));
150 if (!o.percentiles.empty()) {
151 const double hi = 50.0 / (mu * (1.0 - xmax));
152 out.
res.
set(
"percentiles", o.percentiles);
153 out.
res.
set(
"waitingTimePercentile", pct_of(Wccdf, o.percentiles, hi));
154 out.
res.
set(
"sojournTimePercentile", pct_of(Tccdf, o.percentiles, hi));
156 out.
res.
analyzer =
"exact:M/E" + std::to_string(r) +
"/1";
QsysRes qsys_mg1_res(double lambda, double mu, double cs, double ES3=-1.0)
The res output of qsys_mg1 (Pollaczek-Khinchine and Takacs).