5#ifndef LINE_API_LOSSN_LOSSN_ERLANGFP_H
6#define LINE_API_LOSSN_LOSSN_ERLANGFP_H
65 "erlang_b as ported evaluates through log/exp; use the rational recursion "
66 "B_k = nu B_{k-1} / (k + nu B_{k-1}) for an exact variant");
67 if (C < 0)
throw InputError(
"erlang_b: negative capacity");
70 const T lnu = log(nu);
72 for (
int i = 0; i <= C; ++i)
90 const T lnu = log(nu);
92 const long cfloor =
static_cast<long>(std::floor(cd));
93 for (
long i = 0; i <= cfloor; ++i)
118inline long long erlangfp_rat_den(
double x,
double tol = 1e-12) {
119 if (!std::isfinite(x))
throw InputError(
"lossn_erlangfp: non-finite circuit requirement or capacity");
121 long long n0 = 1, d0 = 0, n1 =
static_cast<long long>(std::floor(y)), d1 = 1;
122 double frac = y - std::floor(y);
123 for (
int k = 0; k < 64 && std::fabs(x -
static_cast<double>(n1) /
static_cast<double>(d1)) > tol; ++k) {
124 if (frac == 0.0)
break;
126 const long long a =
static_cast<long long>(std::floor(y));
127 frac = y - std::floor(y);
128 const long long n2 = a * n1 + n0, d2 = a * d1 + d0;
129 n0 = n1; d0 = d1; n1 = n2; d1 = d2;
131 return d1 < 0 ? -d1 : d1;
134inline long long erlangfp_gcd(
long long a,
long long b) {
135 while (b != 0) {
const long long t = a % b; a = b; b = t; }
136 return a < 0 ? -a : a;
145std::vector<T> kaufman_roberts_blocking(
const std::vector<T>& rho,
const std::vector<long long>& a,
long long c) {
146 const std::size_t R = rho.size();
147 const T zero = num_traits<T>::from_int(0);
148 std::vector<T> q(
static_cast<std::size_t
>(c + 1), zero);
149 q[0] = num_traits<T>::from_int(1);
150 for (
long long n = 1; n <= c; ++n) {
152 for (std::size_t r = 0; r < R; ++r)
153 if (a[r] > 0 && n - a[r] >= 0)
154 s += num_traits<T>::from_int(
static_cast<int>(a[r])) * rho[r] *
155 q[
static_cast<std::size_t
>(n - a[r])];
156 q[
static_cast<std::size_t
>(n)] = T(s / num_traits<T>::from_int(
static_cast<int>(n)));
158 if (num_traits<T>::to_double(q[
static_cast<std::size_t
>(n)]) > 1e250) {
159 const T scale = q[
static_cast<std::size_t
>(n)];
160 for (
long long m = 0; m <= n; ++m) q[static_cast<std::size_t>(m)] = T(q[
static_cast<std::size_t
>(m)] / scale);
164 for (
const T& v : q)
tot += v;
165 std::vector<T> b(R, zero);
166 for (std::size_t r = 0; r < R; ++r) {
167 if (a[r] <= 0)
continue;
169 for (
long long n = std::max<long long>(0, c - a[r] + 1); n <= c; ++n) acc += q[static_cast<std::size_t>(n)];
192 const std::vector<T>& C,
195 "lossn_erlangfp requires transcendental arithmetic (a tolerance-driven fixed "
196 "point around the Kaufman-Roberts recursion)");
197 const std::size_t R = nu.size();
198 const std::size_t J = C.size();
200 throw InputError(
"lossn_erlangfp: the route matrix does not match nu and C");
205 std::vector<std::vector<long long>> Aint(J, std::vector<long long>(R, 0));
206 std::vector<long long> Cint(J, 0);
207 for (std::size_t j = 0; j < J; ++j) {
209 for (std::size_t r = 0; r <= R; ++r) {
211 const long long d = detail::erlangfp_rat_den(x);
212 if (d > 0) m = m / detail::erlangfp_gcd(m, d) * d;
214 for (std::size_t r = 0; r < R; ++r)
217 if (Cint[j] < 0)
throw InputError(
"lossn_erlangfp: negative link capacity");
221 const std::function<std::pair<std::vector<T>, std::vector<T>>(
const std::vector<T>&, std::size_t)>
222 sweep = [&](
const std::vector<T>& B, std::size_t) {
223 std::vector<T> Bnew = B;
224 for (std::size_t j = 0; j < J; ++j) {
225 std::vector<T> rho = nu;
226 for (std::size_t r = 0; r < R; ++r)
227 for (std::size_t i = 0; i < J; ++i)
228 if (i != j && Aint[i][r] > 0) rho[r] = T(rho[r] * T(one - B[i * R + r]));
229 const std::vector<T> bj = detail::kaufman_roberts_blocking<T>(rho, Aint[j], Cint[j]);
230 for (std::size_t r = 0; r < R; ++r) Bnew[j * R + r] = bj[r];
232 return std::make_pair(Bnew, B);
235 const std::vector<T> B0(J * R, zero);
240 for (std::size_t j = 0; j < J; ++j)
241 for (std::size_t c = 0; c < R; ++c) r.
E(j, c) = fp.
x[j * R + c];
244 r.
QLen.assign(R, zero);
245 r.
Loss.assign(R, zero);
246 for (std::size_t c = 0; c < R; ++c) {
248 for (std::size_t j = 0; j < J; ++j) pass = T(pass * T(one - r.
E(j, c)));
249 r.
QLen[c] = T(nu[c] * pass);
250 r.
Loss[c] = T(one - pass);
258 const std::vector<int>& C,
260 std::vector<T> Ct(C.size());
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.
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...
ErlangFpResult< T > lossn_erlangfp(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const da::FpiOptions &options=lossn_erlangfp_options())
Erlang fixed-point (reduced-load) approximation for a loss network.
T erlang_b_real(const T &nu, const T &C)
Erlang B for a REAL capacity, the reference's ErlangB(nu, C) evaluated as it stands: the sum runs ove...
da::FpiOptions lossn_erlangfp_options()
The reference's fixed-point settings: iter_max 10000, iter_tol 1e-10, the max-norm of the increment (...
double tot(const std::vector< double > &pops)
Conservation laws of a layered queueing network, enumerated from its structure.
T num_factorial(unsigned n)
Factorial as a value of T.
Number-type abstraction for the templated API port.
Options mirroring the fields MATLAB reads off the options struct.
bool nanstop
stop when the increment norm is NaN (the reference's isnan test)
Matrix< T > E
(J x R) blocking of class r on link j (the fixed point)
std::vector< T > QLen
carried traffic per class
std::vector< T > Loss
blocking probability per class