LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
pfqn_rgf.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_RGF_H
6
#define LINE_API_PFQN_PFQN_RGF_H
7
8
/**
9
* @file
10
* @ingroup api_pfqn
11
* Recursion by Generating Functions (RGF) for the normalizing constant of a
12
* SINGLE-CLASS closed product-form network with replicated stations.
13
*
14
* Templated port of matlab/src/api/pfqn/pfqn_rgf.m, i.e. Property 1 of
15
* J. Coury and P. G. Harrison, "Asymptotic properties of queuing networks",
16
* IEE Proc.-Comput. Digit. Tech. 144(5):247-254, 1997.
17
*
18
* WHAT MAKES IT DIFFERENT FROM BUZEN. Convolution proceeds per GENERATING
19
* FUNCTION rather than per station: a group of m stations sharing one demand p
20
* collapses into the single negative-binomial sequence r(k) = C(k+m-1,k) p^k,
21
* so the group costs one convolution pass instead of m. The delay contributes
22
* the Poisson sequence r(k) = Z^k / k!. Convolving the G distinct sequences
23
* yields g(0..N) exactly, and lG = log g(N).
24
*
25
* Cost O(G N^2) against Buzen's O(M N), so RGF is the cheaper route precisely
26
* when the model is heavily replicated and the population moderate (G N < M).
27
*
28
* ARITHMETIC. The whole recursion runs in the LOG domain -- that is the point
29
* of the routine, since no intermediate can then overflow or underflow -- so it
30
* is gated on num_traits<T>::has_transcendental. A caller that wants the same
31
* constant in exact arithmetic wants pfqn_ca, which computes it by the ordinary
32
* convolution over the rationals.
33
*
34
* SINGLE CLASS BY CONSTRUCTION. Grouping stations by their demand only defines
35
* a sequence when the demand is a scalar, so a multiclass argument is refused
36
* here by name; `pfqn_nc`'s 'rgf' branch sends a multiclass model to pfqn_ca
37
* instead, which is what the reference does.
38
*/
39
40
#include <algorithm>
41
#include <cmath>
42
#include <cstddef>
43
#include <limits>
44
#include <vector>
45
46
#include "
line/api/pfqn/pfqn_asympt_common.h
"
47
#include "
line/num/number.h
"
48
#include "
line/util/error.h
"
49
#include "
line/util/matrix.h
"
50
51
namespace
line
{
52
namespace
pfqn
{
53
54
/** Return value of pfqn_rgf, mirroring [G, lG, lg]. */
55
template
<
class
T>
56
struct
RgfResult
{
57
T
G
;
///< normalizing constant
58
T
lG
;
///< its logarithm, i.e. lg[N]
59
std::vector<T>
lg
;
///< log g(0), log g(1), ..., log g(N)
60
};
61
62
namespace
detail {
63
64
/** Log-domain linear convolution truncated at the common length (logconv.m). */
65
template
<
class
T>
66
std::vector<T> rgf_logconv(
const
std::vector<T>& u,
const
std::vector<T>& v) {
67
const
std::size_t n = u.size();
68
std::vector<T> c(n,
num_traits<T>::from_double
(-std::numeric_limits<double>::infinity()));
69
for
(std::size_t k = 0; k < n; ++k) {
70
std::vector<T> t(k + 1);
71
for
(std::size_t i = 0; i <= k; ++i) t[i] = T(u[i] + v[k - i]);
72
// logsumexp returns the maximum unchanged when every term is -inf,
73
// which is the reference's `if isinf(vm), c(k) = vm` branch.
74
c[k] = logsumexp(t);
75
}
76
return
c;
77
}
78
79
}
// namespace detail
80
81
/**
82
* @brief Recursion by Generating Functions (RGF) for the normalizing constant
83
* of a SINGLE-CLASS closed product-form network with replicated
84
* stations.
85
*
86
* @param L (M) service demands of the queueing stations
87
* @param N population, a nonnegative integer
88
* @param Z aggregate delay demand (think time); zero for none
89
*/
90
template
<
class
T>
91
RgfResult<T>
pfqn_rgf
(
const
std::vector<T>& L,
int
N,
const
T& Z) {
92
static_assert
(
num_traits<T>::has_transcendental
,
93
"pfqn_rgf requires transcendental arithmetic: the recursion is carried in the "
94
"log domain so that no intermediate can overflow. Use pfqn_ca for the same "
95
"constant in exact arithmetic"
);
96
using
std::exp;
97
using
std::log;
98
const
T zero =
num_traits<T>::from_int
(0);
99
const
T ninf =
num_traits<T>::from_double
(-std::numeric_limits<double>::infinity());
100
101
if
(N < 0)
throw
InputError
(
"pfqn_rgf: requires a nonnegative integer population"
);
102
if
(Z < zero)
throw
InputError
(
"pfqn_rgf: requires a nonnegative think time"
);
103
for
(
const
T& v : L)
104
if
(v < zero)
throw
InputError
(
"pfqn_rgf: requires nonnegative demands"
);
105
106
const
std::size_t Np =
static_cast<
std::size_t
>
(N);
107
RgfResult<T>
res;
108
// g(k) = 1 for the empty network, 0 elsewhere before any node is folded in.
109
res.
lg
.assign(Np + 1, ninf);
110
res.
lg
[0] = zero;
111
112
// Delay node: the Poisson sequence Z^k / k!.
113
if
(Z > zero) {
114
std::vector<T> lr(Np + 1);
115
for
(std::size_t k = 0; k <= Np; ++k) {
116
const
T kT =
num_traits<T>::from_int
(
static_cast<
long
>
(k));
117
lr[k] = T(kT * log(Z) - detail::num_factln<T>(kT));
118
}
119
res.
lg
= detail::rgf_logconv(res.
lg
, lr);
120
}
121
122
// Queueing stations grouped by identical demand: one sequence per group.
123
// The reference groups through unique(L), which sorts; the multiplicity is
124
// all that enters the sequence, so sorting a copy reproduces it exactly.
125
std::vector<T> pos;
126
for
(
const
T& v : L)
127
if
(v > zero) pos.push_back(v);
128
std::sort(pos.begin(), pos.end(), [](
const
T& a,
const
T& b) { return a < b; });
129
std::size_t i = 0;
130
while
(i < pos.size()) {
131
std::size_t j = i;
132
while
(j < pos.size() && pos[j] == pos[i]) ++j;
133
const
std::size_t m = j - i;
134
const
T p = pos[i];
135
std::vector<T> lr(Np + 1);
136
for
(std::size_t k = 0; k <= Np; ++k) {
137
const
T kT =
num_traits<T>::from_int
(
static_cast<
long
>
(k));
138
if
(m == 1) {
139
// 1 / (1 - p u)
140
lr[k] = T(kT * log(p));
141
}
else
{
142
const
T mT =
num_traits<T>::from_int
(
static_cast<
long
>
(m));
143
lr[k] = T(detail::num_lgamma<T>(T(kT + mT)) - detail::num_factln<T>(kT) -
144
detail::num_lgamma<T>(mT) + kT * log(p));
145
}
146
}
147
res.
lg
= detail::rgf_logconv(res.
lg
, lr);
148
i = j;
149
}
150
151
res.
lG
= res.
lg
[Np];
152
res.
G
= exp(res.
lG
);
153
return
res;
154
}
155
156
/** Overload without a delay. */
157
template
<
class
T>
158
RgfResult<T>
pfqn_rgf
(
const
std::vector<T>& L,
int
N) {
159
return
pfqn_rgf
(L, N,
num_traits<T>::from_int
(0));
160
}
161
162
}
// namespace pfqn
163
}
// namespace line
164
165
#endif
// LINE_API_PFQN_PFQN_RGF_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
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_rgf
RgfResult< T > pfqn_rgf(const std::vector< T > &L, int N, const T &Z)
Recursion by Generating Functions (RGF) for the normalizing constant of a SINGLE-CLASS closed product...
Definition
pfqn_rgf.h:91
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
pfqn_asympt_common.h
Shared scalar machinery for the integration / asymptotic members of the pfqn family (pfqn_le,...
line::num_traits
Definition
number.h:111
line::pfqn::RgfResult
Return value of pfqn_rgf, mirroring [G, lG, lg].
Definition
pfqn_rgf.h:56
line::pfqn::RgfResult::lG
T lG
its logarithm, i.e. lg[N]
Definition
pfqn_rgf.h:58
line::pfqn::RgfResult::G
T G
normalizing constant
Definition
pfqn_rgf.h:57
line::pfqn::RgfResult::lg
std::vector< T > lg
log g(0), log g(1), ..., log g(N)
Definition
pfqn_rgf.h:59
include
line
api
pfqn
pfqn_rgf.h
Generated by
1.18.0