LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
pfqn_sib.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_SIB_H
6
#define LINE_API_PFQN_PFQN_SIB_H
7
8
/**
9
* @file
10
* @ingroup api_pfqn
11
* Successively Improving Bounds (Srinivasan 1985/1987) on the cycle time and
12
* throughput of a single-class closed product-form network.
13
*
14
* Templated port of matlab/src/api/pfqn/pfqn_sib.m, including its local
15
* functions phi_u1, sigma and betaL. The hierarchy bounds
16
* phi(K) = sum_m rho_m Q_m(K) from both sides, with level 1 the closed form of
17
* Theorem 2.1 and higher levels the S_i power-sum forms of Theorems 3.5 and
18
* 3.6, and reads the cycle time off W(N) = sum(L) (1 + phi(N-1)).
19
*
20
* REFERENCE DEFECT ABOVE LEVEL 1, and the port does NOT reproduce it.
21
* pfqn_sib.m declares phi_u1, sigma and betaL as NESTED functions, which in
22
* MATLAB share the parent's workspace rather than getting their own. phi_u1
23
* assigns `eta = (K-1)/K` internally, and the parent has already set
24
* `eta = (N-2)/(N-1)` for the Theorem 3.5 prefactor 0.5/eta. At level 1 sigma
25
* returns before it ever calls phi_u1, so eta survives and the bound is
26
* correct; from level 2 on, sigma calls phi_u1(N-3), which OVERWRITES the
27
* parent's eta, and the prefactor 0.5/eta is then evaluated with the wrong
28
* value. On L = [1/2, 1/3, 1/5], N = 5 the parent eta is 3/4 and the clobbered
29
* one is 1/2, so phi_u_n comes out 1.5x too large -- 2.4919 instead of
30
* 1.6613 -- which exceeds the Section-2 baseline 1.7798, so `min` discards it.
31
* The net effect is that MATLAB's SIB hierarchy is INERT above level 1: levels
32
* 2, 3 and 4 all return the level-0 baseline, and the "bound" reported at
33
* level 2 (X in [1.7407, 1.9335]) is LOOSER than the one at level 1
34
* ([1.7607, 1.9335]), which contradicts the monotonicity the method is for.
35
* A C++ port has no such aliasing -- the three helpers are lambdas with their
36
* own scope -- so this port computes the intended Theorem 3.5 value and its
37
* bounds do tighten with the level: level 2 gives 1.8182, level 3 gives
38
* 1.8369, both still below the exact 1.8558. Reproducing the MATLAB value here
39
* would mean deliberately reintroducing a scoping accident, so the divergence
40
* is documented and pinned in the tests instead.
41
*
42
* DELAY IS REJECTED, following the reference: the no-delay phi bounds do not
43
* bracket the with-delay congestion, so pfqn_sib.m raises
44
* pfqn_sib:delayUnsupported for Z > 0 rather than return an invalid bracket.
45
* The port throws InputError in the same case; returning a bound that is not
46
* one would be worse than refusing.
47
*
48
* ARITHMETIC. Both Theorem 3.5 and Theorem 2.1 solve a quadratic, so the
49
* bounds carry a square root and the routine is gated on
50
* num_traits<T>::has_transcendental. That is a real restriction rather than a
51
* formality: unlike the CBH and PBH families, SIB cannot be evaluated exactly.
52
*/
53
54
#include <algorithm>
55
#include <cmath>
56
#include <cstddef>
57
#include <vector>
58
59
#include "
line/num/number.h
"
60
#include "
line/util/error.h
"
61
62
namespace
line
{
63
namespace
pfqn
{
64
65
/** Return value of pfqn_sib, mirroring [Xlo, Xhi, Wlo, Whi]. */
66
template
<
class
T>
67
struct
SibBounds
{
68
T
Xlo
;
69
T
Xhi
;
70
T
Wlo
;
71
T
Whi
;
72
};
73
74
/**
75
* @brief Successively Improving Bounds (Srinivasan 1985/1987) on the cycle
76
* time and throughput of a single-class closed product-form network.
77
*
78
* @param L (M) fixed-rate demands; delay demand is NOT accepted
79
* @param N population, at least 2
80
* @param Z think time, must be zero (see the header note)
81
* @param level bound level >= 1, default 3 at the convenience overload
82
*/
83
template
<
class
T>
84
SibBounds<T>
pfqn_sib
(
const
std::vector<T>& L,
int
N,
const
T& Z,
int
level) {
85
static_assert
(
num_traits<T>::has_transcendental
,
86
"pfqn_sib requires transcendental arithmetic (the bounds solve a quadratic)"
);
87
using
std::sqrt;
88
const
std::size_t M = L.size();
89
if
(M == 0)
throw
InputError
(
"pfqn_sib: empty demand vector"
);
90
if
(N < 2)
throw
InputError
(
"pfqn_sib: population must be at least two"
);
91
const
T zero =
num_traits<T>::from_int
(0), one =
num_traits<T>::from_int
(1);
92
if
(Z > zero)
93
throw
InputError
(
94
"pfqn_sib supports Z=0 only (delay needs the Section-3.2 demand substitution)"
);
95
const
int
lv = std::max(1, level);
96
97
T Lsum = zero;
98
for
(
const
T& x : L) Lsum += x;
99
if
(Lsum == zero)
throw
InputError
(
"pfqn_sib: all demands are zero"
);
100
std::vector<T> rho(M);
101
T rho_u = zero;
102
for
(std::size_t i = 0; i < M; ++i) {
103
rho[i] = T(L[i] / Lsum);
104
if
(rho[i] > rho_u) rho_u = rho[i];
105
}
106
const
int
imax = lv + 3;
107
std::vector<T> S(
static_cast<
std::size_t
>
(imax) + 1, zero);
// S[i], i = 1..imax
108
for
(
int
i = 1; i <= imax; ++i) {
109
T s = zero;
110
for
(std::size_t m = 0; m < M; ++m) s += num_pow_int(rho[m], static_cast<unsigned>(i));
111
S[
static_cast<
std::size_t
>
(i)] = s;
112
}
113
const
T S2 = S[2];
114
115
// alpha_i, i = 0..level (eqs. 3.5-3.6).
116
std::vector<T> alpha(
static_cast<
std::size_t
>
(lv) + 1, zero);
117
alpha[0] = S2;
118
for
(
int
i = 1; i <= lv; ++i) {
119
T acc = zero;
120
for
(
int
j = 0; j <= i - 1; ++j)
121
acc += S[
static_cast<
std::size_t
>
(i + 1 - j)] * alpha[
static_cast<
std::size_t
>
(j)];
122
alpha[
static_cast<
std::size_t
>
(i)] = T(S[
static_cast<
std::size_t
>
(i + 2)] - acc);
123
}
124
125
// Level-1 upper bound on phi(K), eq. 3.18.
126
const
auto
phi_u1 = [&](
int
K) -> T {
127
if
(K <= 0)
return
zero;
128
if
(K == 1)
return
S2;
129
const
T KT =
num_traits<T>::from_int
(K);
130
const
T eta = T(T(KT - one) / KT);
131
const
T T1 = T(T(KT - one) * rho_u - one);
132
const
T disc = T(T1 * T1 +
num_traits<T>::from_int
(4) * T(KT - one) * S2);
133
return
T(T(
num_traits<T>::from_rational
(1, 2) / eta) * T(T1 + sqrt(disc)));
134
};
135
136
// eq. (3.22c). NN plays the role of N-1.
137
const
auto
sigma = [&](
int
NN,
int
i) -> T {
138
T s = zero;
139
if
(i <= 0)
return
s;
140
const
T Dbar = T(one + phi_u1(NN - 2));
141
T pnum = one;
142
for
(
int
j = 1; j <= i; ++j) {
143
pnum *=
num_traits<T>::from_int
(NN - 1 - (j - 1));
144
s += T(T(rho_u * S[
static_cast<
std::size_t
>
(j + 1)] - S[
static_cast<
std::size_t
>
(j + 2)]) *
145
pnum /
num_pow_int
(Dbar,
static_cast<
unsigned
>
(j)));
146
}
147
return
s;
148
};
149
150
// eq. (3.23c). NN plays the role of N-1.
151
const
auto
betaL = [&](
int
NN,
int
i) -> T {
152
T b = zero;
153
if
(i <= 0)
return
b;
154
for
(
int
j = 1; j <= i - 1; ++j) {
155
T p = one;
156
for
(
int
m = 2; m <= j; ++m)
157
p *= T(
num_traits<T>::from_int
(NN - m) / T(one + phi_u1(NN - m)));
158
b += alpha[
static_cast<
std::size_t
>
(j)] * p;
159
}
160
T p = one;
161
for
(
int
m = 2; m <= i; ++m) p *= T(
num_traits<T>::from_int
(NN - m) / T(one + phi_u1(NN - m)));
162
const
T Nim2 =
num_traits<T>::from_int
(NN - i - 2);
// MATLAB Nim2 = NN-1-i-1
163
if
(alpha[
static_cast<
std::size_t
>
(i) - 1] == zero)
164
throw
NumericError
(
"pfqn_sib: zero alpha coefficient in the level correction"
);
165
const
T corr =
166
T(one + T(alpha[
static_cast<
std::size_t
>
(i)] / alpha[
static_cast<
std::size_t
>
(i) - 1]) *
167
Nim2 / T(one + Nim2 * alpha[0]));
168
b += alpha[
static_cast<
std::size_t
>
(i)] * p * corr;
169
return
b;
170
};
171
172
const
int
NN = N - 1;
173
T phi_lo = T(
num_traits<T>::from_int
(N - 1) * S2);
174
const
T T1s2 = T(
num_traits<T>::from_int
(N - 1) * rho_u - one);
175
T phi_hi = T(
num_traits<T>::from_rational
(1, 2) *
176
T(T1s2 + sqrt(T(T1s2 * T1s2 +
num_traits<T>::from_int
(4 * (N - 1)) * S2))));
177
178
if
(N >= 3) {
179
const
T eta = T(
num_traits<T>::from_int
(N - 2) /
num_traits<T>::from_int
(N - 1));
180
const
T T1u = T(
num_traits<T>::from_int
(N - 2) * rho_u - one);
181
const
T su = sigma(NN, lv - 1);
182
T d = T(T1u * T1u +
num_traits<T>::from_int
(4 * (N - 2)) * T(S2 - su));
183
if
(d < zero) d = zero;
184
const
T phi_u_n = T(T(
num_traits<T>::from_rational
(1, 2) / eta) * T(T1u + sqrt(d)));
185
if
(phi_u_n < phi_hi) phi_hi = phi_u_n;
186
187
const
T T1l = T(
num_traits<T>::from_int
(N - 2) * S2 - one);
188
const
T bl = betaL(NN, lv - 1);
189
T dl = T(T1l * T1l +
num_traits<T>::from_int
(4 * (N - 2)) *
190
T(S2 +
num_traits<T>::from_int
(N - 2) * bl));
191
if
(dl < zero) dl = zero;
192
// eq (3.23) divides by 2*eta, the SAME constant eq (3.22) applies as
193
// 0.5/eta above; the Greek eta on the scan was read as the level index n
194
const
T phi_l_n = T(T(T1l + sqrt(dl)) / T(
num_traits<T>::from_int
(2) * eta));
195
if
(phi_l_n > phi_lo) phi_lo = phi_l_n;
196
}
197
198
if
(phi_lo < zero) phi_lo = zero;
199
if
(phi_hi < phi_lo) phi_hi = phi_lo;
200
201
SibBounds<T>
r;
202
r.
Wlo
= T(Lsum * T(one + phi_lo) + Z);
203
r.
Whi
= T(Lsum * T(one + phi_hi) + Z);
204
r.
Xlo
= T(
num_traits<T>::from_int
(N) / r.
Whi
);
205
r.
Xhi
= T(
num_traits<T>::from_int
(N) / r.
Wlo
);
206
return
r;
207
}
208
209
template
<
class
T>
210
SibBounds<T>
pfqn_sib
(
const
std::vector<T>& L,
int
N,
const
T& Z) {
211
return
pfqn_sib
(L, N, Z, 3);
212
}
213
214
}
// namespace pfqn
215
}
// namespace line
216
217
#endif
// LINE_API_PFQN_PFQN_SIB_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::NumericError::NumericError
NumericError(const std::string &what)
Definition
error.h:45
error.h
The exception types the port throws.
line::pfqn
Definition
cd_peak_scaling.h:43
line::pfqn::pfqn_sib
SibBounds< T > pfqn_sib(const std::vector< T > &L, int N, const T &Z, int level)
Successively Improving Bounds (Srinivasan 1985/1987) on the cycle time and throughput of a single-cla...
Definition
pfqn_sib.h:84
line
Definition
aoi_dist2ph.h:52
line::num_pow_int
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition
number.h:192
number.h
Number-type abstraction for the templated API port.
line::num_traits
Definition
number.h:111
line::pfqn::SibBounds
Return value of pfqn_sib, mirroring [Xlo, Xhi, Wlo, Whi].
Definition
pfqn_sib.h:67
line::pfqn::SibBounds::Xhi
T Xhi
Definition
pfqn_sib.h:69
line::pfqn::SibBounds::Wlo
T Wlo
Definition
pfqn_sib.h:70
line::pfqn::SibBounds::Whi
T Whi
Definition
pfqn_sib.h:71
line::pfqn::SibBounds::Xlo
T Xlo
Definition
pfqn_sib.h:68
include
line
api
pfqn
pfqn_sib.h
Generated by
1.18.0