LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
lossn_erlangfp.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_LOSSN_LOSSN_ERLANGFP_H
6#define LINE_API_LOSSN_LOSSN_ERLANGFP_H
7
8/**
9 * @file
10 * @ingroup api_lossn
11 * Erlang fixed-point (reduced-load) approximation for a loss network.
12 *
13 * Templated port of matlab/src/api/lossn/lossn_erlangfp.m. Each link j carries
14 * an offered load
15 * rho_j = (1/(1-E_j)) sum_r nu_r A(j,r) prod_i (1-E_i)^A(i,r)
16 * and blocks it with Erlang's loss formula B(rho_j, C_j); the vector E is the
17 * fixed point of that map, reached by the shared damped iteration in
18 * line::da::da_fpi. Carried traffic and per-class loss follow from E.
19 *
20 * MATLAB evaluates Erlang B through logs and exponentials to keep the
21 * factorials in range. The port keeps that form, and consequently the whole
22 * function is transcendental-gated: the fixed point is only defined to within
23 * the iteration tolerance anyway, so an exact instantiation would promise more
24 * than the algorithm delivers.
25 *
26 * The recursive form B_k = rho B_{k-1} / (k + rho B_{k-1}) IS rational, and a
27 * future exact variant of the blocking formula alone could use it; the fixed
28 * point around it would still be inexact.
29 */
30
31#include <cmath>
32#include <cstddef>
33#include <functional>
34#include <vector>
35
36#include "line/api/da/da_fpi.h"
37#include "line/num/number.h"
38#include "line/util/error.h"
39#include "line/util/matrix.h"
40
41namespace line {
42namespace lossn {
43
44template <class T>
46 std::vector<T> QLen; ///< carried traffic per class
47 std::vector<T> Loss; ///< blocking probability per class
48 std::vector<T> E; ///< per-link blocking probabilities (the fixed point)
49 std::size_t iterations = 0;
50 bool converged = false;
51};
52
53/**
54 * Erlang's loss formula B(nu, C), evaluated through logs as MATLAB does so the
55 * factorials stay in range for large C.
56 */
57template <class T>
58T erlang_b(const T& nu, int C) {
60 "erlang_b as ported evaluates through log/exp; use the rational recursion "
61 "B_k = nu B_{k-1} / (k + nu B_{k-1}) for an exact variant");
62 if (C < 0) throw InputError("erlang_b: negative capacity");
63 using std::exp;
64 using std::log;
65 const T lnu = log(nu);
66 T den = num_traits<T>::from_int(0);
67 for (int i = 0; i <= C; ++i)
68 den += exp(num_traits<T>::from_int(i) * lnu - log(num_factorial<T>(static_cast<unsigned>(i))));
69 const T lb = num_traits<T>::from_int(C) * lnu -
70 log(num_factorial<T>(static_cast<unsigned>(C))) - log(den);
71 return exp(lb);
72}
73
74/**
75 * @brief Erlang fixed-point (reduced-load) approximation for a loss network.
76 *
77 * @param nu offered load per class (R)
78 * @param A (J x R) route matrix: A(j,r) is the number of circuits class r
79 * takes on link j
80 * @param C (J) link capacities
81 * @param options fixed-point options (tolerance, iteration cap, damping)
82 */
83template <class T>
84ErlangFpResult<T> lossn_erlangfp(const std::vector<T>& nu, const Matrix<T>& A,
85 const std::vector<int>& C,
86 const da::FpiOptions& options = da::FpiOptions()) {
88 "lossn_erlangfp requires transcendental arithmetic (Erlang B through logs, and "
89 "a tolerance-driven fixed point)");
90 const std::size_t R = nu.size();
91 const std::size_t J = C.size();
92 if (A.rows() != J || A.cols() != R)
93 throw InputError("lossn_erlangfp: the route matrix does not match nu and C");
94
95 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
96
97 // One sweep of the reduced-load map, in the (xnew, xref) form da_fpi wants.
98 const std::function<std::pair<std::vector<T>, std::vector<T>>(const std::vector<T>&, std::size_t)>
99 sweep = [&](const std::vector<T>& Eprev, std::size_t) {
100 std::vector<T> Enew = Eprev;
101 for (std::size_t j = 0; j < J; ++j) {
102 T rhoj = zero;
103 for (std::size_t r = 0; r < R; ++r) {
104 if (A(j, r) <= zero) continue;
105 T term = nu[r] * A(j, r);
106 for (std::size_t i = 0; i < J; ++i) {
107 if (A(i, r) <= zero) continue;
108 const long e = static_cast<long>(num_traits<T>::to_double(A(i, r)));
109 term *= num_pow_int(T(one - Eprev[i]), static_cast<unsigned>(e));
110 }
111 rhoj += term;
112 }
113 const T avail = one - Eprev[j];
114 if (avail == zero) throw NumericError("lossn_erlangfp: link blocked with probability 1");
115 rhoj /= avail;
116 Enew[j] = erlang_b(rhoj, C[j]);
117 }
118 return std::make_pair(Enew, Eprev);
119 };
120
121 std::vector<T> E0(J, num_traits<T>::from_rational(1, 2));
122 da::FpiResult<T> fp = da::da_fpi<T>(sweep, E0, options);
123
125 r.E = fp.x;
126 r.iterations = fp.iterations;
127 r.converged = fp.converged;
128 r.QLen.assign(R, zero);
129 r.Loss.assign(R, zero);
130 for (std::size_t cls = 0; cls < R; ++cls) {
131 T q = nu[cls];
132 for (std::size_t j = 0; j < J; ++j) {
133 if (A(j, cls) <= zero) continue;
134 const long e = static_cast<long>(num_traits<T>::to_double(A(j, cls)));
135 q *= num_pow_int(T(one - r.E[j]), static_cast<unsigned>(e));
136 }
137 r.QLen[cls] = q;
138 r.Loss[cls] = (nu[cls] == zero) ? zero : one - q / nu[cls];
139 }
140 return r;
141}
142
143} // namespace lossn
144} // namespace line
145
146#endif // LINE_API_LOSSN_LOSSN_ERLANGFP_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
NumericError(const std::string &what)
Definition error.h:45
Damped fixed-point iteration, the shared driver of the decomposition algorithms.
The exception types the port throws.
Dense matrix and non-owning view.
FpiResult< T > da_fpi(const std::function< std::pair< std::vector< T >, std::vector< T > >(const std::vector< T > &, std::size_t)> &iterfun, const std::vector< T > &x0, const FpiOptions &options=FpiOptions())
Damped fixed-point iteration, the shared driver of the decomposition algorithms.
Definition da_fpi.h:92
ErlangFpResult< T > lossn_erlangfp(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< int > &C, const da::FpiOptions &options=da::FpiOptions())
Erlang fixed-point (reduced-load) approximation for a loss network.
T erlang_b(const T &nu, int C)
Erlang's loss formula B(nu, C), evaluated through logs as MATLAB does so the factorials stay in range...
T num_factorial(unsigned n)
Factorial as a value of T.
Definition number.h:184
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.
Options mirroring the fields MATLAB reads off the options struct.
Definition da_fpi.h:50
std::size_t iterations
Definition da_fpi.h:78
std::vector< T > x
Definition da_fpi.h:77
std::vector< T > E
per-link blocking probabilities (the fixed point)
std::vector< T > QLen
carried traffic per class
std::vector< T > Loss
blocking probability per class