233 if (lambda <= zero)
throw InputError(
"qsys_mgisrgi_whitt: the arrival rate lambda must be positive");
234 if (mu <= zero)
throw InputError(
"qsys_mgisrgi_whitt: the service rate mu must be positive");
235 if (s < 1)
throw InputError(
"qsys_mgisrgi_whitt: the number of servers s must be at least 1");
237 throw InputError(
"qsys_mgisrgi_whitt: the number of extra waiting spaces r must be non-negative");
239 const bool finiteR = std::isfinite(r);
240 std::size_t rr = finiteR ?
static_cast<std::size_t
>(r + 0.5) : opts.maxQueue;
244 std::vector<T> xUp(rr + 1, zero), dlt(rr + 1, zero), delta(rr, zero);
246 std::size_t kUsed = rr;
249 for (std::size_t k = 0; k < rr; ++k) {
250 const std::size_t j = k + 1;
251 detail::mgisrgi_rate_step(j, lambda, dlt[j - 1], patience, delta[j - 1], dlt[j]);
252 xUp[k + 1] = lambda * xUp[k] / (smu + dlt[j]);
253 if (xUp[k + 1] > peak) peak = xUp[k + 1];
254 if (!finiteR && xUp[k + 1] < tolT * peak && k >= 1) {
260 if (kUsed == rr && rr > 0)
261 throw InputError(
"qsys_mgisrgi_whitt: the queue-length tail is still sizeable at the "
262 "truncation level; with r = Inf the patience law must make the chain "
263 "ergodic (raise maxQueue if the model is genuinely that large)");
264 xUp.resize(kUsed + 1);
265 dlt.resize(kUsed + 1);
271 std::vector<T> x(s + rr + 1, zero);
273 for (
unsigned k = s; k >= 1; --k) {
277 for (std::size_t k = 0; k <= rr; ++k) x[s + k] = xUp[k];
280 for (
const T& v : x) total += v;
283 for (std::size_t i = 0; i < x.size(); ++i) res.
queueLengthDist[i] = x[i] / total;
286 for (std::size_t i = 0; i < pa.size(); ++i)
289 T meanNumber = zero, meanQueue = zero, busy = zero;
293 meanNumber += kT * pk;
297 T varNumber = zero, varQueue = zero;
302 varNumber += dN * dN * pk;
303 varQueue += (q - meanQueue) * (q - meanQueue) * pk;
307 for (
unsigned k = 0; k < s; ++k) probNoWait += pa[k];
309 std::vector<T> sigma(rr, zero), mSum(rr, zero), vSum(rr, zero), ewa1(rr, zero), ewa2(rr, zero);
310 std::vector<std::vector<T>> rateK(rr), phiK(rr), survK(rr);
311 for (std::size_t k = 1; k <= rr; ++k) {
312 detail::mgisrgi_kernel(k, smu, dlt, delta, rateK[k - 1], phiK[k - 1], survK[k - 1]);
313 T prod = one, sm = zero, sv = zero, cumM = zero, cumV = zero, e1 = zero, e2 = zero;
314 for (std::size_t j = 0; j < k; ++j) {
315 const T m = one / rateK[k - 1][j];
316 prod *= (one - phiK[k - 1][j]);
323 const T w = survK[k - 1][j] * phiK[k - 1][j];
325 e2 += w * (cumV + cumM * cumM);
336 std::vector<T> wArr(rr, zero);
337 for (std::size_t k = 0; k < rr; ++k) wArr[k] = pa[s + k];
338 T probServed = probNoWait, ews1 = zero, ews2 = zero, ewa1Tot = zero, ewa2Tot = zero;
339 for (std::size_t k = 0; k < rr; ++k) {
340 probServed += wArr[k] * sigma[k];
341 ews1 += wArr[k] * sigma[k] * mSum[k];
342 ews2 += wArr[k] * sigma[k] * (vSum[k] + mSum[k] * mSum[k]);
343 ewa1Tot += wArr[k] * ewa1[k];
344 ewa2Tot += wArr[k] * ewa2[k];
358 res.
varWaitServed = detail::mgisrgi_ratio(ews2, probServed) -
373 if (!opts.wPoints.empty()) {
374 if constexpr (std::is_same_v<T, double>) {
378 auto transform = [&](
const lti::Cplx& z,
bool served) {
380 for (std::size_t k = 1; k <= rr; ++k) {
382 for (std::size_t j = 0; j < k; ++j) {
383 chain *= rateK[k - 1][j] / (rateK[k - 1][j] + z);
384 if (!served) val += chain * (wArr[k - 1] * survK[k - 1][j] * phiK[k - 1][j]);
386 if (served) val += chain * (wArr[k - 1] * sigma[k - 1]);
391 const double capS = std::max(probServed - probNoWait, 0.0);
392 const double capA = std::max(res.
probAbandon, 0.0);
396 res.
cdfWait.resize(opts.wPoints.size());
397 for (std::size_t i = 0; i < opts.wPoints.size(); ++i) {
399 [&](
const lti::Cplx& z) {
return transform(z,
true) / z; }, opts.wPoints[i],
402 [&](
const lti::Cplx& z) {
return transform(z,
false) / z; }, opts.wPoints[i],
404 fs = std::min(std::max(fs, 0.0), capS);
405 fa = std::min(std::max(fa, 0.0), capA);
406 res.
cdfWaitServed[i] = (probNoWait + fs) / std::max(probServed, 1e-300);
408 res.
cdfWait[i] = probNoWait + fs + fa;
411 throw InputError(
"qsys_mgisrgi_whitt: the waiting-time cdfs need a numerical Laplace "
412 "inversion, which only the double instantiation carries");