LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_ldbcmp.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_LDBCMP_H
6#define LINE_API_PFQN_PFQN_LDBCMP_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Anselmi-Cremonesi (2008) lower throughput bound for a closed single-class
12 * BCMP network with load-dependent stations.
13 *
14 * Templated port of matlab/src/api/pfqn/pfqn_ldbcmp.m. The bound (their eq. 15)
15 * uses the fact that a closed BCMP network is, as N -> inf, equivalent to the
16 * open network obtained by removing the bottleneck and injecting at rate
17 * 1/Dmax; Algorithm 1 refines it to a monotone fixed point
18 *
19 * X = (N - Qhat) / [ Dmax (b + N - Qhat) - b (Dmax X')^N Dmax ]
20 *
21 * where Qhat is the sum of the non-bottleneck open queue lengths, b the number
22 * of bottleneck stations, and c(i) the Heffes load-dependence coefficient.
23 *
24 * APPLICABILITY. The bound requires N >= Qhat and every non-bottleneck
25 * utilization below one. MATLAB returns NaN in both cases; the port reports it
26 * through a flag on the result instead, since a NaN throughput propagates
27 * silently while a flag has to be read.
28 *
29 * ARITHMETIC. The fixed point raises Dmax X to the integer power N, which is
30 * num_pow_int and stays in the field, and everything else is an addition or a
31 * division. The bound is therefore EXACT in rational arithmetic and is left
32 * ungated -- but note that the iteration is a contraction, not a closed form,
33 * so what is exact is each iterate, not the limit.
34 */
35
36#include <cstddef>
37#include <limits>
38#include <vector>
39
40#include "line/num/number.h"
41#include "line/util/error.h"
42
43namespace line {
44namespace pfqn {
45
46/** Return value of pfqn_ldbcmp, mirroring [Xlo, Rhi, Qhat]. */
47template <class T>
49 T Xlo;
50 T Rhi;
52 bool applicable; ///< false where MATLAB returns NaN (N < Qhat, or rho >= 1)
53};
54
55/**
56 * @brief Anselmi-Cremonesi (2008) lower throughput bound for a closed
57 * single-class BCMP network with load-dependent stations.
58 *
59 * @param L (M) limiting demands, @param N population, @param Z think time
60 * @param c (M) Heffes coefficients, empty for all fixed-rate stations
61 * @param tol relative fixed-point tolerance (MATLAB default 1e-10)
62 */
63template <class T>
64LdBcmpBound<T> pfqn_ldbcmp(const std::vector<T>& L, const T& N, const T& Z, const std::vector<T>& c,
65 const T& tol) {
66 const std::size_t M = L.size();
67 if (M == 0) throw InputError("pfqn_ldbcmp: empty demand vector");
68 if (!c.empty() && c.size() != M) throw InputError("pfqn_ldbcmp: c has the wrong length");
69 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
70
71 T Dm = L[0];
72 for (const T& x : L)
73 if (x > Dm) Dm = x;
74 if (Dm <= zero) throw InputError("pfqn_ldbcmp: all demands are zero");
75 // MATLAB's bottleneck test is abs(D - Dm) <= 1e-12*Dm; the tolerance is a
76 // floating-point guard and is kept, so the two agree station for station.
77 const T bt = T(num_traits<T>::from_double(1e-12) * Dm);
78 long bmax = 0;
79 std::vector<bool> isbott(M, false);
80 for (std::size_t i = 0; i < M; ++i) {
81 if (num_abs(T(L[i] - Dm)) <= bt) {
82 isbott[i] = true;
83 ++bmax;
84 }
85 }
86
88 r.applicable = true;
89 const T lambda = T(one / Dm);
90 T Qhat = zero;
91 for (std::size_t i = 0; i < M; ++i) {
92 if (isbott[i]) continue;
93 const T rho = T(lambda * L[i]);
94 if (rho >= one) {
95 r.applicable = false;
96 r.Xlo = zero;
97 r.Rhi = zero;
98 r.Qhat = zero;
99 return r;
100 }
101 const T ci = c.empty() ? zero : c[i];
102 Qhat += T(T(ci + one) * rho / T(one - rho));
103 }
104 Qhat += lambda * Z;
105 r.Qhat = Qhat;
106 if (T(N - Qhat) < zero) {
107 r.applicable = false;
108 r.Xlo = zero;
109 r.Rhi = zero;
110 return r;
111 }
112
113 const T a = T(N - Qhat);
114 // Dmax X' is raised to the integer power N, so N must be integral here,
115 // exactly as in MATLAB, where N indexes a closed population.
116 const double Nd = num_traits<T>::to_double(N);
117 if (Nd < 0.0 || Nd != std::floor(Nd))
118 throw InputError("pfqn_ldbcmp: the population must be a non-negative integer");
119 const unsigned Nu = static_cast<unsigned>(Nd);
120
121 T Xprime = zero, Xlo = zero;
122 for (int it = 0; it < 10000; ++it) {
123 const T Xprev = Xlo;
124 const T denom = T(Dm * T(num_traits<T>::from_int(bmax) + N - Qhat) -
125 num_traits<T>::from_int(bmax) * num_pow_int(T(Dm * Xprime), Nu) * Dm);
126 if (denom == zero) throw NumericError("pfqn_ldbcmp: zero denominator in the fixed point");
127 Xlo = T(a / denom);
128 Xprime = Xlo;
129 if (Xprev > zero && T(num_abs(T(Xprev - Xlo)) / Xprev) <= tol) break;
130 }
131 r.Xlo = Xlo;
132 if (Xlo == zero) {
133 // Exactly at the regime boundary N == Qhat the numerator N - Qhat is
134 // zero, so the bound degenerates to X >= 0: still valid, and MATLAB
135 // returns it with Rhi = N/0 = Inf rather than erroring. An exact
136 // backend has no infinity to return there.
137 if constexpr (num_traits<T>::has_transcendental) {
138 r.Rhi = num_traits<T>::from_double(std::numeric_limits<double>::infinity());
139 return r;
140 } else {
141 throw UnsupportedError(
142 "pfqn_ldbcmp: at N == Qhat the throughput bound degenerates to zero and the "
143 "response-time bound is infinite, which exact arithmetic cannot represent");
144 }
145 }
146 r.Rhi = T(N / Xlo);
147 return r;
148}
149
150template <class T>
151LdBcmpBound<T> pfqn_ldbcmp(const std::vector<T>& L, const T& N, const T& Z) {
152 return pfqn_ldbcmp(L, N, Z, std::vector<T>(), num_traits<T>::from_double(1e-10));
153}
154
155} // namespace pfqn
156} // namespace line
157
158#endif // LINE_API_PFQN_PFQN_LDBCMP_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.
LdBcmpBound< T > pfqn_ldbcmp(const std::vector< T > &L, const T &N, const T &Z, const std::vector< T > &c, const T &tol)
Anselmi-Cremonesi (2008) lower throughput bound for a closed single-class BCMP network with load-depe...
Definition pfqn_ldbcmp.h:64
T num_abs(const T &v)
Definition number.h:172
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.
Return value of pfqn_ldbcmp, mirroring [Xlo, Rhi, Qhat].
Definition pfqn_ldbcmp.h:48
bool applicable
false where MATLAB returns NaN (N < Qhat, or rho >= 1)
Definition pfqn_ldbcmp.h:52