LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fj_ism_green.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_FJ_ISM_GREEN_H
6#define LINE_API_FJ_ISM_GREEN_H
7
8/**
9 * @file
10 * @ingroup api_fj
11 * Green's independent server model of simultaneous server requests.
12 *
13 * Templated port of matlab/src/api/fj/fj_ism_green.m.
14 *
15 * A customer needs j servers at once with probability c(j) and then releases
16 * them asynchronously as each of its j tasks completes at rate mu. Servers can
17 * idle while customers wait, which is what separates the model from M/G/s, and
18 * customer service ends with the last of its tasks, so its mean is H_j/mu.
19 *
20 * E[B] = sum_j c(j) sum_{i=0..j-1} 1/((s-i) mu)
21 *
22 * is the interservice time, the j-th order statistic of s exponentials because
23 * all s servers are busy whenever a customer enters service in a queueing
24 * period, and
25 *
26 * E[D] = sum_i sum_{k=1..i} [ sum_{m=0..k-1} 1/((i-m) mu) ] q(i) c(s-i+k)/p_d
27 *
28 * the initial delay of the customer that starts one. The busy-server
29 * distribution q and the nonqueue length come from the embedded chain absorbed
30 * when a queue forms, V = (I-T)^-1. The waiting-time transform of Eq. (61)
31 * factors into the equilibrium transform of D and the Pollaczek-Khinchine
32 * transform of an M/G/1 queue with service B, so
33 *
34 * E[W] = (1-pi0) [ E[D^2]/(2 E[D]) + lambda E[B^2]/(2 (1-lambda E[B])) ].
35 *
36 * Eq. (65) of the survey prints the inner sum as starting at 1/(s mu) even
37 * though only i servers are busy; it is started at 1/(i mu) here, which is what
38 * the accompanying text prescribes and what makes E[D] reduce to E[B] at i = s.
39 */
40
41#include <cstddef>
42#include <vector>
43
46#include "line/num/number.h"
47#include "line/util/error.h"
48#include "line/util/matrix.h"
49
50namespace line {
51namespace fj {
52
53/** Everything Green's cycle decomposition produces. */
54template <class T>
56 T W;
57 T R;
58 T EB;
59 T EB2;
60 T ED;
61 T ED2;
62 T EQ;
64 T pq;
65 T pd;
66 T pi0;
67 T rho;
68 T ES;
69 std::vector<T> q;
70};
71
72/**
73 * @brief Green's independent server model of simultaneous server requests.
74 *
75 * @param lambda customer arrival rate
76 * @param mu per-task service rate
77 * @param s number of servers
78 * @param c c[j-1] = P(a customer needs j servers), summing to one
79 * @return the waiting and response times with the cycle quantities
80 */
81template <class T>
82FJIsmGreenResult<T> fj_ism_green(const T& lambda, const T& mu, unsigned s,
83 const std::vector<T>& c) {
84 if (s < 1) throw InputError("fj_ism_green: s must be a positive integer");
85 if (c.size() != s)
86 throw InputError("fj_ism_green: c must have one entry per server requirement");
87 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1),
89 if (!(lambda > zero) || !(mu > zero))
90 throw InputError("fj_ism_green: lambda and mu must be positive");
91 T tot = zero;
92 for (std::size_t i = 0; i < s; ++i) {
93 if (c[i] < zero)
94 throw InputError("fj_ism_green: the server-requirement probabilities must be non-negative");
95 tot += c[i];
96 }
97 if (!(tot - one < num_traits<T>::from_double(1e-9)) ||
98 !(one - tot < num_traits<T>::from_double(1e-9)))
99 throw InputError("fj_ism_green: the server-requirement probabilities must sum to one");
100
102
103 // Interservice time B: the j-th order statistic of s exponentials of rate mu
104 out.EB = zero;
105 out.EB2 = zero;
106 for (unsigned j = 1; j <= s; ++j) {
107 T mj = zero, vj = zero;
108 for (unsigned i = 0; i < j; ++i) {
109 const T st = one / (num_traits<T>::from_int(static_cast<long>(s - i)) * mu);
110 mj += st;
111 vj += st * st;
112 }
113 out.EB += c[j - 1] * mj;
114 out.EB2 += c[j - 1] * (vj + mj * mj);
115 }
116
117 out.rho = lambda * out.EB;
118 if (out.rho >= one)
119 throw NumericError("fj_ism_green: unstable system, rho = lambda*E[B] >= 1");
120
121 // Embedded chain over the busy-server count, absorbed when an arrival needs
122 // more servers than are free
123 const std::size_t n = s + 1;
124 Matrix<T> A(n, n, zero);
125 for (std::size_t i = 0; i < n; ++i) {
126 const T den = lambda + num_traits<T>::from_int(static_cast<long>(i)) * mu;
127 if (i > 0) A(i, i - 1) = num_traits<T>::from_int(static_cast<long>(i)) * mu / den;
128 for (std::size_t j = 1; j + i <= s; ++j)
129 A(i, i + j) = A(i, i + j) + lambda * c[j - 1] / den;
130 }
131 // V = (I - T)^-1; only the row that starts with all s servers busy is used
132 Matrix<T> M(n, n, zero);
133 for (std::size_t i = 0; i < n; ++i)
134 for (std::size_t j = 0; j < n; ++j) M(i, j) = (i == j ? one : zero) - A(i, j);
135 // Gaussian elimination on M^T x = e_s, which is the row s of (I-T)^-1
136 std::vector<std::vector<T> > G(n, std::vector<T>(n + 1, zero));
137 for (std::size_t i = 0; i < n; ++i) {
138 for (std::size_t j = 0; j < n; ++j) G[i][j] = M(j, i);
139 G[i][n] = (i == s) ? one : zero;
140 }
141 for (std::size_t col = 0; col < n; ++col) {
142 std::size_t piv = col;
143 for (std::size_t r = col + 1; r < n; ++r) {
144 const T a = G[r][col] > zero ? G[r][col] : -G[r][col];
145 const T b = G[piv][col] > zero ? G[piv][col] : -G[piv][col];
146 if (a > b) piv = r;
147 }
148 if (G[piv][col] == zero)
149 throw NumericError("fj_ism_green: the embedded chain is singular");
150 G[col].swap(G[piv]);
151 for (std::size_t r = 0; r < n; ++r) {
152 if (r == col) continue;
153 const T f = G[r][col] / G[col][col];
154 for (std::size_t j = col; j <= n; ++j) G[r][j] -= f * G[col][j];
155 }
156 }
157 std::vector<T> v(n);
158 for (std::size_t i = 0; i < n; ++i) v[i] = G[i][n] / G[i][i];
159
160 out.EQbar = zero;
161 std::vector<T> hold(n);
162 for (std::size_t i = 0; i < n; ++i) {
163 hold[i] = one / (lambda + num_traits<T>::from_int(static_cast<long>(i)) * mu);
164 out.EQbar += v[i] * hold[i];
165 }
166 out.q.resize(n);
167 for (std::size_t i = 0; i < n; ++i) out.q[i] = v[i] * hold[i] / out.EQbar;
168
169 // A customer arriving during a nonqueue period is delayed when it needs more
170 // servers than the s-i free ones
171 out.pd = zero;
172 for (std::size_t i = 0; i < n; ++i) {
173 const std::size_t free = s - i;
174 for (std::size_t j = free + 1; j <= s; ++j) out.pd += out.q[i] * c[j - 1];
175 }
176 if (!(out.pd > zero))
177 throw NumericError("fj_ism_green: no arrival can ever be delayed; the model is M/M/s");
178
179 // Initial delay D: i busy, the customer needs k = j-(s-i) more to free
180 out.ED = zero;
181 out.ED2 = zero;
182 for (unsigned i = 1; i <= s; ++i) {
183 for (unsigned k = 1; k <= i; ++k) {
184 const unsigned j = s - i + k;
185 if (j < 1 || j > s) continue;
186 const T wgt = out.q[i] * c[j - 1] / out.pd;
187 if (wgt == zero) continue;
188 T mk = zero, vk = zero;
189 for (unsigned m = 0; m < k; ++m) {
190 const T st = one / (num_traits<T>::from_int(static_cast<long>(i - m)) * mu);
191 mk += st;
192 vk += st * st;
193 }
194 out.ED += wgt * mk;
195 out.ED2 += wgt * (vk + mk * mk);
196 }
197 }
198
199 out.EQ = out.ED / (one - out.rho);
200 out.pq = out.EQ / (out.EQ + out.EQbar);
201 out.pi0 = (one - out.rho) / (one - lambda * (out.EB - out.ED));
202
203 const T Weq = out.ED2 / (two * out.ED);
204 const T Wmg1 = lambda * out.EB2 / (two * (one - out.rho));
205 out.W = (one - out.pi0) * (Weq + Wmg1);
206
207 // Customer service time: the maximum of the j tasks it holds
208 out.ES = zero;
209 for (unsigned j = 1; j <= s; ++j) out.ES += c[j - 1] * fj_harmonic<T>(j) / mu;
210 out.R = out.W + out.ES;
211 return out;
212}
213
214} // namespace fj
215} // namespace line
216
217#endif // LINE_API_FJ_ISM_GREEN_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Harmonic number H_K = sum_{k=1..K} 1/k.
Shared return types and arithmetic helpers for the templated fork-join port.
Dense matrix and non-owning view.
FJIsmGreenResult< T > fj_ism_green(const T &lambda, const T &mu, unsigned s, const std::vector< T > &c)
Green's independent server model of simultaneous server requests.
T fj_harmonic(unsigned K)
Harmonic number H_K = sum_{k=1..K} 1/k.
Definition fj_harmonic.h:37
Number-type abstraction for the templated API port.
Everything Green's cycle decomposition produces.