LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_gigk_rqt.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_GIGK_RQT_H
6#define LINE_API_QSYS_QSYS_GIGK_RQT_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Robust Queueing Theory (RQT) worst-case system time of a G/G/k FCFS queue.
12 *
13 * Templated port of matlab/src/api/qsys/qsys_gigk_rqt.m, cross-checked against
14 * jar/src/main/java/jline/api/qsys/Qsys_gigk_rqt.java.
15 *
16 * The arrival and service processes are not described by distributions but by
17 * the polyhedral uncertainty sets
18 *
19 * U^a = { T : (sum_{i=k+1}^n T_i - (n-k)/lambda)/(n-k)^(1/alpha_a) >= -Gamma_a }
20 * U^s = { X : (sum_{i=k}^n X_i - (n-k+1)/mu)/(n-k+1)^(1/alpha_s) <= Gamma_s }
21 *
22 * whose shape follows the (generalized) central limit theorem: alpha = 2 is the
23 * finite-variance regime, alpha in (1,2) the heavy-tailed one. Performance
24 * analysis is then a worst-case optimization rather than an expectation.
25 *
26 * W is the closed-form bound of Theorem 3 (Theorem 8 when the two tails differ,
27 * with ab = min(alpha_a,alpha_s)),
28 *
29 * W <= (ab-1)/ab^(ab/(ab-1)) lambda^(1/(ab-1))
30 * (Gamma_a + Gamma_s/k^(1/ab))^(ab/(ab-1)) / (1-rho)^(1/(ab-1)) + k/lambda,
31 *
32 * which for k = 1 reduces to Theorem 2 and, at ab = 2, to the Kingman-like form
33 * (lambda/4)(Gamma_a+Gamma_s)^2/(1-rho) + 1/lambda. Sworst is the exact worst
34 * case over the uncertainty sets, eq. (45): the supremum over the integer
35 * x = nu-j+1 >= 1 of
36 *
37 * x/mu + Gamma_s x^(1/alpha_s) - k(x-1)/lambda + Gamma_a (k(x-1))^(1/alpha_a).
38 *
39 * The arrival deviation ADDS to the worst case, since the adversary shortens the
40 * interarrival times; the sign printed in eq. (12) is easily misread as a
41 * subtraction of the whole arrival bracket, and reading it that way puts Sworst
42 * an order of magnitude below W.
43 *
44 * W is a SYSTEM time (waiting plus service), and its additive term is k/lambda
45 * rather than the mean service time 1/mu.
46 *
47 * ARITHMETIC. Real exponents make this transcendental. At rho >= 1 MATLAB
48 * returns Inf; the port raises instead, as the rest of the qsys port does.
49 *
50 * Reference: C. Bandi, D. Bertsimas, N. Youssef (2015). Robust Queueing Theory.
51 * Operations Research 63(3), 676-700.
52 */
53
54#include <cmath>
55#include <cstddef>
56#include <vector>
57
59#include "line/num/number.h"
60#include "line/util/error.h"
61
62namespace line {
63namespace qsys {
64
65template <class T>
67 T W; ///< closed-form bound on the system time (Theorem 3 / Theorem 8)
68 T rhohat; ///< modified utilization, so that M/M/1 relations still hold
69 T Sworst; ///< exact worst-case system time over the uncertainty sets
70};
71
72/**
73 * @brief Robust Queueing Theory (RQT) worst-case system time of a G/G/k FCFS
74 * queue.
75 *
76 * @param lambda arrival rate
77 * @param mu service rate of each server
78 * @param Gamma_a variability parameter of the arrival uncertainty set
79 * @param Gamma_s variability parameter of the service uncertainty set
80 * @param k number of servers
81 * @param alpha_a arrival tail coefficient in (1,2]
82 * @param alpha_s service tail coefficient in (1,2]
83 */
84template <class T>
85GigkRqtResult<T> qsys_gigk_rqt(const T& lambda, const T& mu, const T& Gamma_a, const T& Gamma_s,
86 std::size_t k, const T& alpha_a, const T& alpha_s) {
88 "qsys_gigk_rqt requires transcendental arithmetic");
89 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
90 const T kk = num_traits<T>::from_int(static_cast<int>(k));
91 if (alpha_a <= one || alpha_a > T(one + one) || alpha_s <= one || alpha_s > T(one + one))
92 throw InputError("qsys_gigk_rqt: tail coefficients must lie in (1,2]");
93
95 const T rho = lambda / (kk * mu);
96 if (lambda <= zero) {
97 r.W = one / mu;
98 r.rhohat = zero;
99 r.Sworst = one / mu;
100 return r;
101 }
102 if (rho >= one)
103 throw InputError("qsys_gigk_rqt: rho must be strictly less than 1 for a finite system time");
104
105 // Theorem 8 collapses to Theorem 3 when the two tails agree.
106 const T ab = alpha_a < alpha_s ? alpha_a : alpha_s;
107 const T beta = Gamma_a + Gamma_s / detail::num_pow(kk, T(one / ab));
108 if (beta <= zero) {
109 // a nonpositive effective variability leaves only the deterministic term
110 r.W = kk / lambda;
111 } else {
112 const T e = ab / (ab - one);
113 r.W = (ab - one) / detail::num_pow(ab, e) * detail::num_pow(lambda, T(one / (ab - one))) *
114 detail::num_pow(beta, e) / detail::num_pow(T(one - rho), T(one / (ab - one))) +
115 kk / lambda;
116 }
117 r.rhohat = detail::rhohat_from_W(r.W, lambda);
118
119 // Exact worst case, eq. (45), over the integer lattice x >= 1.
120 auto obj = [&](const T& x) -> T {
121 T y = T(x - one);
122 if (y < zero) y = zero;
123 return x / mu + Gamma_s * detail::num_pow(x, T(one / alpha_s)) - kk * y / lambda +
124 Gamma_a * detail::num_pow(T(kk * y), T(one / alpha_a));
125 };
126 // the continuous maximizer of the bounding problem, eq. (16), sizes the scan
127 double xstar = 1.0;
128 if (beta > zero) {
129 const T xs = detail::num_pow(T(lambda * beta / (ab * (one - rho))), T(ab / (ab - one)));
130 xstar = num_traits<T>::to_double(xs);
131 }
132 double xhi = std::max(4.0, std::ceil(4.0 * xstar));
133 if (!(xhi > 0.0) || !std::isfinite(xhi)) xhi = 4.0;
134 const std::size_t NS = 400;
135 std::vector<double> xs;
136 xs.reserve(NS);
137 for (std::size_t i = 0; i < NS; ++i) {
138 const double e = std::log10(xhi) * static_cast<double>(i) / static_cast<double>(NS - 1);
139 const double x = std::max(1.0, std::floor(std::pow(10.0, e) + 0.5));
140 if (xs.empty() || x != xs.back()) xs.push_back(x); // unique(round(logspace(...)))
141 }
142 std::size_t imax = 0;
143 T best = obj(T(num_traits<T>::from_double(xs[0])));
144 for (std::size_t i = 1; i < xs.size(); ++i) {
145 const T v = obj(T(num_traits<T>::from_double(xs[i])));
146 if (v > best) {
147 best = v;
148 imax = i;
149 }
150 }
151 // Refine on the continuous relaxation, then round back onto the lattice.
152 T lo = num_traits<T>::from_double(xs[imax > 0 ? imax - 1 : 0]);
153 T hi = num_traits<T>::from_double(xs[imax + 1 < xs.size() ? imax + 1 : xs.size() - 1]);
154 if (hi > lo) {
155 const T tolx = num_traits<T>::from_double(1e-8);
156 const T invphi = num_traits<T>::from_double(0.6180339887498949);
157 T c = hi - (hi - lo) * invphi;
158 T d = lo + (hi - lo) * invphi;
159 T fc = obj(c), fd = obj(d);
160 for (unsigned it = 0; it < 500u && hi - lo > tolx; ++it) {
161 if (fc > fd) {
162 hi = d;
163 d = c;
164 fd = fc;
165 c = hi - (hi - lo) * invphi;
166 fc = obj(c);
167 } else {
168 lo = c;
169 c = d;
170 fc = fd;
171 d = lo + (hi - lo) * invphi;
172 fd = obj(d);
173 }
174 }
175 const double xc = num_traits<T>::to_double(T((lo + hi) / (one + one)));
176 const double cand[2] = {std::floor(xc), std::ceil(xc)};
177 for (int i = 0; i < 2; ++i) {
178 if (cand[i] >= 1.0) {
179 const T v = obj(T(num_traits<T>::from_double(cand[i])));
180 if (v > best) best = v;
181 }
182 }
183 }
184 r.Sworst = best;
185 return r;
186}
187
188} // namespace qsys
189} // namespace line
190
191#endif // LINE_API_QSYS_QSYS_GIGK_RQT_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
GigkRqtResult< T > qsys_gigk_rqt(const T &lambda, const T &mu, const T &Gamma_a, const T &Gamma_s, std::size_t k, const T &alpha_a, const T &alpha_s)
Robust Queueing Theory (RQT) worst-case system time of a G/G/k FCFS queue.
Number-type abstraction for the templated API port.
Shared return type and arithmetic helpers for the templated qsys port.
T W
closed-form bound on the system time (Theorem 3 / Theorem 8)
T rhohat
modified utilization, so that M/M/1 relations still hold
T Sworst
exact worst-case system time over the uncertainty sets