51 if (!(alpha > 0.0))
throw Error(
"qsys_mmckk: alpha must be a positive real scalar");
52 if (!(mu > 0.0))
throw Error(
"qsys_mmckk: mu must be a positive real scalar");
53 if (c < 1)
throw Error(
"qsys_mmckk: c must be a positive integer");
54 if (K < c)
throw Error(
"qsys_mmckk: K must be an integer >= c");
55 check_percentiles(o.percentiles,
"qsys_mmckk");
57 std::vector<double> logp(K + 1);
58 for (
unsigned n = 0; n <= K; ++n) {
59 logp[n] = gammaln(K + 1.0) - gammaln(n + 1.0) - gammaln(
static_cast<double>(K) - n + 1.0) +
60 n * std::log(alpha / mu);
61 if (n > c) logp[n] += gammaln(n + 1.0) - gammaln(c + 1.0) - (
static_cast<double>(n) - c) * std::log(c);
64 for (
double v : logp) mx = std::max(mx, v);
65 std::vector<double> p(K + 1);
67 for (
unsigned n = 0; n <= K; ++n) s += (p[n] = std::exp(logp[n] - mx));
68 for (
double& v : p) v /= s;
70 double L = 0.0, L2 = 0.0, Lq = 0.0;
71 for (
unsigned n = 0; n <= K; ++n) {
73 L2 +=
static_cast<double>(n) * n * p[n];
74 if (n > c) Lq += (
static_cast<double>(n) - c) * p[n];
76 const double X = alpha * (K - L);
77 std::vector<double> Pi(K);
78 for (
unsigned n = 0; n < K; ++n) Pi[n] = (static_cast<double>(K) - n) * p[n] / (K - L);
80 const double theta = c * mu;
81 std::vector<double> Piw, m;
82 for (
unsigned nw = c; nw + 1 <= K; ++nw) {
83 Piw.push_back(Pi[nw]);
84 m.push_back(
static_cast<double>(nw - c + 1));
86 double probWait = 0.0, EW = 0.0, EW2 = 0.0;
87 for (std::size_t i = 0; i < m.size(); ++i) {
90 EW2 += Piw[i] * m[i] * (m[i] + 1.0);
96 r.
set(
"meanQueueLength", L);
97 r.
set(
"varQueueLength", L2 - L * L);
98 r.
set(
"meanQueueLengthQ", Lq);
99 r.
set(
"meanWaitingTime", EW);
100 r.
set(
"varWaitingTime", EW2 - EW * EW);
101 r.
set(
"meanSojournTime", EW + 1.0 / mu);
102 r.
set(
"varSojournTime", EW2 - EW * EW + 1.0 / (mu * mu));
103 r.
set(
"probWait", probWait);
104 r.
set(
"condMeanWaitingTime", probWait > 0.0 ? EW / probWait : 0.0);
105 r.
set(
"throughput", X);
106 r.
set(
"utilization", X / (c * mu));
107 r.
set(
"meanOperating", K - L);
108 r.
set(
"queueLengthDist", p);
109 r.
set(
"arrivalDist", Pi);
111 const std::function<double(
double)> Wccdf = [Piw, m, theta](
double t) {
112 if (t < 0.0)
return 1.0;
114 for (std::size_t i = 0; i < m.size(); ++i) v += Piw[i] * gammainc_upper(theta * t, m[i]);
117 const std::function<double(
double)> Tccdf = [Pi, Piw, m, theta, mu, c](
double t) {
118 if (t < 0.0)
return 1.0;
120 for (std::size_t i = 0; i < std::min<std::size_t>(c, Pi.size()); ++i) c0 += Pi[i];
121 double v = c0 * std::exp(-mu * t);
123 for (std::size_t i = 0; i < m.size(); ++i) v += Piw[i] * gammainc_upper(mu * t, m[i] + 1.0);
125 const double d = theta - mu;
126 for (std::size_t i = 0; i < m.size(); ++i) {
127 const double lower = gammainc_lower(d * t, m[i]);
129 lower > 0.0 ? std::exp(m[i] * std::log(theta / d) - mu * t + std::log(lower)) : 0.0;
130 v += Piw[i] * (gammainc_upper(theta * t, m[i]) + cross);
135 if (!o.tPoints.empty()) {
136 r.
set(
"tPoints", o.tPoints);
137 r.
set(
"waitingTimeCdf", cdf_of(Wccdf, o.tPoints));
138 r.
set(
"sojournTimeCdf", cdf_of(Tccdf, o.tPoints));
140 if (!o.percentiles.empty()) {
141 const double hiT = (K + 50.0) / std::min(mu, theta);
142 r.
set(
"percentiles", o.percentiles);
143 r.
set(
"waitingTimePercentile", pct_of(Wccdf, o.percentiles, hiT));
144 r.
set(
"sojournTimePercentile", pct_of(Tccdf, o.percentiles, hiT));
146 r.
analyzer =
"exact:M/M/" + std::to_string(c) +
"/" + std::to_string(K) +
"/" + std::to_string(K);