LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
pfqn_lldfun.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_LLDFUN_H
6
#define LINE_API_PFQN_LLDFUN_H
7
8
/**
9
* @file
10
* @ingroup api_pfqn
11
* AMVA-QD limited-load-dependence function.
12
*
13
* Templated port of matlab/src/api/pfqn/pfqn_lldfun.m, cross-checked against
14
* jar/src/main/java/jline/api/pfqn/ld/Pfqn_lldfun.java. Returns, for every
15
* station i, the reciprocal service-capacity multiplier at the (fractional)
16
* queue length n(i), combining
17
*
18
* - the multiserver term 1/softmin(n_i, c_i, alpha), a smooth approximation
19
* of 1/min(n_i, c_i), with alpha = 20 as in the reference. A delay station
20
* (c_i infinite) contributes 1; the caller handles the 1/n_i itself.
21
* - the generic load-dependence term 1/alpha_i(n_i), obtained by CLAMPED
22
* LINEAR interpolation of the lattice lldscaling(i, 1..smax) at
23
* min(max(n_i,1), smax). Linear is exact for the piecewise-linear
24
* min(1:N, c) lattice that load dependence is overwhelmingly used to
25
* express, whereas a cubic spline overshoots between knots and extrapolates
26
* to negative rates past the lattice. A station whose lattice row is
27
* constant is skipped, exactly as `range(lldscaling(i,:)) > 0` does.
28
*
29
* softmin. The reference rewrites the literal weighted average
30
* (x e^{-ax} + y e^{-ay})/(e^{-ax} + e^{-ay}) as lo + gap w/(1+w) with
31
* w = e^{-a gap}, so the exponent argument is never positive and the limit
32
* w -> 0 returns min(x,y) exactly; that form is reproduced here, including its
33
* cutoff at a*gap > 745 where exp underflows to zero in double.
34
*
35
* Arithmetic: TRANSCENDENTAL-GATED, and here on a genuine transcendental, not
36
* on a tolerance: softmin evaluates exp(-alpha |n_i - c_i|), which is not an
37
* element of the field generated by the inputs for any alpha and gap of
38
* interest. There is no exact formulation of a soft minimum.
39
*/
40
41
#include <cmath>
42
#include <cstddef>
43
#include <limits>
44
#include <vector>
45
46
#include "
line/num/number.h
"
47
#include "
line/util/error.h
"
48
#include "
line/util/matrix.h
"
49
50
namespace
line
{
51
namespace
pfqn
{
52
53
namespace
detail {
54
55
/** matlab/src/util/softmin.m, in the overflow-free form. */
56
template
<
class
T>
57
T softmin(
const
T& x,
const
T& y,
double
alpha) {
58
const
T lo = x < y ? x : y;
59
const
T hi = x < y ? y : x;
60
const
T gap = hi - lo;
61
// exp(-t) underflows to exactly 0 for t > 745.13; beyond that softmin is min.
62
if
(!(num_traits<T>::to_double(gap) < 745.0 / alpha))
return
lo;
63
using
std::exp;
64
const
T w = exp(T(-num_traits<T>::from_double(alpha) * gap));
65
return
lo + gap * w / (num_traits<T>::from_int(1) + w);
66
}
67
68
}
// namespace detail
69
70
/**
71
* @brief AMVA-QD limited-load-dependence function.
72
*
73
* @param n (M) queue lengths, possibly fractional
74
* @param lldscaling (M x smax) rate lattice; empty for none
75
* @param nservers (M) server counts; a non-finite entry marks a delay
76
* station. Empty to skip the multiserver term entirely,
77
* which is what omitting the argument does in MATLAB.
78
* @return (M) reciprocals of the effective rate multipliers
79
*/
80
template
<
class
T>
81
std::vector<T>
pfqn_lldfun
(
const
std::vector<T>& n,
const
Matrix<T>
& lldscaling,
82
const
std::vector<double>& nservers) {
83
static_assert
(
num_traits<T>::has_transcendental
,
84
"pfqn_lldfun requires transcendental arithmetic"
);
85
86
const
std::size_t M = n.size();
87
const
T one =
num_traits<T>::from_int
(1);
88
std::vector<T> r(M, one);
89
if
(!nservers.empty() && nservers.size() != M)
90
throw
InputError
(
"pfqn_lldfun: server-count vector has the wrong station count"
);
91
if
(!lldscaling.
empty
() && lldscaling.
rows
() != M)
92
throw
InputError
(
"pfqn_lldfun: rate lattice has the wrong station count"
);
93
const
std::size_t smax = lldscaling.
empty
() ? 0 : lldscaling.
cols
();
94
const
double
alpha = 20.0;
95
96
for
(std::size_t i = 0; i < M; ++i) {
97
if
(!nservers.empty()) {
98
if
(!std::isfinite(nservers[i])) {
99
// Delay station: the 1/n_i is applied by the caller.
100
r[i] = one;
101
}
else
{
102
const
T c =
num_traits<T>::from_double
(nservers[i]);
103
const
T sm = detail::softmin(n[i], c, alpha);
104
if
(sm ==
num_traits<T>::from_int
(0)) {
105
// The soft minimum collapses only when both arguments do.
106
const
T m = n[i] < c ? n[i] : c;
107
if
(m ==
num_traits<T>::from_int
(0))
108
throw
NumericError
(
"pfqn_lldfun: zero queue length and zero server count"
);
109
r[i] = r[i] / m;
110
}
else
{
111
r[i] = r[i] / sm;
112
}
113
}
114
}
115
if
(smax > 0) {
116
T lo = lldscaling(i, 0), hi = lldscaling(i, 0);
117
for
(std::size_t k = 1; k < smax; ++k) {
118
if
(lldscaling(i, k) < lo) lo = lldscaling(i, k);
119
if
(lldscaling(i, k) > hi) hi = lldscaling(i, k);
120
}
121
if
(hi > lo) {
122
// Clamp into [1, smax] and interpolate linearly between knots.
123
T x = n[i];
124
const
T xlo = one, xhi =
num_traits<T>::from_int
(
static_cast<
long
>
(smax));
125
if
(x < xlo) x = xlo;
126
if
(x > xhi) x = xhi;
127
const
double
xd =
num_traits<T>::to_double
(x);
128
std::size_t k0 =
static_cast<
std::size_t
>
(xd) - 1;
129
if
(k0 + 1 >= smax) k0 = smax - 2;
130
const
T t = x -
num_traits<T>::from_int
(
static_cast<
long
>
(k0) + 1);
131
const
T val =
132
lldscaling(i, k0) + t * (lldscaling(i, k0 + 1) - lldscaling(i, k0));
133
if
(val ==
num_traits<T>::from_int
(0))
134
throw
NumericError
(
"pfqn_lldfun: the interpolated rate multiplier is zero"
);
135
r[i] = r[i] / val;
136
}
137
}
138
}
139
return
r;
140
}
141
142
/** Overload without the multiserver term, matching the two-argument MATLAB call. */
143
template
<
class
T>
144
std::vector<T>
pfqn_lldfun
(
const
std::vector<T>& n,
const
Matrix<T>
& lldscaling) {
145
return
pfqn_lldfun
(n, lldscaling, std::vector<double>());
146
}
147
148
}
// namespace pfqn
149
}
// namespace line
150
151
#endif
// LINE_API_PFQN_LLDFUN_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::Matrix
Definition
matrix.h:56
line::Matrix::cols
std::size_t cols() const
Definition
matrix.h:90
line::Matrix::rows
std::size_t rows() const
Definition
matrix.h:89
line::Matrix::empty
bool empty() const
Definition
matrix.h:92
line::NumericError::NumericError
NumericError(const std::string &what)
Definition
error.h:45
error.h
The exception types the port throws.
matrix.h
Dense matrix and non-owning view.
line::pfqn
Definition
cd_peak_scaling.h:43
line::pfqn::pfqn_lldfun
std::vector< T > pfqn_lldfun(const std::vector< T > &n, const Matrix< T > &lldscaling, const std::vector< double > &nservers)
AMVA-QD limited-load-dependence function.
Definition
pfqn_lldfun.h:81
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
line::num_traits
Definition
number.h:111
include
line
api
pfqn
pfqn_lldfun.h
Generated by
1.18.0