LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
me_gegecn.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_ME_ME_GEGECN_H
6#define LINE_API_ME_ME_GEGECN_H
7
8/**
9 * @file
10 * @ingroup api_me
11 * Censored GE/GE/c/K;N queue by entropy maximisation.
12 *
13 * Templated port of `matlab/src/api/me/me_gegecn.m`: the single-class censored
14 * FCFS queue of Kouvatsos (1994), Section 4.1, equations (4.1)-(4.3).
15 *
16 * WHAT "CENSORED" MEANS HERE. The queue holds at most N jobs and never fewer
17 * than K: arrivals finding N jobs present are turned away, and departures are
18 * not allowed from state K. For a queue embedded in an OPEN network K is always
19 * 0; a positive K arises in closed networks, where it records the minimum
20 * occupancy forced by the remaining stations being full.
21 *
22 * THE SOLUTION IS CLOSED FORM, NOT AN ITERATION. The ME state probabilities
23 * coincide with the global balance solution
24 *
25 * p(n) = p(K) G_n x^h(n) y^f(n), n = K+1,...,N
26 *
27 * with G_n = prod_{l=K+1}^{m(n)} g(l), J = max(c,K+1), h(n) = max(0,n-J),
28 * f(n) = max(0,n-N+1) and m(n) = max{K+1, min(c,n)}. The Lagrangian
29 * coefficients g(l), x and y come from raw system data. They are INVARIANT to
30 * N and K, which is why letting K -> 0 and N -> infinity recovers the stable
31 * GE/GE/c solution `me_oqn` uses -- the same coefficients serve both.
32 *
33 * WHY THIS PORT WORKS IN LOGS, as the reference does. `x^(N-J)` overflows on a
34 * saturated queue with a large buffer, and the log form additionally makes the
35 * rho = 1 case (x = 1) fall out of the same expression instead of needing the
36 * separate p(K) branch of (4.2).
37 */
38
39#include <algorithm>
40#include <cmath>
41#include <cstddef>
42#include <vector>
43
45#include "line/num/number.h"
46#include "line/util/error.h"
47
48namespace line {
49namespace me {
50
51/** What `me_gegecn` returns: the law and the four means read off it. */
52template <class T>
54 std::vector<T> p; ///< p[idx] = Pr{n = K + idx}, idx = 0..N-K
55 T L; ///< mean number in the queue, sum_n n p(n)
56 T U; ///< utilization, E[min(n,c)]/c
57 T PB; ///< probability an arrival of the aggregate stream is blocked
58 T Lq; ///< mean number WAITING, L - E[min(n,c)]
59};
60
61/**
62 * Port of `me_gegecn`.
63 *
64 * @param lambda arrival rate OFFERED to the queue, the arrivals turned away
65 * included
66 * @param Ca squared coefficient of variation of the interarrival times; the
67 * GE distribution needs at least 1
68 * @param mu service rate of ONE server
69 * @param Cs squared coefficient of variation of the service times, >= 1
70 * @param c number of servers, finite and at least 1
71 * @param K minimum number of jobs in the queue, >= 0
72 * @param N buffer capacity in jobs, service included, finite and > K
73 */
74template <class T>
75GegecnResult<T> me_gegecn(const T& lambda, const T& Ca, const T& mu, const T& Cs, long c, long K,
76 long N) {
78 "me_gegecn requires transcendental arithmetic: the state law is assembled in "
79 "logarithms so that a saturated queue with a large buffer does not overflow");
80 using std::exp;
81 using std::log;
82 const T zero = num_traits<T>::from_int(0);
83 const T one = num_traits<T>::from_int(1);
84 const T two = num_traits<T>::from_int(2);
85
86 if (c < 1) throw InputError("me_gegecn: requires a finite number of servers c >= 1.");
87 if (N <= K) throw InputError("me_gegecn: requires N > K.");
88 if (num_traits<T>::to_double(Ca) < 1.0 - 1e-12 ||
89 num_traits<T>::to_double(Cs) < 1.0 - 1e-12)
90 throw InputError(
91 "me_gegecn: requires Ca >= 1 and Cs >= 1: the GE distribution is not defined for "
92 "scv < 1.");
93 if (!(mu > zero)) throw InputError("me_gegecn: requires a positive service rate.");
94
95 const T tau = T(two / (Ca + one));
96 const T sigma = T(two / (Cs + one));
97 const T cT = num_traits<T>::from_int(c);
98 const T rho = T(lambda / (cT * mu));
99
100 const long J = std::max(c, K + 1);
101 const T den1 = T(sigma * (one - tau) + tau);
102 const T den2 = T(tau * rho * (one - sigma) + sigma);
103
104 // Lagrangian coefficients g(l), l = K+1,...,J, stored 1-based as in the
105 // reference so the three-way first-entry branch reads the same.
106 std::vector<T> g(static_cast<std::size_t>(J) + 1, one);
107 const T Kp1 = num_traits<T>::from_int(K + 1);
108 if (K < c - 1) {
109 g[static_cast<std::size_t>(K + 1)] = T(tau * cT * rho / (Kp1 * den1));
110 } else if (K == c - 1) {
111 g[static_cast<std::size_t>(K + 1)] = T(tau * sigma * rho / den2);
112 } else {
113 g[static_cast<std::size_t>(K + 1)] = T((den1 / den2) * tau * rho);
114 }
115 for (long l = K + 2; l <= J; ++l) {
116 const T lT = num_traits<T>::from_int(l);
117 const T lm1 = num_traits<T>::from_int(l - 1);
118 if (l < J) {
119 g[static_cast<std::size_t>(l)] =
120 T((tau * cT * rho + lm1 * sigma * (one - tau)) / (lT * den1));
121 } else {
122 const T Jm1 = num_traits<T>::from_int(J - 1);
123 const T JT = num_traits<T>::from_int(J);
124 g[static_cast<std::size_t>(l)] =
125 T(sigma * (tau * cT * rho + Jm1 * sigma * (one - tau)) / (JT * den2));
126 }
127 }
128
129 const T x = T((tau * rho + sigma * (one - tau)) / den2);
130 const T y = T(one / (one - (one - sigma) * x));
131
132 // cumlogg(i) = sum_{l=K+1}^{K+i} log g(l), i = 1..J-K
133 const std::size_t ng = static_cast<std::size_t>(J - K);
134 std::vector<T> cumlogg(ng, zero);
135 {
136 T acc = zero;
137 for (std::size_t i = 0; i < ng; ++i) {
138 acc += T(log(g[static_cast<std::size_t>(K + 1) + i]));
139 cumlogg[i] = acc;
140 }
141 }
142
143 const std::size_t nn = static_cast<std::size_t>(N - K + 1);
144 const T logx = T(log(x));
145 const T logy = T(log(y));
146 std::vector<T> logp(nn, zero);
147 for (std::size_t idx = 0; idx < nn; ++idx) {
148 const long n = K + static_cast<long>(idx);
149 if (n > K) {
150 const long m = std::max(K + 1, std::min(c, n));
151 logp[idx] = cumlogg[static_cast<std::size_t>(m - K) - 1];
152 }
153 const long h = std::max<long>(0, n - J);
154 const long f = std::max<long>(0, n - N + 1);
155 if (h > 0) logp[idx] += num_traits<T>::from_int(h) * logx;
156 if (f > 0) logp[idx] += num_traits<T>::from_int(f) * logy;
157 }
158 // Shift by the maximum before exponentiating, then normalize.
159 T mx = logp[0];
160 for (const T& v : logp)
161 if (v > mx) mx = v;
162 GegecnResult<T> res;
163 res.p.assign(nn, zero);
164 T tot = zero;
165 for (std::size_t idx = 0; idx < nn; ++idx) {
166 res.p[idx] = T(exp(T(logp[idx] - mx)));
167 tot += res.p[idx];
168 }
169 for (T& v : res.p) v = T(v / tot);
170
171 T L = zero, Ebusy = zero;
172 for (std::size_t idx = 0; idx < nn; ++idx) {
173 const long n = K + static_cast<long>(idx);
174 L += num_traits<T>::from_int(n) * res.p[idx];
175 Ebusy += num_traits<T>::from_int(std::min<long>(n, c)) * res.p[idx];
176 }
177 res.L = L;
178 res.U = T(Ebusy / cT);
179 res.Lq = T(L - Ebusy);
180 res.PB = me_gegecn_pb(res.p, K, N, c, Cs, Ca);
181 return res;
182}
183
184} // namespace me
185} // namespace line
186
187#endif // LINE_API_ME_ME_GEGECN_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Blocking probability seen by ONE arrival stream of a censored GE/GE/c/K;N queue.
GegecnResult< T > me_gegecn(const T &lambda, const T &Ca, const T &mu, const T &Cs, long c, long K, long N)
Port of me_gegecn.
Definition me_gegecn.h:75
T me_gegecn_pb(const std::vector< T > &p, long K, long N, long c, const T &Cs, const T &Ca)
Port of me_gegecn_pb.
Number-type abstraction for the templated API port.
What me_gegecn returns: the law and the four means read off it.
Definition me_gegecn.h:53
T Lq
mean number WAITING, L - E[min(n,c)]
Definition me_gegecn.h:58
T U
utilization, E[min(n,c)]/c
Definition me_gegecn.h:56
T L
mean number in the queue, sum_n n p(n)
Definition me_gegecn.h:55
T PB
probability an arrival of the aggregate stream is blocked
Definition me_gegecn.h:57
std::vector< T > p
p[idx] = Pr{n = K + idx}, idx = 0..N-K
Definition me_gegecn.h:54