5#ifndef LINE_API_PFQN_HAREL_BOUNDS_H
6#define LINE_API_PFQN_HAREL_BOUNDS_H
82std::vector<T> harel_power_sums(
const std::vector<T>& rho,
int maxPower) {
84 for (
int i = 1; i <= maxPower; ++i) {
86 for (std::size_t j = 0; j < rho.size(); ++j) s +=
num_pow_int(rho[j],
static_cast<unsigned>(i));
87 A[
static_cast<std::size_t
>(i)] = s;
94std::vector<T> harel_G(
const std::vector<T>& A,
int n) {
95 if (
static_cast<int>(A.size()) <= n)
96 throw InputError(
"pfqn_harel_bounds: too few power sums for the requested population");
99 for (
int m = 1; m <= n; ++m) {
101 for (
int i = 1; i <= m; ++i)
102 acc += T(A[
static_cast<std::size_t
>(i)] * G[
static_cast<std::size_t
>(m - i)]);
110void harel_reject_thinktime(
const T& Z,
const std::string& who) {
111 if (Z != num_traits<T>::from_int(0))
113 " is only valid for networks with zero think time; the provided think "
119void harel_check_rho(
const std::vector<T>& rho) {
121 throw InputError(
"pfqn_harel_bounds: the loading vector must have at least one element");
122 for (std::size_t i = 0; i < rho.size(); ++i)
123 if (rho[i] <= num_traits<T>::from_int(0))
124 throw InputError(
"pfqn_harel_bounds: all loading factors must be positive");
129T harel_lower_bound(
const std::vector<T>& A,
int N) {
131 if (N == 1)
return T(num_traits<T>::from_int(1) / A1);
132 const T ratio = T(A[
static_cast<std::size_t
>(N)] / A1);
136 return T(num_traits<T>::from_int(2) / T(A1 + ratio));
137 if constexpr (!num_traits<T>::has_transcendental) {
139 "pfqn_harel_lb needs an (N-1)-st root and is unavailable in exact arithmetic for "
143 const T root = pow(ratio, T(num_traits<T>::from_int(1) / num_traits<T>::from_int(N - 1)));
144 return T(num_traits<T>::from_int(N) / T(A1 + num_traits<T>::from_int(N - 1) * root));
150T harel_upper_from_th(
const T& A1,
int N,
int n,
const T& THn) {
151 if (THn == num_traits<T>::from_int(0))
152 throw NumericError(
"pfqn_harel_bounds: the throughput at the extrapolation point is zero");
153 const T nOverTH = T(num_traits<T>::from_int(n) / THn);
154 const T den = T(A1 + T(num_traits<T>::from_int(N - 1) / num_traits<T>::from_int(n - 1)) *
156 if (den == num_traits<T>::from_int(0))
157 throw NumericError(
"pfqn_harel_bounds: the upper-bound denominator vanishes");
158 return T(num_traits<T>::from_int(N) / den);
172 detail::harel_reject_thinktime(Z,
"pfqn_harel_lb");
173 if (N < 1)
throw InputError(
"pfqn_harel_lb: the population must be at least 1");
174 detail::harel_check_rho(rho);
175 const std::vector<T> A = detail::harel_power_sums(rho, N);
176 return detail::harel_lower_bound(A, N);
192 detail::harel_reject_thinktime(Z,
"pfqn_harel_ub");
193 if (N < 1)
throw InputError(
"pfqn_harel_ub: the population must be at least 1");
194 if (n < 2)
throw InputError(
"pfqn_harel_ub: the extrapolation point must be at least 2");
195 if (n > N)
throw InputError(
"pfqn_harel_ub: the extrapolation point cannot exceed N");
197 if (n > 7)
throw InputError(
"pfqn_harel_ub: the extrapolation point cannot exceed 7");
198 detail::harel_check_rho(rho);
199 const std::vector<T> A = detail::harel_power_sums(rho, n);
200 const std::vector<T> G = detail::harel_G(A, n);
202 throw NumericError(
"pfqn_harel_ub: the normalizing constant vanishes");
203 const T THn = T(G[
static_cast<std::size_t
>(n) - 1] / G[
static_cast<std::size_t
>(n)]);
204 return detail::harel_upper_from_th(A[1], N, n, THn);
220 detail::harel_reject_thinktime(Z,
"pfqn_harel_bounds");
221 if (N < 1)
throw InputError(
"pfqn_harel_bounds: the population must be at least 1");
222 detail::harel_check_rho(rho);
223 const int effectiveMaxUB = maxUB > 0 ? maxUB : (N < 7 ? N : 7);
224 if (effectiveMaxUB > 7)
225 throw InputError(
"pfqn_harel_bounds: upper bounds are available only for n <= 7");
226 if (effectiveMaxUB > N)
227 throw InputError(
"pfqn_harel_bounds: the extrapolation point cannot exceed N");
232 res.
maxUB = effectiveMaxUB;
235 const int maxPower = N > effectiveMaxUB ? N : effectiveMaxUB;
236 const std::vector<T> A = detail::harel_power_sums(rho, maxPower);
237 res.
LB = detail::harel_lower_bound(A, N);
239 const std::vector<T> G = detail::harel_G(A, effectiveMaxUB);
241 res.
TH.assign(
static_cast<std::size_t
>(effectiveMaxUB) + 1, zero);
242 res.
UB.assign(
static_cast<std::size_t
>(effectiveMaxUB) + 1, zero);
243 for (
int n = 1; n <= effectiveMaxUB; ++n) {
244 if (G[
static_cast<std::size_t
>(n)] == zero)
245 throw NumericError(
"pfqn_harel_bounds: the normalizing constant vanishes");
246 res.
TH[
static_cast<std::size_t
>(n)] =
247 T(G[
static_cast<std::size_t
>(n) - 1] / G[
static_cast<std::size_t
>(n)]);
249 for (
int n = 2; n <= effectiveMaxUB; ++n)
250 res.
UB[
static_cast<std::size_t
>(n)] =
251 detail::harel_upper_from_th(A[1], N, n, res.
TH[
static_cast<std::size_t
>(n)]);
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
The exception types the port throws.
HarelBoundsResult< T > pfqn_harel_bounds(const std::vector< T > &rho, int N, const T &Z, int maxUB)
Both bounds, plus the exact throughputs the upper bounds extrapolate from.
T pfqn_harel_ub(const std::vector< T > &rho, int N, int n, const T &Z)
Upper bound extrapolated from the exact throughput at population n.
T pfqn_harel_lb(const std::vector< T > &rho, int N, const T &Z)
Lower bound alone.
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Number-type abstraction for the templated API port.
Return value of pfqn_harel_bounds, mirroring Ret.pfqnHarelBounds.
std::vector< T > UB
UB[n] for n = 2..maxUB; entries 0 and 1 are unset.
T LB
throughput lower bound at population N
std::vector< T > TH
TH[n] = exact throughput at population n, n = 1..maxUB.
int maxUB
largest n at which an upper bound was formed
int N
population the bounds are stated at
std::size_t k
number of stations