LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mmcc_retrial_fp.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_MMCC_RETRIAL_FP_H
6#define LINE_API_QSYS_QSYS_MMCC_RETRIAL_FP_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Fixed-point approximation for the M/M/c/c retrial queue.
12 *
13 * Port of matlab/src/api/qsys/qsys_mmcc_retrial_fp.m. Blocked customers join an
14 * orbit and retry; under the assumption that the retrial rate is small relative
15 * to the service rate the superposition of fresh and retrial arrivals is
16 * approximated by a Poisson stream of rate lambda + r, with r the solution of
17 *
18 * r = (lambda + r) B((lambda + r)/mu, c),
19 *
20 * B being Erlang's loss formula. Cohen (1957); Phung-Duc, "Retrial Queueing
21 * Models: A Survey on Theory and Applications" (2019), eq. (1).
22 *
23 * The reference evaluates B by the rational recursion
24 * B_k = a B_{k-1}/(k + a B_{k-1}), which is exactly what line::lossn::erlang_b
25 * declines to do (it goes through log/exp and says so). This port keeps the
26 * reference's recursion, so B itself is a rational function of a and needs no
27 * transcendental arithmetic. The static_assert is nonetheless present because
28 * the OUTER iteration is a fixed point tested against a tolerance: it converges
29 * geometrically but not in a finite number of field operations, and at Rational
30 * the iterates would grow without bound in representation size while never
31 * meeting an exact stopping rule.
32 *
33 * The iteration is a monotone increasing map of r started from 0 and bounded
34 * above by lambda/(1-B) at the fixed point, so it converges from below and the
35 * successive-difference test is a genuine stopping criterion rather than a
36 * heuristic. The reference does NOT report non-convergence; when maxiter is
37 * exhausted it silently returns the last iterate, and the port reports the
38 * iteration count so the caller can tell the two apart, exactly as the third
39 * MATLAB output niter does.
40 */
41
42#include <cstddef>
43
44#include "line/num/number.h"
45#include "line/util/error.h"
46
47namespace line {
48namespace qsys {
49
50/** Return value of qsys_mmcc_retrial_fp, mirroring the three MATLAB outputs. */
51template <class T>
53 T blockingProbability; ///< B((lambda + r)/mu, c)
54 T retrialRate; ///< r, the extra arrival rate contributed by the orbit
55 std::size_t iterations; ///< iterations performed, = maxiter when unconverged
56 bool converged; ///< whether the successive-difference test was met
57};
58
59/**
60 * Erlang's loss formula B(a, c) by the numerically stable rational recursion
61 * B_0 = 1, B_k = a B_{k-1}/(k + a B_{k-1}). Exact in any field: no logs, no
62 * factorials, no cancellation.
63 */
64template <class T>
65T erlang_b_recursive(const T& a, unsigned c) {
67 for (unsigned i = 1; i <= c; ++i) b = a * b / (num_traits<T>::from_int(static_cast<long>(i)) + a * b);
68 return b;
69}
70
71/**
72 * M/M/c/c with retrials by the Cohen fixed point.
73 *
74 * @param lambda fresh arrival rate
75 * @param mu service rate of one server
76 * @param c number of servers, which is also the capacity
77 * @param tol stopping tolerance on |r_{k+1} - r_k|
78 * @param maxiter iteration cap
79 */
80template <class T>
81MmccRetrialFpResult<T> qsys_mmcc_retrial_fp(const T& lambda, const T& mu, unsigned c, const T& tol,
82 std::size_t maxiter) {
84 "qsys_mmcc_retrial_fp is a tolerance-terminated fixed point");
85 const T zero = num_traits<T>::from_int(0);
86 if (lambda <= zero) throw InputError("qsys_mmcc_retrial_fp: arrival rate must be positive");
87 if (mu <= zero) throw InputError("qsys_mmcc_retrial_fp: service rate must be positive");
88 if (c == 0) throw InputError("qsys_mmcc_retrial_fp: at least one server is required");
89 if (tol <= zero) throw InputError("qsys_mmcc_retrial_fp: tolerance must be positive");
90 if (maxiter == 0) throw InputError("qsys_mmcc_retrial_fp: maxiter must be positive");
91
92 T r = zero;
93 std::size_t it = 0;
94 bool converged = false;
95 for (; it < maxiter;) {
96 ++it;
97 const T a = (lambda + r) / mu;
98 const T b = erlang_b_recursive(a, c);
99 const T rnew = (lambda + r) * b;
100 const T gap = num_abs(T(rnew - r));
101 r = rnew;
102 if (gap < tol) {
103 converged = true;
104 break;
105 }
106 }
107
109 out.retrialRate = r;
110 out.blockingProbability = erlang_b_recursive(T((lambda + r) / mu), c);
111 out.iterations = it;
112 out.converged = converged;
113 return out;
114}
115
116/** qsys_mmcc_retrial_fp with the reference defaults tol = 1e-10, maxiter = 10000. */
117template <class T>
118MmccRetrialFpResult<T> qsys_mmcc_retrial_fp(const T& lambda, const T& mu, unsigned c) {
119 return qsys_mmcc_retrial_fp(lambda, mu, c, T(num_traits<T>::from_double(1e-10)),
120 static_cast<std::size_t>(10000));
121}
122
123} // namespace qsys
124} // namespace line
125
126#endif // LINE_API_QSYS_QSYS_MMCC_RETRIAL_FP_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
T erlang_b_recursive(const T &a, unsigned c)
Erlang's loss formula B(a, c) by the numerically stable rational recursion B_0 = 1,...
MmccRetrialFpResult< T > qsys_mmcc_retrial_fp(const T &lambda, const T &mu, unsigned c, const T &tol, std::size_t maxiter)
M/M/c/c with retrials by the Cohen fixed point.
T num_abs(const T &v)
Definition number.h:172
Number-type abstraction for the templated API port.
Return value of qsys_mmcc_retrial_fp, mirroring the three MATLAB outputs.
bool converged
whether the successive-difference test was met
T blockingProbability
B((lambda + r)/mu, c).
T retrialRate
r, the extra arrival rate contributed by the orbit
std::size_t iterations
iterations performed, = maxiter when unconverged