LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
pfqn_sqni.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_SQNI_H
6
#define LINE_API_PFQN_PFQN_SQNI_H
7
8
/**
9
* @file
10
* @ingroup api_pfqn
11
* Square-root non-iterative (SQNI) approximation for a single queueing station
12
* with per-class delay.
13
*
14
* Templated port of matlab/src/api/pfqn/pfqn_sqni.m. Each class throughput is
15
* the admissible root of a quadratic assembled from the balanced-job estimate
16
* B_r of the other classes' contribution:
17
*
18
* X_r = (Z_r - sqrt(disc) - B_r + L_r Ntot) / (2 L_r Z_r)
19
* disc = B_r^2 - 2 B_r L_r Ntot - 2 B_r Z_r + L_r^2 Ntot^2 + 2 L_r Ntot Z_r
20
* - 4 N_r L_r Z_r + Z_r^2
21
*
22
* with the discriminant clamped at zero, as in the reference. Classes with
23
* Z_r = 0 (self-looping classes, whose quadratic is degenerate) are handled by
24
* the reference's two-pass structure: their queue length is pre-set to N_r,
25
* and their throughput is filled in from the total queue length afterwards.
26
*
27
* ARITHMETIC. The square root is essential, so the routine is gated on
28
* num_traits<T>::has_transcendental.
29
*/
30
31
#include <cmath>
32
#include <cstddef>
33
#include <vector>
34
35
#include "
line/num/number.h
"
36
#include "
line/util/error.h
"
37
38
namespace
line
{
39
namespace
pfqn
{
40
41
/** Return value of pfqn_sqni, mirroring [Q, U, X] for the single station. */
42
template
<
class
T>
43
struct
SqniResult
{
44
std::vector<T>
Q
;
45
std::vector<T>
U
;
46
std::vector<T>
X
;
47
};
48
49
/**
50
* @brief Square-root non-iterative (SQNI) approximation for a single queueing
51
* station with per-class delay.
52
*
53
* @param N (R) population, @param L (R) demand at the station,
54
* @param Z (R) think times
55
*/
56
template
<
class
T>
57
SqniResult<T>
pfqn_sqni
(
const
std::vector<T>& N,
const
std::vector<T>& L,
const
std::vector<T>& Z) {
58
static_assert
(
num_traits<T>::has_transcendental
,
59
"pfqn_sqni requires transcendental arithmetic (the throughput solves a quadratic)"
);
60
using
std::sqrt;
61
const
std::size_t C = L.size();
62
if
(N.size() != C || Z.size() != C)
63
throw
InputError
(
"pfqn_sqni: N, L and Z must have the same length"
);
64
const
T zero =
num_traits<T>::from_int
(0), one =
num_traits<T>::from_int
(1);
65
const
T two =
num_traits<T>::from_int
(2), four =
num_traits<T>::from_int
(4);
66
67
SqniResult<T>
r;
68
r.
Q
.assign(C, zero);
69
r.
U
.assign(C, zero);
70
r.
X
.assign(C, zero);
71
T Nt = zero;
72
for
(
const
T& v : N) Nt += v;
73
if
(Nt <= zero)
return
r;
74
75
if
(Nt == one) {
76
for
(std::size_t s = 0; s < C; ++s) {
77
const
T den = T(Z[s] + L[s]);
78
if
(den == zero)
throw
NumericError
(
"pfqn_sqni: zero cycle time"
);
79
r.
X
[s] = T(N[s] / den);
80
r.
U
[s] = T(r.
X
[s] * L[s]);
81
r.
Q
[s] = r.
U
[s];
82
}
83
return
r;
84
}
85
86
for
(std::size_t s = 0; s < C; ++s)
87
if
(Z[s] == zero) r.
Q
[s] = N[s];
88
89
for
(std::size_t s = 0; s < C; ++s) {
90
if
(Z[s] == zero)
continue
;
// handled after the main loop
91
const
T Nr = N[s], Lr = L[s], Zr = Z[s];
92
// B_r: balanced-job estimate of the delay-resident population of the
93
// other classes, at the population with one class-r job removed.
94
T inner = zero;
95
for
(std::size_t t = 0; t < C; ++t) {
96
T N1 = N[t];
97
if
(t == s) N1 -= one;
98
const
T d = T(Z[t] + L[t] + L[t] * T(Nt - two));
99
if
(d == zero)
continue
;
100
inner += T(Z[t] * N1 / d);
101
}
102
T Brsum = zero;
103
for
(std::size_t t = 0; t < C; ++t) {
104
if
(t == s)
continue
;
105
const
T d = T(Z[t] + L[t] + L[t] * T(Nt - one - inner));
106
if
(d == zero)
continue
;
107
Brsum += T(N[t] / d * Z[t]);
108
}
109
const
T Br = T(Lr * Brsum);
110
if
(Lr == zero) {
111
r.
X
[s] = T(Nr / Zr);
112
}
else
{
113
T disc = T(Br * Br - two * Br * Lr * Nt - two * Br * Zr + Lr * Lr * Nt * Nt +
114
two * Lr * Nt * Zr - four * Nr * Lr * Zr + Zr * Zr);
115
if
(disc < zero) disc = zero;
116
r.
X
[s] = T(T(Zr - sqrt(disc) - Br + Lr * Nt) / T(two * Lr * Zr));
117
}
118
r.
U
[s] = T(r.
X
[s] * L[s]);
119
r.
Q
[s] = T(N[s] - r.
X
[s] * Z[s]);
120
}
121
122
T Qtot = zero;
123
for
(
const
T& v : r.
Q
) Qtot += v;
124
for
(std::size_t s = 0; s < C; ++s) {
125
if
(Z[s] != zero)
continue
;
126
if
(L[s] == zero)
throw
NumericError
(
"pfqn_sqni: a class has neither demand nor think time"
);
127
r.
X
[s] = T(N[s] / T(L[s] * T(one + Qtot)));
128
r.
U
[s] = T(r.
X
[s] * L[s]);
129
r.
Q
[s] = T(N[s] - r.
X
[s] * Z[s]);
130
}
131
return
r;
132
}
133
134
}
// namespace pfqn
135
}
// namespace line
136
137
#endif
// LINE_API_PFQN_PFQN_SQNI_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_sqni
SqniResult< T > pfqn_sqni(const std::vector< T > &N, const std::vector< T > &L, const std::vector< T > &Z)
Square-root non-iterative (SQNI) approximation for a single queueing station with per-class delay.
Definition
pfqn_sqni.h:57
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::SqniResult
Return value of pfqn_sqni, mirroring [Q, U, X] for the single station.
Definition
pfqn_sqni.h:43
line::pfqn::SqniResult::X
std::vector< T > X
Definition
pfqn_sqni.h:46
line::pfqn::SqniResult::Q
std::vector< T > Q
Definition
pfqn_sqni.h:44
line::pfqn::SqniResult::U
std::vector< T > U
Definition
pfqn_sqni.h:45
include
line
api
pfqn
pfqn_sqni.h
Generated by
1.18.0