LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_gig1_bnds_extremal.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_GIG1_BNDS_EXTREMAL_H
6#define LINE_API_QSYS_GIG1_BNDS_EXTREMAL_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Extremal two-moment bounds for the GI/GI/1 queue.
12 *
13 * Templated port of matlab/src/api/qsys/qsys_gig1_bnds_extremal.m, cross-checked
14 * against jar/src/main/java/jline/api/qsys/Qsys_gig1_bnds_extremal.java.
15 *
16 * Two moments do not determine E[W]; they determine a SET of possible values,
17 * and the width of that set is the honest uncertainty in any two-moment
18 * approximation. The ends are attained by extremal laws:
19 *
20 * lower D(1) interarrivals and a three-point service law on multiples of it,
21 * E[W] = rho((1+cs^2)rho - 1)^+ / (2(1-rho)) (2.12)
22 * upper two-point laws: an interarrival atom at 0, and a service law whose
23 * upper atom runs to infinity as its probability vanishes (3.2)
24 *
25 * Making an interarrival time larger only empties the queue once, but making a
26 * service time larger delays every customer behind it, which is why the two ends
27 * look so different. The upper end reduces to a D(1/p)/RS(D(rho),p)/1 model with
28 * p = 1/(1+ca^2), whose mean wait is evaluated by Spitzer's identity
29 * sum_n E[Sn^+]/n with Sn = rho(NB(n,1-p)+n) - n/p (Algorithm 1). The closed
30 * form (3.4) uses the D/M/1 root delta = exp(-(1-delta)/rho) and is within about
31 * 1% of it.
32 *
33 * ARITHMETIC. The tight bound is a truncated infinite sum and delta comes out of
34 * a bisection, so nothing here is exact; the instantiation is restricted to the
35 * transcendental types.
36 *
37 * PARITY. All four codebases form the negative binomial pmf in LOG space,
38 * log P(NB(n,1-p)=k) = lgamma(n+k) - lgamma(k+1) - lgamma(n)
39 * + n log p + k log(1-p),
40 * from one precomputed table of log-gammas, rather than by the ratio recursion
41 * Algorithm 1 prints. The two are equivalent, but the recursion accumulates
42 * rounding over thousands of multiplications and would leave the ports agreeing
43 * only to about 1e-4.
44 *
45 * Reference: Y. Chen, W. Whitt (2020). Algorithms for the upper bound mean
46 * waiting time in the GI/GI/1 queue. Queueing Systems 94, 327-356.
47 */
48
49#include <cmath>
50#include <cstddef>
51#include <vector>
52
54#include "line/num/number.h"
55#include "line/util/error.h"
56
57namespace line {
58namespace qsys {
59
60/** The bounds, all as TIMES IN QUEUE: add 1/mu for a response time. */
61template <class T>
63 T trafficIntensity; ///< rho = lambda/mu
64 T lowerBound; ///< the tight lower bound, eq. (2.12)
65 T upperBound; ///< the conjectured tight upper bound, eq. (3.2)
66 T upperBoundClosed; ///< the closed-form upper bound, eq. (3.4)
67 T upperBoundDaley; ///< Daley's bound, eq. (2.7)
68 T upperBoundKingman; ///< Kingman's bound, eq. (2.6)
69 T heavyTraffic; ///< the heavy-traffic approximation, eq. (2.9)
70 T delta; ///< the D/M/1 root behind upperBoundClosed
71 T relativeWidth; ///< (upper-lower)/upper, what two moments leave undetermined
72 bool tightComputed; ///< whether the O(K*N) bound was evaluated
73};
74
75namespace detail {
76
77/**
78 * The D/M/1 root of eq. (3.5), delta = exp(-(1-delta)/rho), in (0,1).
79 *
80 * g(delta) = delta - exp(-(1-delta)/rho) is negative at 0 and positive just
81 * below 1, where the second root delta = 1 sits, so bisection on [0,1) finds the
82 * wanted root without landing on the trivial one.
83 */
84template <class T>
85T extremal_delta(const T& rho) {
86 using std::exp;
87 const T two = num_traits<T>::from_int(2);
90 for (int i = 0; i < 200; ++i) {
91 const T mid = (lo + hi) / two;
92 if (mid - exp(-(num_traits<T>::from_int(1) - mid) / rho) < num_traits<T>::from_int(0)) {
93 lo = mid;
94 } else {
95 hi = mid;
96 }
97 }
98 return (lo + hi) / two;
99}
100
101/** Algorithm 1: the mean waiting time of the extremal model, by the log pmf. */
102template <class T>
103T extremal_tight(const T& rho, const T& ca2, const T& cs2, std::size_t K, std::size_t N) {
104 using std::exp;
105 using std::lgamma;
106 using std::log;
107 using std::log1p;
108 const T zero = num_traits<T>::from_int(0);
109 const T one = num_traits<T>::from_int(1);
110 const T two = num_traits<T>::from_int(2);
111 const T p = one / (one + ca2);
112 const T logp = log(p);
113 const T log1mp = log1p(-p);
114 std::vector<T> lg(N + K + 2, zero);
115 for (std::size_t i = 1; i < lg.size(); ++i)
116 lg[i] = lgamma(num_traits<T>::from_int(static_cast<long>(i)));
117 T total = rho * ca2 + rho * rho * cs2 / (two * (one - rho));
118 for (std::size_t k = 1; k <= K; ++k) {
119 T s = zero;
120 for (std::size_t n = 1; n <= N; ++n) {
121 const T nT = num_traits<T>::from_int(static_cast<long>(n));
122 const T step = num_traits<T>::from_int(static_cast<long>(n + k)) * rho - nT / p;
123 if (step > zero) {
124 const T lpmf = lg[n + k] - lg[k + 1] - lg[n] + nT * logp +
125 num_traits<T>::from_int(static_cast<long>(k)) * log1mp;
126 s += exp(lpmf) * step / nT;
127 }
128 }
129 total += s;
130 }
131 return total;
132}
133
134} // namespace detail
135
136/**
137 * @brief Extremal two-moment bounds for the GI/GI/1 queue.
138 *
139 * @param lambda arrival rate
140 * @param mu service rate
141 * @param ca coefficient of variation of the interarrival time
142 * @param cs coefficient of variation of the service time
143 * @param K truncation of the negative binomial value
144 * @param N truncation of the random-walk length
145 * @param skipTight skip the O(K*N) tight bound and return the closed forms only
146 */
147template <class T>
148Gig1ExtremalResult<T> qsys_gig1_bnds_extremal(const T& lambda, const T& mu, const T& ca,
149 const T& cs, std::size_t K = 4000,
150 std::size_t N = 2000, bool skipTight = false) {
152 "qsys_gig1_bnds_extremal truncates an infinite sum, so it needs inexact arithmetic");
153 const T zero = num_traits<T>::from_int(0);
154 const T one = num_traits<T>::from_int(1);
155 const T two = num_traits<T>::from_int(2);
156 if (lambda <= zero || mu <= zero)
157 throw InputError("qsys_gig1_bnds_extremal: the arrival and service rates must be positive");
158 const T rho = lambda / mu;
159 if (rho >= one)
160 throw InputError("qsys_gig1_bnds_extremal: the bounds require a stable queue, rho < 1");
161 const T ca2 = ca * ca;
162 const T cs2 = cs * cs;
163 // The reference sets E[U] = 1, so every waiting time carries the factor
164 // 1/lambda, that time unit expressed in the caller's units.
165 const T scale = one / lambda;
166
168 r.trafficIntensity = rho;
169 const T lowNum = (one + cs2) * rho - one;
170 r.lowerBound = scale * rho * (lowNum > zero ? lowNum : zero) / (two * (one - rho));
171 r.upperBoundKingman = scale * rho * rho * (ca2 / (rho * rho) + cs2) / (two * (one - rho));
173 scale * rho * rho * ((two - rho) * ca2 / rho + cs2) / (two * (one - rho));
174 r.heavyTraffic = scale * rho * rho * (ca2 + cs2) / (two * (one - rho));
175 r.delta = detail::extremal_delta(rho);
177 scale * (two * (one - rho) * rho / (one - r.delta) * ca2 + rho * rho * cs2) /
178 (two * (one - rho));
179 if (skipTight) {
181 r.tightComputed = false;
182 } else {
183 r.upperBound = scale * detail::extremal_tight(rho, ca2, cs2, K, N);
184 r.tightComputed = true;
185 }
186 r.relativeWidth = r.upperBound > zero ? T((r.upperBound - r.lowerBound) / r.upperBound) : zero;
187 return r;
188}
189
190} // namespace qsys
191} // namespace line
192
193#endif // LINE_API_QSYS_GIG1_BNDS_EXTREMAL_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Gig1ExtremalResult< T > qsys_gig1_bnds_extremal(const T &lambda, const T &mu, const T &ca, const T &cs, std::size_t K=4000, std::size_t N=2000, bool skipTight=false)
Extremal two-moment bounds for the GI/GI/1 queue.
Number-type abstraction for the templated API port.
Shared return type and arithmetic helpers for the templated qsys port.
The bounds, all as TIMES IN QUEUE: add 1/mu for a response time.
T upperBoundClosed
the closed-form upper bound, eq. (3.4)
T upperBoundKingman
Kingman's bound, eq. (2.6).
T upperBoundDaley
Daley's bound, eq. (2.7).
T delta
the D/M/1 root behind upperBoundClosed
T heavyTraffic
the heavy-traffic approximation, eq. (2.9)
T upperBound
the conjectured tight upper bound, eq. (3.2)
T relativeWidth
(upper-lower)/upper, what two moments leave undetermined
bool tightComputed
whether the O(K*N) bound was evaluated
T lowerBound
the tight lower bound, eq. (2.12)