LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_gig1_rq.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_GIG1_RQ_H
6#define LINE_API_QSYS_QSYS_GIG1_RQ_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Robust Queueing (RQ) approximation of a G/GI/1 queue characterized by its
12 * arrival index of dispersion and the first two service moments.
13 *
14 * Templated port of matlab/src/api/qsys/qsys_gig1_rq.m, cross-checked against
15 * jar/src/main/java/jline/api/qsys/Qsys_gig1_rq.java.
16 *
17 * The mean steady-state workload is the value of a one-dimensional variational
18 * problem (Whitt and You 2018, eqs. (13), (16)-(18)):
19 *
20 * Zstar = sup_{x>=0} [ -(1-rho) x + sqrt( 2 rho x (I_a(x) + c_s^2)/mu ) ]
21 * W = max(0, Zstar/rho - (c_s^2+1)/(2 mu))
22 * Q = lambda W the mean number waiting
23 * X = Q + rho the mean number in system
24 *
25 * The objective is unimodal in practice but not guaranteed to be, so MATLAB
26 * brackets it with a 200-point log-spaced scan over [1e-6, 1e8] and refines
27 * the best bracket with fminbnd at TolX 1e-10, keeping the better of the scan
28 * value and the refined value. The port keeps the same scan and refines with
29 * golden-section search on the same bracket at the same TolX. fminbnd is
30 * golden section with parabolic acceleration, so on a bracket containing a
31 * single interior maximum the two locate the same point to within TolX; and
32 * because MATLAB and the port both return max(scan, refined), a refinement
33 * that lands short can never fall below the scan value. Where the objective
34 * really is multimodal both are equally at the mercy of the scan, and neither
35 * claims a global optimum.
36 *
37 * ARITHMETIC. The square root and the tolerance-driven search make this
38 * transcendental.
39 *
40 * The arrival process enters only through the callable I_a(x), so the caller
41 * supplies whatever index-of-dispersion model applies; for a renewal arrival
42 * stream I_a is the constant c_a^2.
43 *
44 * At rho <= 0 all four measures are zero, as in MATLAB. At rho >= 1 MATLAB
45 * returns Inf; the port raises instead, since the exact instantiations have no
46 * infinity and a silent Inf propagates into whatever consumes the result.
47 */
48
49#include <cstddef>
50#include <vector>
51
53#include "line/num/number.h"
54#include "line/util/error.h"
55
56namespace line {
57namespace qsys {
58
59template <class T>
61 T Z; ///< mean steady-state workload E[Z]
62 T W; ///< mean steady-state waiting time E[W]
63 T Q; ///< mean number waiting, lambda W
64 T X; ///< mean number in system, Q + rho
65};
66
67/**
68 * @brief Robust Queueing (RQ) approximation of a G/GI/1 queue characterized
69 * by its arrival index of dispersion and the first two service moments.
70 *
71 * @param rho traffic intensity lambda/mu
72 * @param mu service rate
73 * @param cs2 squared coefficient of variation of the service time
74 * @param IaFun_ callable, IaFun_(x) -> the arrival IDC I_a(x) at x > 0
75 */
76template <class T, class IaFun>
77Gig1RqResult<T> qsys_gig1_rq(const T& rho, const T& mu, const T& cs2, IaFun&& IaFun_) {
79 "qsys_gig1_rq requires transcendental arithmetic");
80 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
81 const T two = num_traits<T>::from_int(2);
83 if (rho <= zero) {
84 r.Z = r.W = r.Q = r.X = zero;
85 return r;
86 }
87 if (rho >= one)
88 throw InputError("qsys_gig1_rq: rho must be strictly less than 1 for a finite workload");
89 const T lambda = rho * mu;
90
91 // f(x) = -(1-rho) x + sqrt(2 rho x (I_a(x) + c_s^2)/mu), and f(x<=0) = 0.
92 auto f = [&](const T& x) -> T {
93 if (x <= zero) return zero;
94 const T ia = IaFun_(x);
95 T inner = two * rho * x * (ia + cs2) / mu;
96 if (inner < zero) inner = zero;
97 return -(one - rho) * x + detail::num_sqrt(inner);
98 };
99
100 // Coarse log-spaced scan over [1e-6, 1e8], 200 points, as in MATLAB.
101 const std::size_t NS = 200;
102 std::vector<T> xs(NS);
103 for (std::size_t i = 0; i < NS; ++i) {
104 const double e = -6.0 + 14.0 * static_cast<double>(i) / static_cast<double>(NS - 1);
105 xs[i] = T(num_traits<T>::from_double(std::pow(10.0, e)));
106 }
107 std::size_t imax = 0;
108 T best = f(xs[0]);
109 for (std::size_t i = 1; i < NS; ++i) {
110 const T v = f(xs[i]);
111 if (v > best) {
112 best = v;
113 imax = i;
114 }
115 }
116 T lo = xs[imax > 0 ? imax - 1 : 0];
117 T hi = xs[imax + 1 < NS ? imax + 1 : NS - 1];
118
119 // Golden-section refinement on the bracket at TolX 1e-10.
120 const T tolx = T(num_traits<T>::from_double(1e-10));
121 const T invphi = T(num_traits<T>::from_double(0.6180339887498949));
122 T c = hi - (hi - lo) * invphi;
123 T d = lo + (hi - lo) * invphi;
124 T fc = f(c), fd = f(d);
125 for (unsigned it = 0; it < 500u && hi - lo > tolx; ++it) {
126 if (fc > fd) {
127 hi = d;
128 d = c;
129 fd = fc;
130 c = hi - (hi - lo) * invphi;
131 fc = f(c);
132 } else {
133 lo = c;
134 c = d;
135 fc = fd;
136 d = lo + (hi - lo) * invphi;
137 fd = f(d);
138 }
139 }
140 const T refined = fc > fd ? fc : fd;
141 T Z = best > refined ? best : refined;
142 if (Z < zero) Z = zero;
143
144 r.Z = Z;
145 T W = Z / rho - (cs2 + one) / (two * mu);
146 if (W < zero) W = zero;
147 r.W = W;
148 r.Q = lambda * W;
149 r.X = r.Q + rho;
150 return r;
151}
152
153} // namespace qsys
154} // namespace line
155
156#endif // LINE_API_QSYS_QSYS_GIG1_RQ_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Gig1RqResult< T > qsys_gig1_rq(const T &rho, const T &mu, const T &cs2, IaFun &&IaFun_)
Robust Queueing (RQ) approximation of a G/GI/1 queue characterized by its arrival index of dispersion...
Number-type abstraction for the templated API port.
Shared return type and arithmetic helpers for the templated qsys port.
T Q
mean number waiting, lambda W
T Z
mean steady-state workload E[Z]
T X
mean number in system, Q + rho
T W
mean steady-state waiting time E[W]