5#ifndef LINE_API_PFQN_PFQN_RGFMC_H
6#define LINE_API_PFQN_PFQN_RGFMC_H
86 std::vector<std::vector<T> > F;
92void rgfmc_slogsum(
const std::vector<T>& lv,
const std::vector<int>& sv, std::size_t n,
93 T& ls,
int& sg, T& cond) {
99 for (std::size_t i = 0; i < n; ++i) {
100 if (sv[i] != 0 && lv[i] > ninf && (!any || lv[i] > mx)) {
110 T tot = num_traits<T>::from_int(0);
111 T abs = num_traits<T>::from_int(0);
113 for (std::size_t i = 0; i < n; ++i) {
114 if (sv[i] == 0 || !(lv[i] > ninf))
continue;
115 const T e = exp(T(lv[i] - mx));
116 tot = T(tot + num_traits<T>::from_int(sv[i]) * e);
120 if (tot == num_traits<T>::from_int(0)) {
125 const T at = (tot < num_traits<T>::from_int(0)) ? T(-tot) : tot;
126 ls = T(mx + log(at));
127 sg = (tot > num_traits<T>::from_int(0)) ? 1 : -1;
129 const T c = T(mx + log(abs) - ls);
130 if (c > cond) cond = c;
136void rgfmc_slogconv(std::vector<T>& lu, std::vector<int>& su,
const std::vector<T>& lv,
137 const std::vector<int>& sv, T& cond) {
138 const std::size_t n = lu.size();
139 std::vector<T> lo(n), tl(n);
140 std::vector<int> so(n), ts(n);
141 for (std::size_t k = 0; k < n; ++k) {
142 for (std::size_t j = 0; j <= k; ++j) {
143 tl[j] = T(lu[j] + lv[k - j]);
144 ts[j] = su[j] * sv[k - j];
146 rgfmc_slogsum(tl, ts, k + 1, lo[k], so[k], cond);
154T rgfmc_lbinom(
const T& n,
const T& r) {
155 return T(num_factln<T>(n) - num_factln<T>(r) - num_factln<T>(T(n - r)));
159inline std::vector<std::vector<int> > rgfmc_compositions(
int total,
int parts) {
160 std::vector<std::vector<int> > out;
162 if (total == 0) out.push_back(std::vector<int>());
166 out.push_back(std::vector<int>(1, total));
169 for (
int first = 0; first <= total; ++first) {
170 std::vector<std::vector<int> > sub = rgfmc_compositions(total - first, parts - 1);
171 for (std::size_t i = 0; i < sub.
size(); ++i) {
172 std::vector<int> row(1, first);
173 row.insert(row.end(), sub[i].begin(), sub[i].end());
180inline int rgfmc_signpow(
bool negative,
int k) {
return (negative && (k % 2) == 1) ? -1 : 1; }
184void rgfmc_merge(std::vector<std::vector<T> >& F, std::vector<int>& m,
const T& tol,
187 const T zero = num_traits<T>::from_int(0);
188 const std::size_t Tn = F.size();
189 std::vector<bool> used(Tn,
false);
190 std::vector<std::vector<T> > oF;
192 for (std::size_t i = 0; i < Tn; ++i) {
193 if (used[i])
continue;
195 std::vector<T> Fi = F[i];
198 for (std::size_t c = 1; c < Fi.size(); ++c) {
201 if (Fi[pi] == zero)
throw InputError(
"pfqn_rgfmc: met an identically zero factor");
202 for (std::size_t j = i + 1; j < Tn; ++j) {
203 if (used[j])
continue;
205 for (std::size_t c = 0; c < F[j].size(); ++c) nj = std::max(nj,
num_abs(F[j][c]));
206 if (!(nj > zero))
continue;
207 const T r = T(F[j][pi] / Fi[pi]);
208 if (r == zero)
continue;
210 for (std::size_t c = 0; c < F[j].size(); ++c) {
211 if (
num_abs(T(F[j][c] - r * Fi[c])) > tol * nj) {
217 lc = T(lc - num_traits<T>::from_int(m[j]) * log(
num_abs(r)));
218 sc *= rgfmc_signpow(r < zero, m[j]);
232std::vector<RgfmcTerm<T> > rgfmc_step(
const std::vector<RgfmcTerm<T> >& terms, std::size_t col,
233 int kr,
const T& Zr,
const T& tol, std::size_t maxterms,
236 const T zero = num_traits<T>::from_int(0);
237 std::vector<RgfmcTerm<T> > out;
238 for (std::size_t it = 0; it < terms.size(); ++it) {
239 std::vector<std::vector<T> > F = terms[it].F;
240 std::vector<int> m = terms[it].m;
242 int sc = terms[it].sc;
243 rgfmc_merge(F, m, tol, lc, sc);
244 const std::size_t Tn = F.size();
245 std::vector<std::vector<T> > A(Tn, std::vector<T>(col, zero));
246 std::vector<T> B(Tn, zero);
247 std::vector<bool> isMono(Tn,
false);
249 for (std::size_t i = 0; i < Tn; ++i) {
251 for (std::size_t c = 0; c < F[i].size(); ++c) scale = std::max(scale,
num_abs(F[i][c]));
252 if (scale == zero) scale = num_traits<T>::from_int(1);
254 for (std::size_t c = 0; c < col; ++c) {
256 if (
num_abs(F[i][c]) > tol * scale) mono =
false;
258 B[i] = T(-F[i][col]);
261 lc = T(lc - num_traits<T>::from_int(m[i]) * log(
num_abs(B[i])));
262 sc *= rgfmc_signpow(T(-B[i]) < zero, m[i]);
266 std::vector<std::size_t> S, P;
267 for (std::size_t i = 0; i < Tn; ++i) {
268 if (isMono[i])
continue;
269 if (B[i] != zero) S.push_back(i);
272 const int Ntot = kr + shift;
273 const int pmax = (Zr > zero) ? Ntot : 0;
274 for (
int p = 0; p <= pmax; ++p) {
278 const T pT = num_traits<T>::from_int(p);
279 lcz = T(lc + pT * log(Zr) - num_factln<T>(pT));
281 const int n = Ntot - p;
287 for (std::size_t a = 0; a < P.size(); ++a) {
288 nt.F.push_back(A[P[a]]);
289 nt.m.push_back(m[P[a]]);
295 for (std::size_t jj = 0; jj < S.size(); ++jj) {
296 const std::size_t j = S[jj];
297 std::vector<std::size_t> oth;
298 for (std::size_t a = 0; a < S.size(); ++a)
299 if (S[a] != j) oth.push_back(S[a]);
300 const std::size_t no = oth.size();
302 std::vector<std::vector<T> > Cjl(no, std::vector<T>(col, zero));
303 for (std::size_t a = 0; a < no; ++a)
304 for (std::size_t c = 0; c < col; ++c)
305 Cjl[a][c] = T((A[oth[a]][c] * Bj - B[oth[a]] * A[j][c]) / Bj);
306 for (
int k = 0; k < m[j]; ++k) {
307 const T nT = num_traits<T>::from_int(n);
308 const T lbase = T(lcz - num_traits<T>::from_int(k) * log(
num_abs(Bj)) +
309 rgfmc_lbinom<T>(T(nT + num_traits<T>::from_int(m[j] - k - 1)), nT) +
311 const int sbase = sc * rgfmc_signpow(T(-Bj) < zero, k) * rgfmc_signpow(Bj < zero, n);
312 std::vector<std::vector<int> > comps = rgfmc_compositions(k,
static_cast<int>(no));
313 for (std::size_t cc = 0; cc < comps.size(); ++cc) {
316 for (std::size_t a = 0; a < no; ++a) {
317 if (comps[cc][a] > 0) {
318 const T jlT = num_traits<T>::from_int(comps[cc][a]);
319 lt = T(lt + rgfmc_lbinom<T>(T(num_traits<T>::from_int(m[oth[a]]) + jlT -
320 num_traits<T>::from_int(1)), jlT) +
321 jlT * log(
num_abs(B[oth[a]])));
322 st *= rgfmc_signpow(B[oth[a]] < zero, comps[cc][a]);
328 nt.F.push_back(A[j]);
329 nt.m.push_back(n + m[j] - k);
330 for (std::size_t a = 0; a < no; ++a) {
331 nt.F.push_back(Cjl[a]);
332 nt.m.push_back(m[oth[a]] + comps[cc][a]);
334 for (std::size_t a = 0; a < P.size(); ++a) {
335 nt.F.push_back(A[P[a]]);
336 nt.m.push_back(m[P[a]]);
343 if (out.size() > maxterms)
344 throw InputError(
"pfqn_rgfmc: exceeded maxterms; the residue term count grows as "
345 "C(S+M-1,M-1) per further elimination, use method 'ca'");
352void rgfmc_base_kernel(
const std::vector<T>& loads,
const std::vector<int>& mults,
int N,
353 const T& Z, T& lg,
int& sg, T& cond) {
355 const T zero = num_traits<T>::from_int(0);
356 const T ninf = num_traits<T>::from_double(-std::numeric_limits<double>::infinity());
357 const std::size_t len =
static_cast<std::size_t
>(N) + 1;
358 std::vector<T> lv(len, ninf), lr(len, zero);
359 std::vector<int> sv(len, 0), sr(len, 1);
363 for (std::size_t k = 0; k < len; ++k) {
364 const T kT = num_traits<T>::from_int(
static_cast<long>(k));
365 lr[k] = T(kT * log(Z) - num_factln<T>(kT));
368 rgfmc_slogconv(lv, sv, lr, sr, cond);
370 for (std::size_t a = 0; a < loads.size(); ++a) {
371 if (loads[a] == zero)
continue;
372 const T ap =
num_abs(loads[a]);
373 const int mm = mults[a];
374 for (std::size_t k = 0; k < len; ++k) {
375 const T kT = num_traits<T>::from_int(
static_cast<long>(k));
377 lr[k] = T(kT * log(ap));
379 const T mT = num_traits<T>::from_int(mm);
380 lr[k] = T(num_lgamma<T>(T(kT + mT)) - num_factln<T>(kT) - num_lgamma<T>(mT) +
383 sr[k] = (loads[a] > zero || (k % 2) == 0) ? 1 : -1;
385 rgfmc_slogconv(lv, sv, lr, sr, cond);
387 lg = lv[
static_cast<std::size_t
>(N)];
388 sg = sv[
static_cast<std::size_t
>(N)];
393void rgfmc_base(
const std::vector<RgfmcTerm<T> >& terms,
int k1,
const T& Z1,
const T& tol,
394 T& lg,
int& sg, T& cond) {
396 const T zero = num_traits<T>::from_int(0);
397 std::map<std::string, std::pair<T, int> > cache;
400 for (std::size_t it = 0; it < terms.size(); ++it) {
401 std::vector<std::vector<T> > F = terms[it].F;
402 std::vector<int> m = terms[it].m;
404 int sc = terms[it].sc;
405 rgfmc_merge(F, m, tol, lc, sc);
407 std::vector<T> loads;
408 std::vector<int> mults;
409 for (std::size_t a = 0; a < F.size(); ++a) {
410 const T a0 = F[a][0];
411 const T a1 = F[a][1];
414 if (sca == zero)
throw InputError(
"pfqn_rgfmc: met an identically zero factor");
415 if (
num_abs(a0) <= tol * sca) {
416 lc = T(lc - num_traits<T>::from_int(ma) * log(
num_abs(a1)));
417 sc *= rgfmc_signpow(a1 < zero, ma);
420 lc = T(lc - num_traits<T>::from_int(ma) * log(
num_abs(a0)));
421 sc *= rgfmc_signpow(a0 < zero, ma);
423 loads.push_back(T(-a1 / a0));
428 const int Ntot = k1 + shift;
429 std::ostringstream key;
430 key << Ntot <<
'|' << num_traits<T>::to_double(Z1);
431 for (std::size_t a = 0; a < loads.size(); ++a)
432 key <<
'|' << num_traits<T>::to_double(loads[a]) <<
':' << mults[a];
433 typename std::map<std::string, std::pair<T, int> >::iterator hit = cache.find(key.str());
436 if (hit == cache.end()) {
437 rgfmc_base_kernel(loads, mults, Ntot, Z1, klg, ksg, cond);
438 cache[key.str()] = std::make_pair(klg, ksg);
440 klg = hit->second.first;
441 ksg = hit->second.second;
444 lv.push_back(T(lc + klg));
445 sv.push_back(sc * ksg);
448 rgfmc_slogsum(lv, sv, lv.size(), lg, sg, cond);
466 const std::vector<T>& Z,
const T& tol, std::size_t maxterms,
467 const T& maxcancel) {
469 "pfqn_rgfmc requires transcendental arithmetic: the residue coefficients are "
470 "carried in the log domain so that no Poisson weight or binomial is ever formed "
471 "as a naive ratio. Use pfqn_ca for the same constant in exact arithmetic");
475 const std::size_t Rall = L.
cols();
476 if (N.size() != Rall || Z.size() != Rall)
477 throw InputError(
"pfqn_rgfmc: requires N and Z to match the number of columns of L");
478 std::vector<std::size_t> kc;
479 for (std::size_t r = 0; r < Rall; ++r) {
480 if (N[r] < 0 || Z[r] < zero)
throw InputError(
"pfqn_rgfmc: requires nonnegative N and Z");
481 if (N[r] > 0) kc.push_back(r);
483 const std::size_t R = kc.size();
490 std::vector<std::size_t> kr;
491 for (std::size_t i = 0; i < L.
rows(); ++i) {
493 for (std::size_t c = 0; c < R; ++c) {
494 const T d = L(i, kc[c]);
495 if (d < zero)
throw InputError(
"pfqn_rgfmc: requires nonnegative demands");
496 if (d > zero) any =
true;
498 if (any) kr.push_back(i);
500 const std::size_t M = kr.size();
503 for (std::size_t c = 0; c < R; ++c) {
505 lG = T(lG + nT * log(Z[kc[c]]) - detail::num_factln<T>(nT));
512 std::vector<T> col(M);
513 for (std::size_t i = 0; i < M; ++i)
514 col[i] = L(kr[i], kc[0]);
522 std::vector<std::size_t> ord(R);
523 for (std::size_t c = 0; c < R; ++c) ord[c] = c;
524 std::stable_sort(ord.begin(), ord.end(),
525 [&](std::size_t a, std::size_t b) { return N[kc[a]] < N[kc[b]]; });
526 std::vector<std::vector<T> >
Ls(M, std::vector<T>(R, zero));
527 std::vector<int> Ns(R);
528 std::vector<T> Zs(R);
529 for (std::size_t c = 0; c < R; ++c) {
530 Ns[c] = N[kc[ord[c]]];
531 Zs[c] = Z[kc[ord[c]]];
532 for (std::size_t i = 0; i < M; ++i)
533 Ls[i][c] = L(kr[i], kc[ord[c]]);
536 for (std::size_t c = 0; c < R; ++c) {
538 for (std::size_t i = 0; i < M; ++i) cs = std::max(cs,
Ls[i][c]);
540 for (std::size_t i = 0; i < M; ++i)
Ls[i][c] = T(
Ls[i][c] / cs);
541 Zs[c] = T(Zs[c] / cs);
544 detail::RgfmcTerm<T> t0;
547 for (std::size_t i = 0; i < M; ++i) {
548 std::vector<T> row(R + 1, zero);
550 for (std::size_t c = 0; c < R; ++c) row[c + 1] = T(-
Ls[i][c]);
554 std::vector<detail::RgfmcTerm<T> > terms(1, t0);
557 for (std::size_t col = R; col >= 2; --col) {
558 terms = detail::rgfmc_step(terms, col, Ns[col - 1], Zs[col - 1], tol, maxterms, cond);
567 detail::rgfmc_base(terms, Ns[0], Zs[0], tol, lg, sg, cond);
573 if (sg < 0 || cond > maxcancel)
574 throw InputError(
"pfqn_rgfmc: the residue sum cancelled past the tolerated nats, so lG "
575 "carries no significant digits. The eliminated classes have "
576 "near-coincident loads over the stations; use method 'ca'");
577 res.
lG = T(lg + lGscale);
585 const std::vector<T>& Z) {
The exception types the port throws.
Dense matrix and non-owning view.
RgfmcResult< T > pfqn_rgfmc(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, const T &tol, std::size_t maxterms, const T &maxcancel)
Multiclass Recursion by Generating Functions (RGF), with think times.
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...
Number-type abstraction for the templated API port.
Shared scalar machinery for the integration / asymptotic members of the pfqn family (pfqn_le,...
Recursion by Generating Functions (RGF) for the normalizing constant of a SINGLE-CLASS closed product...
Return value of pfqn_rgf, mirroring [G, lG, lg].
T lG
its logarithm, i.e. lg[N]
Return value of pfqn_rgfmc, mirroring [G, lG].