LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_pbh.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_PBH_H
6#define LINE_API_PFQN_PFQN_PBH_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Performance Bound Hierarchy (Eager and Sevcik 1983, ACM TOCS 1(2):99-115)
12 * for single-class closed product-form networks, and the two iterative
13 * families that are defined in terms of it.
14 *
15 * Templated port of matlab/src/api/pfqn/pfqn_pbh.m, pfqn_pbk.m and
16 * pfqn_bjbk.m. `level` MVA steps from an ABA-initialized residence give nested
17 * optimistic and pessimistic bounds that converge to exact MVA as level -> N.
18 *
19 * MATLAB REDUNDANCY, reproduced rather than hidden: pfqn_pbk.m and
20 * pfqn_bjbk.m are both one-line forwarders to pfqn_pbh with the same
21 * arguments, so PB(k) and BJB(k) return identical numbers for every input.
22 * The two names are kept because the surrounding solver code refers to both,
23 * and collapsing them here would hide the fact in the port.
24 *
25 * ARITHMETIC. The recursion is a finite sequence of field operations -- no
26 * root, no logarithm anywhere -- so these bounds are EXACT in rational
27 * arithmetic and are deliberately left ungated. That is worth having: a bound
28 * violated only by rounding cannot be told apart from a real violation.
29 */
30
31#include <algorithm>
32#include <cstddef>
33#include <vector>
34
35#include "line/num/number.h"
36#include "line/util/error.h"
37
38namespace line {
39namespace pfqn {
40
41/** Return value of pfqn_pbh, mirroring [Xlo, Xhi, Qlo, Qhi]. */
42template <class T>
43struct PbhBounds {
44 T Xlo;
45 T Xhi;
46 std::vector<T> Qlo;
47 std::vector<T> Qhi;
48};
49
50namespace detail {
51
52/** Per-station residence vector of the level-`level` bound, one side. */
53template <class T>
54std::vector<T> pbh_residence(const std::vector<T>& L, int N, const T& Z, int level, bool optimistic) {
55 const std::size_t K = L.size();
56 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
57 std::size_t b = 0;
58 for (std::size_t i = 1; i < K; ++i)
59 if (L[i] > L[b]) b = i;
60 const int lv = std::min(level, N);
61 const int n0 = N - lv;
62 T Lsum = zero;
63 for (const T& x : L) Lsum += x;
64
65 std::vector<T> Rk(K, zero);
66 if (optimistic) {
67 T asym = T(num_traits<T>::from_int(n0) * L[b] - Z);
68 if (asym < Lsum) asym = Lsum;
69 const T v = T(asym / num_traits<T>::from_int(static_cast<long>(K)));
70 Rk.assign(K, v);
71 } else {
72 Rk.assign(K, zero);
73 Rk[b] = num_traits<T>::from_int(n0);
74 }
75 if (n0 == 0) Rk.assign(K, zero);
76
77 for (int n = n0 + 1; n <= N; ++n) {
78 T Rtot = zero;
79 for (const T& x : Rk) Rtot += x;
80 if (n == 1 || T(Z + Rtot) == zero) {
81 Rk = L;
82 } else {
83 const T f = T(num_traits<T>::from_int(n - 1) / T(Z + Rtot));
84 for (std::size_t i = 0; i < K; ++i) Rk[i] = T(L[i] * T(one + f * Rk[i]));
85 }
86 }
87 return Rk;
88}
89
90} // namespace detail
91
92/**
93 * @brief Performance Bound Hierarchy (Eager and Sevcik 1983, ACM TOCS
94 * 1(2):99-115) for single-class closed product-form networks, and the
95 * two iterative families that are defined in terms of it.
96 *
97 * @param L (M) per-station demands
98 * @param N population
99 * @param Z think time
100 * @param level hierarchy level >= 0, clamped to N
101 */
102template <class T>
103PbhBounds<T> pfqn_pbh(const std::vector<T>& L, int N, const T& Z, int level) {
104 const std::size_t K = L.size();
105 if (K == 0) throw InputError("pfqn_pbh: empty demand vector");
106 if (N < 0) throw InputError("pfqn_pbh: negative population");
107 if (level < 0) throw InputError("pfqn_pbh: negative hierarchy level");
108 const T zero = num_traits<T>::from_int(0);
109 T Lmax = L[0], Lsum = zero;
110 for (const T& x : L) {
111 Lsum += x;
112 if (x > Lmax) Lmax = x;
113 }
114
115 const std::vector<T> Ro = detail::pbh_residence(L, N, Z, level, true);
116 const std::vector<T> Rp = detail::pbh_residence(L, N, Z, level, false);
117
118 T Rosum = zero, Rpsum = zero;
119 for (const T& x : Ro) Rosum += x;
120 for (const T& x : Rp) Rpsum += x;
121
122 // Joint with the asymptotic residence lower bound R(N) >= max(N Lmax - Z, sum L).
123 T RoC = Rosum;
124 T asym = T(num_traits<T>::from_int(N) * Lmax - Z);
125 if (asym < Lsum) asym = Lsum;
126 if (RoC < asym) RoC = asym;
127
128 PbhBounds<T> r;
129 if (Lmax == zero) throw NumericError("pfqn_pbh: all demands are zero");
130 r.Xhi = T(num_traits<T>::from_int(1) / Lmax);
131 const T alt = T(num_traits<T>::from_int(N) / T(Z + RoC));
132 if (alt < r.Xhi) r.Xhi = alt;
133 r.Xlo = T(num_traits<T>::from_int(N) / T(Z + Rpsum));
134
135 r.Qlo.resize(K);
136 r.Qhi.resize(K);
137 for (std::size_t i = 0; i < K; ++i) {
138 r.Qlo[i] = T(r.Xlo * Ro[i]);
139 r.Qhi[i] = T(r.Xhi * Rp[i]);
140 }
141 return r;
142}
143
144template <class T>
145PbhBounds<T> pfqn_pbh(const std::vector<T>& L, int N, const T& Z) {
146 return pfqn_pbh(L, N, Z, 1);
147}
148
149/** PB(k), the iterative Eager-Sevcik proportional bound. Forwards to pfqn_pbh. */
150template <class T>
151PbhBounds<T> pfqn_pbk(const std::vector<T>& L, int N, const T& Z, int k) {
152 return pfqn_pbh(L, N, Z, k);
153}
154
155template <class T>
156PbhBounds<T> pfqn_pbk(const std::vector<T>& L, int N, const T& Z) {
157 return pfqn_pbh(L, N, Z, 1);
158}
159
160/** BJB(k), the iterative Balanced Job Bound. Forwards to pfqn_pbh. */
161template <class T>
162PbhBounds<T> pfqn_bjbk(const std::vector<T>& L, int N, const T& Z, int k) {
163 return pfqn_pbh(L, N, Z, k);
164}
165
166template <class T>
167PbhBounds<T> pfqn_bjbk(const std::vector<T>& L, int N, const T& Z) {
168 return pfqn_pbh(L, N, Z, 1);
169}
170
171} // namespace pfqn
172} // namespace line
173
174#endif // LINE_API_PFQN_PFQN_PBH_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
PbhBounds< T > pfqn_pbk(const std::vector< T > &L, int N, const T &Z, int k)
PB(k), the iterative Eager-Sevcik proportional bound.
Definition pfqn_pbh.h:151
PbhBounds< T > pfqn_bjbk(const std::vector< T > &L, int N, const T &Z, int k)
BJB(k), the iterative Balanced Job Bound.
Definition pfqn_pbh.h:162
PbhBounds< T > pfqn_pbh(const std::vector< T > &L, int N, const T &Z, int level)
Performance Bound Hierarchy (Eager and Sevcik 1983, ACM TOCS 1(2):99-115) for single-class closed pro...
Definition pfqn_pbh.h:103
Number-type abstraction for the templated API port.
Return value of pfqn_pbh, mirroring [Xlo, Xhi, Qlo, Qhi].
Definition pfqn_pbh.h:43
std::vector< T > Qlo
Definition pfqn_pbh.h:46
std::vector< T > Qhi
Definition pfqn_pbh.h:47