LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
pfqn_grnmol.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_GRNMOL_H
6
#define LINE_API_PFQN_PFQN_GRNMOL_H
7
8
/**
9
* @file
10
* @ingroup api_pfqn
11
* Normalizing constant by the closed-form Grundmann-Moeller rule.
12
*
13
* Templated port of matlab/src/api/pfqn/pfqn_grnmol.m, which applies the rule
14
* directly rather than through the successive-degree recursion of pfqn_cub:
15
*
16
* G = [ (sum N + M - 1)! / prod_r N_r! ] * sum_{i=0}^{S} w_i H_i
17
* w_i = 2^{-2S} (-1)^i c_i^{2S+1} / (i! (i + c_i)!), c_i = 2(S-i) + M
18
* H_i = sum_{|b| = S-i} prod_r ( ((2b+1)/c_i)' L(:,r) )^{N_r}
19
*
20
* with b ranging over the M-vectors of non-negative integers summing to S-i,
21
* which is what the reference's call to matlab/util/sprod.m enumerates. The
22
* point (2b+1)/c_i is barycentric by construction, since sum_m (2b_m+1) = c_i.
23
*
24
* REFERENCE DEFECT, reproduced as a rejection rather than as a wrong number.
25
* pfqn_grnmol.m sets S = ceil(sum(N)-1)/2, which MATLAB parses as
26
* (ceil(sum(N)-1))/2 and NOT as ceil((sum(N)-1)/2), the value pfqn_cub.m uses.
27
* For an EVEN total population S is therefore a half-integer, c_i is a
28
* half-integer, and the weight calls factorial(i + c_i) on a non-integer,
29
* which MATLAB rejects outright ("N must be a matrix of non-negative
30
* integers"). pfqn_grnmol is thus callable only for an ODD total population,
31
* and the port throws InputError for an even one instead of silently choosing
32
* one of the two readings of the expression. Use pfqn_cub for even
33
* populations; it computes the same integral with the correct degree.
34
*
35
* ARITHMETIC. Every ingredient -- the barycentric points, the binomial-like
36
* weights, the integer powers -- is rational, and the factorial prefactor is
37
* formed as an exact factorial rather than through gammaln. The routine is
38
* therefore EXACT in rational arithmetic and is deliberately left ungated:
39
* at the reference degree it returns the exact normalizing constant, and
40
* checking that against pfqn_ca is the sharpest test the rule admits.
41
*/
42
43
#include <cstddef>
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
/**
56
* Advance b to the next M-vector of non-negative integers with a fixed sum,
57
* in the order matlab/util/sprod.m produces through multichoose. Returns
58
* false once the enumeration is exhausted.
59
*/
60
inline
bool
next_composition(std::vector<long>& b,
long
total) {
61
const
std::size_t M = b.size();
62
if
(M < 2)
return
false
;
63
// Find the rightmost position that can be decremented with something to
64
// its right to absorb the unit.
65
for
(std::size_t i = M - 1; i-- > 0;) {
66
if
(b[i] > 0) {
67
b[i] -= 1;
68
long
rest = total;
69
for
(std::size_t k = 0; k <= i; ++k) rest -= b[k];
70
for
(std::size_t k = i + 1; k < M; ++k) b[k] = 0;
71
b[i + 1] = rest;
72
return
true
;
73
}
74
}
75
return
false
;
76
}
77
78
}
// namespace detail
79
80
/**
81
* @brief Normalizing constant by the closed-form Grundmann-Moeller rule.
82
*
83
* @param L (M x R) demands, @param N (R) population with an ODD total
84
* @return the normalizing constant
85
*/
86
template
<
class
T>
87
T
pfqn_grnmol
(
const
Matrix<T>
& L,
const
std::vector<int>& N) {
88
const
std::size_t M = L.
rows
(), R = L.
cols
();
89
if
(N.size() != R)
throw
InputError
(
"pfqn_grnmol: L and N disagree on the class count"
);
90
if
(M == 0)
throw
InputError
(
"pfqn_grnmol: empty demand matrix"
);
91
long
Nt = 0;
92
for
(
int
v : N) {
93
if
(v < 0)
throw
InputError
(
"pfqn_grnmol: negative population"
);
94
Nt += v;
95
}
96
if
(Nt == 0)
return
num_traits<T>::from_int
(1);
97
if
(Nt % 2 == 0)
98
throw
InputError
(
99
"pfqn_grnmol: the reference is only callable for an odd total population "
100
"(S = ceil(sum(N)-1)/2 is a half-integer otherwise); use pfqn_cub"
);
101
const
long
S = (Nt - 1) / 2;
102
const
T zero =
num_traits<T>::from_int
(0), one =
num_traits<T>::from_int
(1);
103
104
T G = zero;
105
for
(
long
i = 0; i <= S; ++i) {
106
const
long
ci = 2 * (S - i) +
static_cast<
long
>
(M);
107
T w = T(one /
num_pow_int
(
num_traits<T>::from_int
(2),
static_cast<
unsigned
>
(2 * S)));
108
if
(i % 2 == 1) w = T(-w);
109
w *=
num_pow_int
(
num_traits<T>::from_int
(ci),
static_cast<
unsigned
>
(2 * S + 1));
110
w /=
num_factorial<T>
(
static_cast<
unsigned
>
(i));
111
w /=
num_factorial<T>
(
static_cast<
unsigned
>
(i + ci));
112
113
const
long
tot = S - i;
114
std::vector<long> b(M, 0);
115
b[0] = tot;
116
T H = zero;
117
while
(
true
) {
118
T prod = one;
119
for
(std::size_t r = 0; r < R; ++r) {
120
if
(N[r] == 0)
continue
;
121
T uL = zero;
122
for
(std::size_t m = 0; m < M; ++m)
123
uL += T(
num_traits<T>::from_int
(2 * b[m] + 1) /
num_traits<T>::from_int
(ci)) *
124
L(m, r);
125
prod *=
num_pow_int
(uL,
static_cast<
unsigned
>
(N[r]));
126
}
127
H += prod;
128
if
(!detail::next_composition(b, tot))
break
;
129
}
130
G += w * H;
131
}
132
133
G *=
num_factorial<T>
(
static_cast<
unsigned
>
(Nt +
static_cast<
long
>
(M) - 1));
134
for
(std::size_t r = 0; r < R; ++r) G /=
num_factorial<T>
(
static_cast<
unsigned
>
(N[r]));
135
return
G;
136
}
137
138
}
// namespace pfqn
139
}
// namespace line
140
141
#endif
// LINE_API_PFQN_PFQN_GRNMOL_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
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_grnmol
T pfqn_grnmol(const Matrix< T > &L, const std::vector< int > &N)
Normalizing constant by the closed-form Grundmann-Moeller rule.
Definition
pfqn_grnmol.h:87
line
Definition
aoi_dist2ph.h:52
line::num_factorial
T num_factorial(unsigned n)
Factorial as a value of T.
Definition
number.h:184
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
include
line
api
pfqn
pfqn_grnmol.h
Generated by
1.18.0