52inline QsysRes qsys_gimc(
const std::function<
double(
double)>& lst,
double lambda,
double mu,
unsigned c,
55 check_percentiles(o.percentiles,
"qsys_gimc");
56 if (!lst)
throw Error(
"qsys_gimc: the interarrival law must be a Distribution or an LST function handle");
57 if (!(lambda > 0.0) || !std::isfinite(lambda))
58 throw Error(
"qsys_gimc: with an LST handle the arrival rate must be given with 'lambda'");
59 if (!(mu > 0.0))
throw Error(
"qsys_gimc: mu must be a positive scalar");
60 if (c < 1)
throw Error(
"qsys_gimc: c must be a positive integer");
61 const double A = lambda / mu;
62 if (A >= c) throw_unstable(
"qsys_gimc",
"lambda/(c mu)", A / c);
64 const std::function<double(
double)> g = [&](
double w) {
return lst(c * mu * (1.0 - w)) - w; };
66 while (g(1.0 - d) >= 0.0 && d > 1e-15) d /= 10.0;
67 if (g(1.0 - d) >= 0.0)
throw NumericError(
ErrorKind{
"Root"},
"qsys_gimc: could not bracket the root omega in (0,1)");
68 double lo = 0.0, hi = 1.0 - d;
69 for (
int it = 0; it < 2000; ++it) {
70 const double mid = 0.5 * (lo + hi);
71 if (mid <= lo || mid >= hi)
break;
72 const double gm = g(mid);
77 if (gm > 0.0) lo = mid;
80 const double omega = std::fabs(g(lo)) <= std::fabs(g(hi)) ? lo : hi;
82 std::vector<double> gj(c), Cj(c), terms(c);
83 for (
unsigned j = 1; j <= c; ++j) gj[j - 1] = lst(j * mu);
85 for (
unsigned j = 1; j <= c; ++j) {
86 cp *= gj[j - 1] / (1.0 - gj[j - 1]);
90 for (
unsigned j = 1; j <= c; ++j) {
91 terms[j - 1] = nchoosek_r(c, j) * (c * (1.0 - gj[j - 1]) - j) /
92 (Cj[j - 1] * (1.0 - gj[j - 1]) * (c * (1.0 - omega) - j));
95 const double D = 1.0 / (1.0 / (1.0 - omega) + tsum);
96 std::vector<double> Cn(c + 1);
98 for (
unsigned j = 1; j <= c; ++j) Cn[j] = Cj[j - 1];
99 std::vector<double> Un(c);
100 for (
unsigned nn = 0; nn < c; ++nn) {
102 for (
unsigned j = nn; j < c; ++j) s += terms[j];
103 Un[nn] = D * Cn[nn] * s;
107 nmax =
static_cast<long>(c) +
108 static_cast<long>(std::max(1.0, std::ceil(std::log(1e-14 * (1.0 - omega)) /
109 std::log(std::max(omega, std::numeric_limits<double>::min())))));
110 nmax = std::max<long>(nmax, c);
111 std::vector<double> Pi(
static_cast<std::size_t
>(nmax + 1));
112 for (
long nn = 0; nn <= nmax; ++nn) {
113 if (nn >=
static_cast<long>(c) - 1) {
114 Pi[nn] = D * std::pow(omega,
static_cast<double>(nn -
static_cast<long>(c)));
117 for (
long ii = nn; ii < static_cast<long>(c); ++ii)
118 s += (((ii - nn) % 2) ? -1.0 : 1.0) * nchoosek_r(ii, nn) * Un[ii];
122 double PiTot = D / omega / (1.0 - omega);
123 for (
long nn = 0; nn + 1 <
static_cast<long>(c); ++nn) PiTot += Pi[nn];
125 for (
long nn = 0; nn < std::max<long>(
static_cast<long>(c) - 1, 1); ++nn)
126 if (Pi[nn] < -1e-10) neg =
true;
127 if (std::fabs(PiTot - 1.0) > 1e-8 || neg) {
129 std::snprintf(buf,
sizeof buf,
130 "qsys_gimc: the arrival distribution does not normalize (sum = %.12g): the alternating "
131 "sums lost accuracy at c = %u",
135 for (
double& v : Pi) v = std::max(v, 0.0);
137 std::vector<double> Pn(Pi.size(), 0.0);
138 for (
long nn = 1; nn <= nmax; ++nn) Pn[nn] = A * Pi[nn - 1] / std::min<long>(nn, c);
140 for (
unsigned j = 1; j + 1 <= c; ++j) sj += Pi[j - 1] * (1.0 / j - 1.0 / c);
141 Pn[0] = 1.0 - A / c - A * sj;
143 const double PW = D / (1.0 - omega);
144 const double theta = c * mu * (1.0 - omega);
145 const double EW = PW / theta;
146 const double EW2 = 2.0 * PW / (theta * theta);
147 const double VW = EW2 - EW * EW;
148 const double tailc = (A / c) * D;
149 const double om1 = 1.0 - omega;
150 const double EQ = tailc / (om1 * om1);
151 const double EQ2 = tailc * (1.0 + omega) / (om1 * om1 * om1);
152 const double m = c + 1.0;
154 for (
unsigned n0 = 0; n0 <= c; ++n0) EN2 += static_cast<double>(n0) * n0 * Pn[n0];
155 EN2 += tailc * (m * m / om1 + 2.0 * m * omega / (om1 * om1) + omega * (1.0 + omega) / (om1 * om1 * om1));
156 const double EN = EQ + A;
159 r.
set(
"meanQueueLength", EN);
160 r.
set(
"varQueueLength", EN2 - EN * EN);
161 r.
set(
"meanQueueLengthQ", EQ);
162 r.
set(
"varQueueLengthQ", EQ2 - EQ * EQ);
163 r.
set(
"meanWaitingTime", EW);
164 r.
set(
"varWaitingTime", VW);
165 r.
set(
"meanSojournTime", EW + 1.0 / mu);
166 r.
set(
"varSojournTime", VW + 1.0 / (mu * mu));
167 r.
set(
"probWait", PW);
168 r.
set(
"condMeanWaitingTime", 1.0 / theta);
169 r.
set(
"throughput", lambda);
170 r.
set(
"utilization", A / c);
171 r.
set(
"omega", omega);
172 r.
set(
"queueLengthDist", Pn);
173 r.
set(
"arrivalDist", Pi);
174 const std::function<double(
double)> Wccdf = [PW, theta](
double t) {
175 return t < 0.0 ? 1.0 : PW * std::exp(-theta * t);
177 const std::function<double(
double)> Tccdf = [PW, theta, mu](
double t) {
178 if (t < 0.0)
return 1.0;
179 if (std::fabs(theta - mu) > 1e-12 * mu) {
180 const double G = PW * mu / (mu - theta);
181 return (1.0 - G) * std::exp(-mu * t) + G * std::exp(-theta * t);
183 return (1.0 + PW * mu * t) * std::exp(-mu * t);
185 if (!o.tPoints.empty()) {
186 r.
set(
"tPoints", o.tPoints);
187 r.
set(
"waitingTimeCdf", cdf_of(Wccdf, o.tPoints));
188 r.
set(
"sojournTimeCdf", cdf_of(Tccdf, o.tPoints));
190 if (!o.percentiles.empty()) {
191 r.
set(
"percentiles", o.percentiles);
192 std::vector<double> wp(o.percentiles.size());
193 for (std::size_t i = 0; i < wp.size(); ++i)
194 wp[i] = std::max(0.0, std::log(PW / (1.0 - o.percentiles[i] / 100.0)) / theta);
195 r.
set(
"waitingTimePercentile", wp);
196 r.
set(
"sojournTimePercentile", pct_of(Tccdf, o.percentiles, 50.0 / std::min(mu, theta)));
198 r.
analyzer =
"exact:GI/M/" + std::to_string(c);