LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mmckk.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_QSYS_QSYS_MMCKK_H
6#define LINE_API_QSYS_QSYS_MMCKK_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Exact analysis of the finite-source M/M/c/K/K (machine-repair) queue.
12 *
13 * Port of matlab/src/api/qsys/qsys_mmckk.m. K sources each operate for an
14 * Exp(alpha) time and then queue for one of c Exp(mu) servers. The number at
15 * the queue is a birth-death chain with
16 *
17 * p_n ~ C(K, n) (alpha/mu)^n n <= c
18 * p_n ~ C(K, n) (alpha/mu)^n n!/(c! c^(n-c)) n > c,
19 *
20 * normalized in log space. An arriving request sees Pi_n ~ (K - n) p_n
21 * (n = 0..K-1); one that finds m-1 waiting waits an Erlang(m, c mu) time, which
22 * gives the waiting and sojourn distributions in closed form.
23 *
24 * ARITHMETIC: double (gammaln, incomplete gamma), as the reference.
25 */
26
27#include <algorithm>
28#include <cmath>
29#include <functional>
30#include <string>
31#include <vector>
32
34#include "line/util/error.h"
35
36namespace line {
37namespace qsys {
38
39/**
40 * @brief Exact M/M/c/K/K finite-source queue.
41 *
42 * @param alpha request rate of one operating source
43 * @param mu service rate of one server
44 * @param c number of servers
45 * @param K number of sources, K >= c
46 * @param o tPoints and percentiles options (nmax is not used)
47 */
48inline QsysRes qsys_mmckk(double alpha, double mu, unsigned c, unsigned K,
49 const QsysResOptions& o = QsysResOptions()) {
50 using namespace resdetail;
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");
56
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);
62 }
63 double mx = logp[0];
64 for (double v : logp) mx = std::max(mx, v);
65 std::vector<double> p(K + 1);
66 double s = 0.0;
67 for (unsigned n = 0; n <= K; ++n) s += (p[n] = std::exp(logp[n] - mx));
68 for (double& v : p) v /= s;
69
70 double L = 0.0, L2 = 0.0, Lq = 0.0;
71 for (unsigned n = 0; n <= K; ++n) {
72 L += n * p[n];
73 L2 += static_cast<double>(n) * n * p[n];
74 if (n > c) Lq += (static_cast<double>(n) - c) * p[n];
75 }
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);
79
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));
85 }
86 double probWait = 0.0, EW = 0.0, EW2 = 0.0;
87 for (std::size_t i = 0; i < m.size(); ++i) {
88 probWait += Piw[i];
89 EW += Piw[i] * m[i];
90 EW2 += Piw[i] * m[i] * (m[i] + 1.0);
91 }
92 EW /= theta;
93 EW2 /= theta * theta;
94
95 QsysRes r;
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);
110
111 const std::function<double(double)> Wccdf = [Piw, m, theta](double t) {
112 if (t < 0.0) return 1.0;
113 double v = 0.0;
114 for (std::size_t i = 0; i < m.size(); ++i) v += Piw[i] * gammainc_upper(theta * t, m[i]);
115 return v;
116 };
117 const std::function<double(double)> Tccdf = [Pi, Piw, m, theta, mu, c](double t) {
118 if (t < 0.0) return 1.0;
119 double c0 = 0.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);
122 if (c == 1) {
123 for (std::size_t i = 0; i < m.size(); ++i) v += Piw[i] * gammainc_upper(mu * t, m[i] + 1.0);
124 } else {
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]);
128 const double cross =
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);
131 }
132 }
133 return v;
134 };
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));
139 }
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));
145 }
146 r.analyzer = "exact:M/M/" + std::to_string(c) + "/" + std::to_string(K) + "/" + std::to_string(K);
147 return r;
148}
149
150} // namespace qsys
151} // namespace line
152
153#endif // LINE_API_QSYS_QSYS_MMCKK_H
Error(const std::string &what)
Definition error.h:75
The exception types the port throws.
QsysRes qsys_mmckk(double alpha, double mu, unsigned c, unsigned K, const QsysResOptions &o=QsysResOptions())
Exact M/M/c/K/K finite-source queue.
Definition qsys_mmckk.h:48
QsysResT< double > QsysRes
Definition qsys_res.h:96
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
The res struct the exact queueing-system functions return, and the numeric helpers those functions sh...
The name-value options shared by the exact functions: tPoints, percentiles and nmax (a negative nmax ...
Definition qsys_res.h:102
std::string analyzer
Definition qsys_res.h:59
void set(const std::string &name, const T &v)
Sets (or overwrites) a scalar field.
Definition qsys_res.h:62