LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
pfqn_scb.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_SCB_H
6
#define LINE_API_PFQN_PFQN_SCB_H
7
8
/**
9
* @file
10
* @ingroup api_pfqn
11
* Dowdy-Carlson-Krantz-Tripathi (1992) single-class bounds of multi-class
12
* queueing networks, J. ACM 39(1):188-213.
13
*
14
* Templated port of matlab/src/api/pfqn/pfqn_scb.m, pfqn_scbgap.m,
15
* pfqn_usumbound.m and pfqn_minclasses.m.
16
*
17
* SEMANTICS DIFFER FROM EVERY OTHER pfqn_* BOUND IN THIS TREE. aba/bjb/gb/...
18
* bracket the exact solution OF THE GIVEN MODEL; pfqn_scb brackets the
19
* multiclass system that the given single-class model aggregates. Its lower
20
* side is therefore the EXACT single-class solution, not an approximation of
21
* it, and mixing the family into an auto composite would compare two different
22
* quantities.
23
*
24
* ARITHMETIC. pfqn_scb runs the exact single-class MVA recursion (additions,
25
* multiplications and one division per population step) and then scales by a
26
* rational factor, so it stays in the field and is left ungated. The three
27
* combinatorial bounds are pure rational expressions in N, K and r.
28
*/
29
30
#include <algorithm>
31
#include <cstddef>
32
#include <vector>
33
34
#include "
line/num/number.h
"
35
#include "
line/util/error.h
"
36
37
namespace
line
{
38
namespace
pfqn
{
39
40
/** Return value of pfqn_scb, mirroring [Xlo, Xhi, Ulo, Uhi]. */
41
template
<
class
T>
42
struct
ScbBounds
{
43
T
Xlo
;
///< lower bound on multiclass throughput X_R (= exact X_1)
44
T
Xhi
;
///< upper bound on X_R
45
std::vector<T>
Ulo
;
///< (K) lower bounds on the multiclass utilizations U_k,R
46
std::vector<T>
Uhi
;
///< (K) upper bounds on U_k,R
47
};
48
49
/**
50
* Bracket on the throughput and the per-device utilizations of the UNKNOWN
51
* multiclass system whose single-class counterpart has demands L at population N.
52
*
53
* Theorem 2 / Corollary 2: aggregating an R-class model into its single-class
54
* counterpart can only understate performance, U_k,1 <= U_k,R and X_1 <= X_R,
55
* and Corollary 1 makes the utilization ratio uniform, U_k,R/U_k,1 = X_R/X_1
56
* for every k. Theorem 3 (their Expression 3) caps the relative throughput
57
* error at (m-1)/(N+m-1), m = min(N,K), independently of the demands. The
58
* single-server capacity U_k,R <= 1 caps the same ratio at 1/(X_1*max(L)),
59
* tight on the paper's own worst case, so both are applied.
60
*
61
* @param L (K) demands of the queueing stations only; a delay station is not
62
* admitted, Theorem 3 resting on the delay-free balanced-network
63
* throughput N/((N+m-1)D)
64
* @param N population (N >= 1)
65
*/
66
template
<
class
T>
67
ScbBounds<T>
pfqn_scb
(
const
std::vector<T>& L,
long
N) {
68
const
std::size_t K = L.size();
69
if
(K == 0)
throw
InputError
(
"pfqn_scb: requires at least one queueing station"
);
70
if
(N < 1)
throw
InputError
(
"pfqn_scb: requires N >= 1"
);
71
const
T zero =
num_traits<T>::from_int
(0), one =
num_traits<T>::from_int
(1);
72
73
// Exact single-class MVA at Z=0. This IS the lower bound (Theorem 2), so it
74
// is computed exactly rather than bounded: a bounded X1 would not bracket X_R.
75
std::vector<T> Q(K, zero), Rk(K, zero);
76
T X1 = zero;
77
for
(
long
n = 1; n <= N; ++n) {
78
T sumR = zero;
79
for
(std::size_t k = 0; k < K; ++k) {
80
Rk[k] = T(L[k] * T(one + Q[k]));
81
sumR += Rk[k];
82
}
83
X1 = T(
num_traits<T>::from_int
(n) / sumR);
84
for
(std::size_t k = 0; k < K; ++k) Q[k] = T(X1 * Rk[k]);
85
}
86
87
const
long
m = std::min<long>(N,
static_cast<
long
>
(K));
88
T ratio = T(
num_traits<T>::from_int
(N + m - 1) /
num_traits<T>::from_int
(N));
89
T Dmax = L[0];
90
for
(
const
T& x : L)
91
if
(x > Dmax) Dmax = x;
92
if
(T(X1 * Dmax) > zero) {
93
// U_k,R <= 1 with the uniform ratio of Corollary 1. Tight at the worst case.
94
const
T cap = T(one / T(X1 * Dmax));
95
if
(cap < ratio) ratio = cap;
96
}
97
98
ScbBounds<T>
b;
99
b.
Xlo
= X1;
100
b.
Xhi
= T(X1 * ratio);
101
b.
Ulo
.resize(K);
102
b.
Uhi
.resize(K);
103
for
(std::size_t k = 0; k < K; ++k) {
104
b.
Ulo
[k] = T(X1 * L[k]);
105
b.
Uhi
[k] = T(b.
Ulo
[k] * ratio);
106
}
107
return
b;
108
}
109
110
/**
111
* Demand-free bound on the relative throughput error incurred when r of the N
112
* single-customer classes are merged into one class. With r = N this is the
113
* full single-class aggregation error of their Theorem 3, at most 50%; with
114
* r < N it is the partial-aggregation error of their Theorem 4. The bound never
115
* reads the demands, so it can be attached as a certified error bar to any
116
* result computed on merged chains.
117
*
118
* General case, dominating classes allowed (Expression 4, and with r = N
119
* Expression 3): e = (min(r,K)-1)/(r+min(r,K)-1). Undominated case, every
120
* customer placing the same total demand (Theorem 5 and its comment (3), which
121
* lifts the N = R restriction): e = r(r-1)/(min(N,K)(2r-1)), valid for r <= K
122
* only, smaller than the general case by the factor r/min(N,K) and equal to it
123
* at r = K. THE DOMAIN IS NOT COSMETIC: Theorem 5 gives each of its R classes a
124
* dedicated device, so r never exceeds K there, and comment (3) states the
125
* generalization for r < K. Evaluated at r > K the expression climbs past the
126
* general bound and past the 50% cap of Theorem 3, i.e. it stops being a bound,
127
* so r > K is refused rather than returned.
128
*
129
* @param N total customers, one per class @param K devices
130
* @param r classes merged into one (1 <= r <= N)
131
* @param undominated true for the tighter Theorem-5 form, valid only when every
132
* customer's total device demand is equal, and only for r <= K
133
*/
134
template
<
class
T>
135
T
pfqn_scbgap
(
long
N,
long
K,
long
r,
bool
undominated) {
136
if
(N < 1 || K < 1)
throw
InputError
(
"pfqn_scbgap: requires N >= 1 and K >= 1"
);
137
if
(r < 1 || r > N)
throw
InputError
(
"pfqn_scbgap: requires 1 <= r <= N"
);
138
if
(r == 1)
return
num_traits<T>::from_int
(0);
// merging one class changes nothing
139
if
(undominated) {
140
if
(r > K)
141
throw
InputError
(
142
"pfqn_scbgap: the undominated (Theorem 5) form is defined for r <= K only; "
143
"beyond it the expression exceeds the general bound and the 50% cap"
);
144
// Theorem 5, comment (3)
145
return
T(
num_traits<T>::from_int
(r * (r - 1)) /
146
num_traits<T>::from_int
(std::min<long>(N, K) * (2 * r - 1)));
147
}
148
const
long
m = std::min<long>(r, K);
149
// Expression (4); r=N gives (3)
150
return
T(
num_traits<T>::from_int
(m - 1) /
num_traits<T>::from_int
(r + m - 1));
151
}
152
153
/** Full single-class aggregation: r = N, dominating classes allowed. */
154
template
<
class
T>
155
T
pfqn_scbgap
(
long
N,
long
K) {
156
return
pfqn_scbgap<T>
(N, K, N,
false
);
157
}
158
159
/**
160
* Largest value the sum of device utilizations can take in any closed
161
* product-form network with R classes, K devices and N customers (Theorem 6):
162
* sum_k U_k,R <= (H-1) + (K-H+1)(N-H+1)/(K+N-2H+1), H = min(R,K). Demand-free
163
* and nondecreasing in R, which is what makes it invertible into a lower bound
164
* on the number of necessary classes; see pfqn_minclasses. The paper's worked
165
* case is K = 2, N = 3, R = 1, giving 2N/(N+1) = 1.5.
166
*/
167
template
<
class
T>
168
T
pfqn_usumbound
(
long
R,
long
K,
long
N) {
169
if
(N < 1 || K < 1)
throw
InputError
(
"pfqn_usumbound: requires N >= 1 and K >= 1"
);
170
if
(R < 1 || R > N)
throw
InputError
(
"pfqn_usumbound: requires 1 <= R <= N"
);
171
const
long
H = std::min<long>(R, K);
172
return
T(
num_traits<T>::from_int
(H - 1) +
173
T(
num_traits<T>::from_int
((K - H + 1) * (N - H + 1)) /
174
num_traits<T>::from_int
(K + N - 2 * H + 1)));
175
}
176
177
/**
178
* Smallest number of customer classes R consistent with an observed sum of
179
* device utilizations, by inverting the nondecreasing pfqn_usumbound. Only
180
* measured quantities are needed -- the utilizations, the device count and the
181
* population -- so the answer is available BEFORE any class-specific demand has
182
* been characterized. An upper bound on R is meaningless and none is returned.
183
*
184
* The paper's example: K = 2, N = 3, Usum = 1.6 -> 2, a single class admitting
185
* at most 2N/(N+1) = 1.5.
186
*
187
* @return least R in 1..N with pfqn_usumbound(R,K,N) >= Usum, or -1 where Usum
188
* exceeds min(N,K) and so is unattainable by ANY class structure. -1 is
189
* this port's integral encoding of the NaN MATLAB and Python return.
190
*/
191
template
<
class
T>
192
long
pfqn_minclasses
(
const
T& Usum,
long
K,
long
N) {
193
if
(N < 1 || K < 1)
throw
InputError
(
"pfqn_minclasses: requires N >= 1 and K >= 1"
);
194
if
(Usum <
num_traits<T>::from_int
(0))
195
throw
InputError
(
"pfqn_minclasses: requires a nonnegative utilization sum"
);
196
// The reference's relative slack, reproduced so a boundary case decides the
197
// same way in both codebases; in exact arithmetic it only widens by 1e-12.
198
const
T one =
num_traits<T>::from_int
(1);
199
const
T scale = Usum > one ? Usum : one;
200
const
T tol = T(
num_traits<T>::from_double
(1e-12) * scale);
201
for
(
long
R = 1; R <= N; ++R)
202
if
(!(
pfqn_usumbound<T>
(R, K, N) < T(Usum - tol)))
return
R;
203
return
-1;
204
}
205
206
}
// namespace pfqn
207
}
// namespace line
208
209
#endif
// LINE_API_PFQN_PFQN_SCB_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
error.h
The exception types the port throws.
line::pfqn
Definition
cd_peak_scaling.h:43
line::pfqn::pfqn_scb
ScbBounds< T > pfqn_scb(const std::vector< T > &L, long N)
Bracket on the throughput and the per-device utilizations of the UNKNOWN multiclass system whose sing...
Definition
pfqn_scb.h:67
line::pfqn::pfqn_minclasses
long pfqn_minclasses(const T &Usum, long K, long N)
Smallest number of customer classes R consistent with an observed sum of device utilizations,...
Definition
pfqn_scb.h:192
line::pfqn::pfqn_usumbound
T pfqn_usumbound(long R, long K, long N)
Largest value the sum of device utilizations can take in any closed product-form network with R class...
Definition
pfqn_scb.h:168
line::pfqn::pfqn_scbgap
T pfqn_scbgap(long N, long K, long r, bool undominated)
Demand-free bound on the relative throughput error incurred when r of the N single-customer classes a...
Definition
pfqn_scb.h:135
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
line::num_traits
Definition
number.h:111
line::pfqn::ScbBounds
Return value of pfqn_scb, mirroring [Xlo, Xhi, Ulo, Uhi].
Definition
pfqn_scb.h:42
line::pfqn::ScbBounds::Ulo
std::vector< T > Ulo
(K) lower bounds on the multiclass utilizations U_k,R
Definition
pfqn_scb.h:45
line::pfqn::ScbBounds::Xlo
T Xlo
lower bound on multiclass throughput X_R (= exact X_1)
Definition
pfqn_scb.h:43
line::pfqn::ScbBounds::Xhi
T Xhi
upper bound on X_R
Definition
pfqn_scb.h:44
line::pfqn::ScbBounds::Uhi
std::vector< T > Uhi
(K) upper bounds on U_k,R
Definition
pfqn_scb.h:46
include
line
api
pfqn
pfqn_scb.h
Generated by
1.18.0