LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fj_rmax_erlang.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_RMAX_ERLANG_H
6#define LINE_API_FJ_RMAX_ERLANG_H
7
8/**
9 * @file
10 * @ingroup api_fj
11 * Expected maximum of K M/E_k/1 branch response times.
12 *
13 * Templated port of matlab/src/api/fj/fj_rmax_erlang.m.
14 *
15 * The branch mean response time comes from Pollaczek-Khinchine with the
16 * Erlang-k SCV 1/k,
17 *
18 * R = (k/mu) [1 + rho (1 + 1/k) / (2 (1 - rho))], rho = lambda k / mu
19 *
20 * At K = 2 the maximum has the closed form (Thomasian 2014, Eq. 34)
21 *
22 * R_2^max = 2 R - sum_{m,n < k} C(m+n,m) mu_R^{m+n} / (2 mu_R)^{m+n+1}
23 *
24 * with mu_R = k / R the rate that matches the response-time mean. For any
25 * other K the MATLAB file moment-matches the response time to an Erlang and
26 * integrates 1 - F(t)^K numerically.
27 *
28 * MIXED ARITHMETIC. The K = 2 branch is rational and exact in any field; the
29 * general-K branch needs exp and a quadrature and throws UnsupportedError at
30 * exact arithmetic.
31 *
32 * REFERENCE DEFECT: FJ_rmax.fj_rmax_erlang in
33 * jar/src/main/java/jline/api/fj/FJ_rmax.java drops the mu_R^{m+n} numerator
34 * from the K = 2 correction, computing C(m+n,m)/(2 mu_R)^{m+n+1} instead. The
35 * two agree only at mu_R = 1. MATLAB is ground truth and is what this port
36 * follows. Note also that the MATLAB correction simplifies to
37 * sum C(m+n,m) / (2^{m+n+1} mu_R), which is how it is evaluated here.
38 */
39
42#include "line/num/number.h"
43#include "line/util/error.h"
44
45namespace line {
46namespace fj {
47
48/**
49 * @brief Expected maximum of K M/E_k/1 branch response times.
50 *
51 * @param K number of branches, K >= 1
52 * @param k Erlang stages of the branch service time, k >= 1
53 * @param lambda arrival rate
54 * @param mu per-stage service rate (branch mean service is k/mu)
55 * @return expected maximum of the K branch response times
56 */
57template <class T>
58T fj_rmax_erlang(unsigned K, unsigned k, const T& lambda, const T& mu) {
59 detail::require_positive_K(K, "fj_rmax_erlang");
60 if (k < 1) throw InputError("fj_rmax_erlang: the stage count k must be a positive integer");
61 const T one = num_traits<T>::from_int(1), two = num_traits<T>::from_int(2);
62 const T kt = num_traits<T>::from_int(static_cast<long>(k));
63
64 const T mean_service = kt / mu;
65 const T rho = lambda * mean_service;
66 if (rho >= one) throw NumericError("fj_rmax_erlang: unstable system, rho >= 1");
67
68 const T cv2 = one / kt;
69 const T R_single = mean_service * (one + rho * (one + cv2) / (two * (one - rho)));
70
71 if (K == 2) {
72 const T mu_resp = kt / R_single;
73 T correction = num_traits<T>::from_int(0);
74 for (unsigned m = 0; m < k; ++m)
75 for (unsigned n = 0; n < k; ++n)
76 correction += detail::fj_binom<T>(m + n, m) * num_pow_int(mu_resp, m + n) /
77 num_pow_int(T(two * mu_resp), m + n + 1);
78 return two * R_single - correction;
79 }
80
82 // MATLAB's cv2_response: (1/k + rho)/(1 + rho), floored at 1/20.
83 const T cv2r_raw = (cv2 + rho) / (one + rho);
84 const T floor20 = num_traits<T>::from_rational(1, 20);
85 const T cv2r = cv2r_raw > floor20 ? cv2r_raw : floor20;
86 const double inv = num_traits<T>::to_double(T(one / cv2r));
87 unsigned k_resp = static_cast<unsigned>(std::ceil(inv));
88 if (k_resp < 1) k_resp = 1;
89 const T mu_resp = num_traits<T>::from_int(static_cast<long>(k_resp)) / R_single;
90 const T upper = R_single * num_traits<T>::from_int(20);
91 return detail::simpson<T>(
92 [&](const T& t) { return T(one - num_pow_int(detail::erlang_cdf(t, k_resp, mu_resp), K)); },
93 num_traits<T>::from_int(0), upper);
94 } else {
95 throw UnsupportedError(
96 "fj_rmax_erlang: only K = 2 has a closed form; any other branch count needs a "
97 "quadrature of the fitted Erlang response-time CDF and therefore transcendental "
98 "arithmetic");
99 }
100}
101
102} // namespace fj
103} // namespace line
104
105#endif // LINE_API_FJ_RMAX_ERLANG_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
UnsupportedError(const std::string &what)
Definition error.h:51
The exception types the port throws.
Shared return types and arithmetic helpers for the templated fork-join port.
Expected maximum of K i.i.d.
T fj_rmax_erlang(unsigned K, unsigned k, const T &lambda, const T &mu)
Expected maximum of K M/E_k/1 branch response times.
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition number.h:192
Number-type abstraction for the templated API port.