LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
qsys_ggnm_diffusion.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_GGNM_DIFFUSION_H
6
#define LINE_API_QSYS_GGNM_DIFFUSION_H
7
8
/**
9
* @file
10
* @ingroup api_qsys
11
* Diffusion approximation for the G/GI/n/m queue.
12
*
13
* Templated port of matlab/src/api/qsys/qsys_ggnm_diffusion.m, cross-checked
14
* against jar/src/main/java/jline/api/qsys/Qsys_ggnm_diffusion.java.
15
*
16
* ONE DIFFUSION WITH TWO REGIONS. Below the staffing level the queue behaves
17
* like an infinite-server system, whose limit is NORMAL with variance-to-mean
18
* ratio the ASYMPTOTIC PEAKEDNESS
19
*
20
* z = 1 + (ca^2 - 1) omega_G, omega_G = int G^c(x)^2 dx / int G^c(x) dx
21
* (1.6)-(1.7)
22
*
23
* above it like a single-server queue, whose limit is EXPONENTIAL with
24
* variability v = (ca^2 + cs^2)/2 (3.7). The steady-state law is a normal piece
25
* spliced to an exponential piece and every measure is an integral of it (3.14):
26
*
27
* P(delay) = [1 + b Phi(b)/(phi(b)(1 - e^{-beta gamma/v}))]^-1, b = beta/sqrt(z)
28
* P(block) = f(gamma) v / sqrt(n) (7.5)
29
*
30
* with beta = sqrt(n)(1-rho) and gamma = m/sqrt(n).
31
*
32
* WHAT z SAYS. The service law enters the delay probability ONLY through
33
* omega_G: 1 for deterministic service, 1/2 for exponential, falling toward 0 as
34
* service gets more variable. At ca^2 = 1 the delay probability does not depend
35
* on the service law at all (z = 1), the long-standing M/GI/n-by-M/M/n
36
* approximation; away from ca^2 = 1 it does, and this is how much.
37
*
38
* At m = Inf this reduces to alpha(beta/sqrt(z)) with the Halfin-Whitt alpha,
39
* i.e. to `qsys_mmk_qed` when the service is exponential and ca^2 = 1.
40
*
41
* ARITHMETIC. erfc, exp and a quadrature: transcendental only.
42
*
43
* DIVERGENCE. The finite-waiting-room delay function is eq. (3.2) of the paper,
44
* whose printed form the available scan does not resolve. What is implemented is
45
* the unique form that (i) reduces to eq. (3.10) as gamma -> Inf and (ii)
46
* reproduces the exact M/M/n/m delay probability in the QED limit, which was
47
* checked numerically against the birth-death chain at n = 100, 400 and 1000.
48
*
49
* Reference: W. Whitt (2004). A diffusion approximation for the G/GI/n/m queue.
50
* Operations Research 52(6), 922-941.
51
*/
52
53
#include <algorithm>
54
#include <cmath>
55
#include <cstddef>
56
#include <functional>
57
#include <limits>
58
59
#include "
line/api/qsys/qsys_types.h
"
60
#include "
line/num/number.h
"
61
#include "
line/util/error.h
"
62
63
namespace
line
{
64
namespace
qsys
{
65
66
/** Steady-state measures of the G/GI/n/m diffusion approximation. */
67
template
<
class
T>
68
struct
QsysGgnmResult
{
69
T
beta
;
///< the QED server slack sqrt(n)(1-rho)
70
T
gamma
;
///< the scaled waiting room m/sqrt(n)
71
T
peakedness
;
///< z, the asymptotic peakedness
72
T
peakednessWeight
;
///< omega_G
73
T
variability
;
///< v = (ca^2+cs^2)/2
74
T
probDelay
;
///< P(an arrival waits)
75
T
probBlock
;
///< P(an arrival is blocked)
76
T
meanQueueLength
;
///< mean number waiting
77
T
meanNumber
;
///< mean number in system
78
T
meanWait
;
///< mean wait of an admitted arrival
79
T
utilization
;
///< min(rho,1)
80
T
throughput
;
///< lambda(1-P(block))
81
T
trafficIntensity
;
///< rho = lambda/(n mu)
82
};
83
84
namespace
detail {
85
86
/** Standard normal density. */
87
template
<
class
T>
88
T ggnm_phi(
const
T& x) {
89
using
std::exp;
90
using
std::sqrt;
91
return
exp(-x * x /
num_traits<T>::from_int
(2)) /
92
sqrt(
num_traits<T>::from_int
(2) *
num_traits<T>::from_double
(M_PI));
93
}
94
95
/** Standard normal cdf, through erfc. */
96
template
<
class
T>
97
T ggnm_Phi(
const
T& x) {
98
using
std::erfc;
99
using
std::sqrt;
100
return
erfc(-x / sqrt(
num_traits<T>::from_int
(2))) /
num_traits<T>::from_int
(2);
101
}
102
103
/**
104
* omega_G of eq. (1.7) by Simpson on a grid cut where the ccdf is negligible.
105
* The denominator is E[S], so only the numerator is integrated.
106
*/
107
template
<
class
T,
class
Ccdf>
108
T ggnm_omega(Ccdf&& ccdf,
const
T& ES,
double
tol, std::size_t panels) {
109
T hi = num_traits<T>::from_int(1);
110
const
T tolT = num_traits<T>::from_double(tol);
111
while
(ccdf(hi) > tolT) {
112
hi *= num_traits<T>::from_int(2);
113
if
(hi > num_traits<T>::from_double(1e12))
114
throw
InputError
(
"qsys_ggnm_diffusion: the service ccdf does not decay, so its "
115
"peakedness is undefined"
);
116
}
117
const
T h = hi / num_traits<T>::from_int(
static_cast<
long
>
(panels));
118
const
T g0 = ccdf(num_traits<T>::from_int(0));
119
const
T gn = ccdf(hi);
120
T sum = g0 * g0 + gn * gn;
121
for
(std::size_t i = 1; i < panels; ++i) {
122
const
T g = ccdf(T(num_traits<T>::from_int(
static_cast<
long
>
(i)) * h));
123
sum += num_traits<T>::from_int(i % 2 == 1 ? 4 : 2) * g * g;
124
}
125
return
(h / num_traits<T>::from_int(3) * sum) / ES;
126
}
127
128
}
// namespace detail
129
130
/**
131
* @brief Diffusion approximation for the G/GI/n/m queue.
132
*
133
* @param lambda arrival rate
134
* @param mu service rate of one server
135
* @param n number of servers, n >= 1
136
* @param m extra waiting spaces; infinity for an unbounded queue
137
* @param ca coefficient of variation of the interarrival time
138
* @param cs coefficient of variation of the service time
139
* @param serviceCcdf G^c(x) = P(S > x); empty takes the exponential of rate mu
140
* @param tol service-tail cut for the peakedness integral
141
* @param panels Simpson panels for it
142
*/
143
template
<
class
T>
144
QsysGgnmResult<T>
qsys_ggnm_diffusion
(
145
const
T& lambda,
const
T& mu,
unsigned
n,
double
m,
const
T& ca,
const
T& cs,
146
const
std::function<T(
const
T&)>& serviceCcdf = std::function<T(
const
T&)>(),
147
double
tol = 1e-12, std::size_t panels = 4000) {
148
static_assert
(
num_traits<T>::has_transcendental
,
149
"qsys_ggnm_diffusion needs erfc, exp and a quadrature"
);
150
using
std::exp;
151
using
std::expm1;
152
using
std::sqrt;
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_ggnm_diffusion: the arrival and service rates must be positive"
);
158
if
(n < 1)
throw
InputError
(
"qsys_ggnm_diffusion: the number of servers n must be at least 1"
);
159
if
(m < 0)
160
throw
InputError
(
"qsys_ggnm_diffusion: the number of extra waiting spaces m must be "
161
"non-negative"
);
162
163
const
T nT =
num_traits<T>::from_int
(
static_cast<
long
>
(n));
164
const
T ca2 = ca * ca;
165
const
T cs2 = cs * cs;
166
const
T ES = one / mu;
167
const
T rho = lambda / (nT * mu);
168
const
T beta = sqrt(nT) * (one - rho);
// eq. (0.1)
169
const
bool
finiteRoom = std::isfinite(m);
170
const
T gamma = finiteRoom ? T(
num_traits<T>::from_double
(m) / sqrt(nT))
171
:
num_traits<T>::from_double
(std::numeric_limits<double>::infinity());
172
173
const
T omega = serviceCcdf ? detail::ggnm_omega<T>(serviceCcdf, ES, tol, panels)
174
:
num_traits<T>::from_rational
(1, 2);
175
const
T z = one + (ca2 - one) * omega;
// eq. (1.6)
176
if
(z <= zero)
177
throw
InputError
(
"qsys_ggnm_diffusion: the asymptotic peakedness came out non-positive; "
178
"check ca and the service ccdf"
);
179
const
T v = (ca2 + cs2) / two;
// eq. (3.7), weight w = 1
180
const
T b = beta / sqrt(z);
181
const
T r = beta / v;
182
183
// The tail factor is negative together with r when the queue is overloaded,
184
// so the ratio below stays positive on both sides of beta = 0.
185
const
T tail = finiteRoom ? T(-expm1(-r * gamma)) : one;
186
T alpha, meanAbove, densityAtTop;
187
if
(
num_abs
(r) <
num_traits<T>::from_double
(1e-14)) {
188
// beta = 0: the exponential piece degenerates to a uniform on [0,gamma].
189
if
(!finiteRoom)
190
throw
InputError
(
"qsys_ggnm_diffusion: with beta = 0 the queue needs a finite waiting "
191
"room to be stable"
);
192
alpha = one / (one + detail::ggnm_Phi(b) / (detail::ggnm_phi(b) * gamma / sqrt(z)));
193
meanAbove = gamma / two;
194
densityAtTop = alpha / gamma;
195
}
else
{
196
alpha = one / (one + b * detail::ggnm_Phi(b) / (detail::ggnm_phi(b) * tail));
197
if
(finiteRoom) {
198
const
T e = exp(-r * gamma);
199
meanAbove = (one / r - (gamma + one / r) * e) / tail;
200
densityAtTop = alpha * r * e / tail;
201
}
else
{
202
meanAbove = one / r;
203
densityAtTop = zero;
204
}
205
}
206
207
// Mean of the normal piece, N(-beta, z) conditioned below 0.
208
const
T meanBelow = -beta - sqrt(z) * detail::ggnm_phi(b) / detail::ggnm_Phi(b);
209
const
T meanScaled = (one - alpha) * meanBelow + alpha * meanAbove;
210
211
QsysGgnmResult<T>
res;
212
res.
beta
= beta;
213
res.
gamma
= gamma;
214
res.
peakedness
= z;
215
res.
peakednessWeight
= omega;
216
res.
variability
= v;
217
res.
probDelay
= alpha;
218
// Eq. (7.5): the loss rate at the upper boundary over the arrival rate is
219
// the density there times v / sqrt(n).
220
res.
probBlock
= zero;
221
if
(finiteRoom) {
222
T pb = densityAtTop * v / sqrt(nT);
223
if
(pb < zero) pb = zero;
224
if
(pb > one) pb = one;
225
res.
probBlock
= pb;
226
}
227
res.
meanQueueLength
= sqrt(nT) * alpha * meanAbove;
228
res.
meanNumber
= nT + sqrt(nT) * meanScaled;
229
res.
throughput
= lambda * (one - res.
probBlock
);
230
res.
meanWait
= res.
throughput
> zero ? T(res.
meanQueueLength
/ res.
throughput
) : zero;
231
res.
utilization
= detail::num_min(rho, one);
232
res.
trafficIntensity
= rho;
233
return
res;
234
}
235
236
}
// namespace qsys
237
}
// namespace line
238
239
#endif
// LINE_API_QSYS_GGNM_DIFFUSION_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
error.h
The exception types the port throws.
line::qsys
Definition
qsys_bmapm1.h:58
line::qsys::qsys_ggnm_diffusion
QsysGgnmResult< T > qsys_ggnm_diffusion(const T &lambda, const T &mu, unsigned n, double m, const T &ca, const T &cs, const std::function< T(const T &)> &serviceCcdf=std::function< T(const T &)>(), double tol=1e-12, std::size_t panels=4000)
Diffusion approximation for the G/GI/n/m queue.
Definition
qsys_ggnm_diffusion.h:144
line
Definition
aoi_dist2ph.h:52
line::num_abs
T num_abs(const T &v)
Definition
number.h:172
number.h
Number-type abstraction for the templated API port.
qsys_types.h
Shared return type and arithmetic helpers for the templated qsys port.
line::num_traits
Definition
number.h:111
line::qsys::QsysGgnmResult
Steady-state measures of the G/GI/n/m diffusion approximation.
Definition
qsys_ggnm_diffusion.h:68
line::qsys::QsysGgnmResult::throughput
T throughput
lambda(1-P(block))
Definition
qsys_ggnm_diffusion.h:80
line::qsys::QsysGgnmResult::trafficIntensity
T trafficIntensity
rho = lambda/(n mu)
Definition
qsys_ggnm_diffusion.h:81
line::qsys::QsysGgnmResult::meanWait
T meanWait
mean wait of an admitted arrival
Definition
qsys_ggnm_diffusion.h:78
line::qsys::QsysGgnmResult::peakedness
T peakedness
z, the asymptotic peakedness
Definition
qsys_ggnm_diffusion.h:71
line::qsys::QsysGgnmResult::variability
T variability
v = (ca^2+cs^2)/2
Definition
qsys_ggnm_diffusion.h:73
line::qsys::QsysGgnmResult::meanQueueLength
T meanQueueLength
mean number waiting
Definition
qsys_ggnm_diffusion.h:76
line::qsys::QsysGgnmResult::peakednessWeight
T peakednessWeight
omega_G
Definition
qsys_ggnm_diffusion.h:72
line::qsys::QsysGgnmResult::probBlock
T probBlock
P(an arrival is blocked).
Definition
qsys_ggnm_diffusion.h:75
line::qsys::QsysGgnmResult::utilization
T utilization
min(rho,1)
Definition
qsys_ggnm_diffusion.h:79
line::qsys::QsysGgnmResult::probDelay
T probDelay
P(an arrival waits).
Definition
qsys_ggnm_diffusion.h:74
line::qsys::QsysGgnmResult::gamma
T gamma
the scaled waiting room m/sqrt(n)
Definition
qsys_ggnm_diffusion.h:70
line::qsys::QsysGgnmResult::beta
T beta
the QED server slack sqrt(n)(1-rho)
Definition
qsys_ggnm_diffusion.h:69
line::qsys::QsysGgnmResult::meanNumber
T meanNumber
mean number in system
Definition
qsys_ggnm_diffusion.h:77
include
line
api
qsys
qsys_ggnm_diffusion.h
Generated by
1.18.0