5#ifndef LINE_API_PFQN_CA_H
6#define LINE_API_PFQN_CA_H
53T pff_delay(
const std::vector<T>& Z,
const std::vector<int>& n) {
55 for (
int v : n) total += v;
58 for (std::size_t r = 0; r < n.size(); ++r) {
59 if (n[r] == 0)
continue;
61 f *=
num_pow_int(Z[r],
static_cast<unsigned>(n[r])) /
73int scale_exponent(
const Matrix<T>& L,
const std::vector<int>& N,
const std::vector<T>& Zsum) {
74 if (!std::is_same<T, double>::value)
return 0;
75 const std::size_t M = L.
rows(), R = L.
cols();
77 for (
int v : N) Nt += v;
89 for (std::size_t r = 0; r < R; ++r) {
90 if (N[r] <= 0)
continue;
91 double best = -std::numeric_limits<double>::infinity();
92 for (std::size_t i = 0; i < M; ++i) {
94 if (lir > 0) best = std::max(best, N[r] * std::log(lir));
96 double zr = num_traits<T>::to_double(Zsum[r]);
97 if (zr > 0) best = std::max(best, N[r] * std::log(zr) - std::lgamma(N[r] + 1.0));
98 if (!std::isfinite(best)) {
100 lGest = -std::numeric_limits<double>::infinity();
105 if (!std::isfinite(lGest) || Nt == 0)
return 0;
106 return static_cast<int>(std::lround(lGest / (
static_cast<double>(Nt) * std::log(2.0))));
121 const std::size_t M = L.
rows();
122 const std::size_t R = N.size();
123 if (!L.
empty() && L.
cols() != R)
throw InputError(
"pfqn_ca: L and N disagree on the class count");
128 if (Z.
cols() != R)
throw InputError(
"pfqn_ca: Z and N disagree on the class count");
129 for (std::size_t k = 0; k < Z.
rows(); ++k)
130 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
134 bool negative =
false;
136 if (v < 0) negative =
true;
143 std::vector<int> n(N);
144 T G = detail::pff_delay(Zsum, n);
149 const int kscale = detail::scale_exponent(L, N, Zsum);
151 std::vector<T> Zs = Zsum;
152 if constexpr (std::is_same<T, double>::value) {
155 for (std::size_t i = 0; i < M; ++i)
156 for (std::size_t r = 0; r < R; ++r)
Ls(i, r) = std::ldexp(
Ls(i, r), -kscale);
157 for (std::size_t r = 0; r < R; ++r) Zs[r] = std::ldexp(Zs[r], -kscale);
161 const std::vector<std::size_t> prods =
plane_sizes(N);
166 std::vector<int> n(R, 0);
169 const std::size_t idxn =
pop_index(n, prods);
170 G[idxn] = detail::pff_delay(Zs, n);
171 for (std::size_t m = 1; m <= M; ++m) {
172 T acc = G[(m - 1) * total + idxn];
173 for (std::size_t r = 0; r < R; ++r)
174 if (n[r] >= 1) acc +=
Ls(m - 1, r) * G[m * total + (idxn - prods[r])];
175 G[m * total + idxn] = acc;
180 const T raw = G[M * total + (total - 1)];
184 if constexpr (std::is_same<T, double>::value) {
186 if (kscale != 0) Gn = std::ldexp(raw,
static_cast<int>(
static_cast<long>(Nt) * kscale));
The exception types the port throws.
Dense matrix and non-owning view.
NcResult< T > pfqn_ca(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z)
Convolution algorithm for the exact normalizing constant of a closed product-form network (Buzen 1973...
T num_factorial(unsigned n)
Factorial as a value of T.
std::size_t population_count(const std::vector< int > &N)
Number of population vectors n with 0 <= n <= N.
std::vector< std::size_t > plane_sizes(const std::vector< int > &N)
Mixed-radix plane sizes: prods[r] = prod_{s<r} (N[s]+1).
bool next_pop(std::vector< int > &n, const std::vector< int > &N)
Advance n to the next population vector in the lattice 0 <= n <= N, odometer order with the last clas...
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
std::size_t pop_index(const std::vector< int > &n, const std::vector< std::size_t > &prods)
Index of n in the lattice, 0-based (MATLAB hashpop is 1-based).
Number-type abstraction for the templated API port.
Population-vector enumeration and combinatorics.
Return value of the normalizing-constant family, mirroring Ret.pfqnNc.
T G
normalizing constant in the requested arithmetic
double lG
log of the constant, always a double and always finite