LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_ble.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_PFQN_PFQN_BLE_H
6#define LINE_API_PFQN_PFQN_BLE_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Logistic expansion with the eps->0 bias correction (BLE).
12 *
13 * Templated port of matlab/src/api/pfqn/pfqn_ble.m. Cas17 Theorem 4.1 holds for
14 * eps >= eps_N > 0; the K(1+eps*N) self-looping populations are what make the
15 * integrand concentrate. Evaluated at eps->0, as pfqn_le does, the curvature at
16 * the saddle tends to 1 rather than growing with N, so Laplace's method has no
17 * asymptotic regime there and carries an O(1) relative bias per Laplaced
18 * direction. With kappa = r(1) = 1 - log(2 pi)/2 and r(a) the Stirling remainder
19 * r(a) = gammaln(a) - ((a - 1/2) log a - a + log(2 pi)/2),
20 * the two branch constants are (as in MATLAB pfqn_ble.m):
21 * Z = 0 : M*kappa - r(M), the M-1 simplex directions plus the Jacobian, the
22 * radial integral being exact as Gamma(N+M);
23 * Z > 0 : M*kappa, the radial direction is Laplaced in t = log(v) and
24 * contributes exactly +r(M), cancelling the -r(M).
25 * The published expansion is NOT in error; see _kb/03-api-layer.md.
26 *
27 * ARITHMETIC. Inherited from pfqn_le: the correction is a logarithm, so the
28 * routine is gated on num_traits<T>::has_transcendental for the same reason.
29 *
30 * DEGENERATE BRANCH. When there is no queueing station to expand around, pfqn_le
31 * returns the exact delay term and there is no Laplace step to correct, so
32 * pfqn_ble returns it unchanged; this is the MATLAB branching.
33 */
34
35#include <cmath>
36#include <cstddef>
37#include <vector>
38
42#include "line/num/number.h"
43#include "line/util/matrix.h"
44
45namespace line {
46namespace pfqn {
47
48/** Return value of pfqn_ble, mirroring [Gn, lGn]. */
49template <class T>
51
52/**
53 * Logistic expansion estimate of the normalizing constant, bias-corrected.
54 *
55 * @param L (M x R) demands, @param N (R) population, @param Z (R) think times
56 * (pass an empty vector or all zeros for the Z = 0 branch)
57 */
58template <class T>
59BleResult<T> pfqn_ble(const Matrix<T>& L, const std::vector<T>& N, const std::vector<T>& Z) {
61 "pfqn_ble requires transcendental arithmetic (Laplace approximation of an integral)");
62 using std::exp;
63 using std::log;
64 const std::size_t M = L.rows(), R = L.cols();
65 const T zero = num_traits<T>::from_int(0);
66
67 BleResult<T> res = pfqn_le(L, N, Z);
68
69 T Ntot = zero, Lsum = zero, Zsum = zero;
70 for (const T& x : N) Ntot += x;
71 for (const T& x : Z) Zsum += x;
72 for (std::size_t i = 0; i < M; ++i)
73 for (std::size_t r = 0; r < R; ++r) Lsum += L(i, r);
74 if (M == 0 || N.empty() || Ntot == zero || num_traits<T>::to_double(Lsum) < 1e-4) {
75 return res;
76 }
77
78 // Same predicate pfqn_le branches on, so the constant always matches the branch.
79 const bool no_delay =
81 const T one = num_traits<T>::from_int(1);
82 const T twopi = num_traits<T>::from_double(6.283185307179586476925286766559);
83 const T kappa = T(one - log(twopi) / num_traits<T>::from_int(2));
84 const T Mt = num_traits<T>::from_int(static_cast<long>(M));
85 // MATLAB: Z=0 adds M*kappa - stirlingrem(M); Z>0 adds M*kappa.
86 res.lG += Mt * kappa;
87 if (no_delay) {
88 // stirlingrem(a) = gammaln(a) - ((a-0.5)*log(a) - a + 0.5*log(2*pi))
89 const T half = num_traits<T>::from_rational(1, 2);
90 const T rem = T(detail::num_lgamma<T>(Mt) - T(T(Mt - half) * log(Mt) - Mt + half * log(twopi)));
91 res.lG -= rem;
92 }
93 res.G = exp(res.lG);
94 return res;
95}
96
97template <class T>
98BleResult<T> pfqn_ble(const Matrix<T>& L, const std::vector<T>& N) {
99 return pfqn_ble(L, N, std::vector<T>());
100}
101
102} // namespace pfqn
103} // namespace line
104
105#endif // LINE_API_PFQN_PFQN_BLE_H
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Dense matrix and non-owning view.
BleResult< T > pfqn_ble(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Logistic expansion estimate of the normalizing constant, bias-corrected.
Definition pfqn_ble.h:59
LeResult< T > BleResult
Return value of pfqn_ble, mirroring [Gn, lGn].
Definition pfqn_ble.h:50
LeResult< T > pfqn_le(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Logistic expansion estimate of the normalizing constant.
Definition pfqn_le.h:245
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Number-type abstraction for the templated API port.
Shared scalar machinery for the integration / asymptotic members of the pfqn family (pfqn_le,...
Logistic expansion (LE) asymptotic approximation of the normalizing constant of a closed product-form...
static constexpr double Zero
Definition lang_types.h:767
Return value of pfqn_le, mirroring [Gn, lGn].
Definition pfqn_le.h:233