5#ifndef LINE_API_PFQN_CLW_H
6#define LINE_API_PFQN_CLW_H
126Cx<T> cx_sub(
const Cx<T>& a,
const Cx<T>& b) {
127 return Cx<T>(T(a.re - b.re), T(a.im - b.im));
132Cx<T> cx_log(
const Cx<T>& a) {
136 const T mod = sqrt(T(a.re * a.re + a.im * a.im));
137 return Cx<T>(T(log(mod)), T(atan2(a.im, a.re)));
141Cx<T> cx_exp(
const Cx<T>& a) {
145 const T e = exp(a.re);
146 return Cx<T>(T(e * cos(a.im)), T(e * sin(a.im)));
155T clw_pow_real(
const T& x,
const T& y) {
162std::vector<T> clw_beta(
const std::vector<std::size_t>& keep,
const ClwOptions& opt) {
163 std::vector<T> beta(keep.size(), num_traits<T>::from_int(1));
164 if (opt.beta.empty())
return beta;
165 for (std::size_t j = 0; j < keep.size(); ++j) {
166 if (keep[j] >= opt.beta.size())
throw InputError(
"pfqn_clw: options.beta has the wrong length");
167 beta[j] = num_traits<T>::from_double(opt.beta[keep[j]]);
178 std::vector<std::size_t> D;
179 std::vector<std::vector<std::size_t>> comps;
183inline std::vector<std::vector<std::size_t>> clw_components(
184 const std::vector<std::vector<char>>& adj,
const std::vector<char>& mask) {
185 const std::size_t p = adj.size();
186 std::vector<long> lab(p, -1);
188 for (std::size_t s = 0; s < p; ++s) {
189 if (mask[s] || lab[s] >= 0)
continue;
191 std::vector<std::size_t> stack(1, s);
192 while (!stack.empty()) {
193 const std::size_t v = stack.back();
195 for (std::size_t u = 0; u < p; ++u)
196 if (adj[v][u] && !mask[u] && lab[u] < 0) {
203 std::vector<std::vector<std::size_t>> out(
static_cast<std::size_t
>(nc));
204 for (std::size_t j = 0; j < p; ++j)
205 if (lab[j] >= 0) out[
static_cast<std::size_t
>(lab[j])].push_back(j);
209inline double clw_binom(std::size_t n, std::size_t k) {
211 for (std::size_t i = 1; i <= k; ++i) v = v * static_cast<double>(n - k + i) /
static_cast<double>(i);
222ClwPlan clw_plan(
const Matrix<T>& L,
const ClwOptions& opt) {
223 const std::size_t qd = L.rows(), p = L.cols();
225 trivial.comps.resize(1);
226 for (std::size_t j = 0; j < p; ++j) trivial.comps[0].push_back(j);
227 if (!opt.dimred || p <= 2)
return trivial;
228 const T zero = num_traits<T>::from_int(0);
229 std::vector<std::vector<char>> adj(p, std::vector<char>(p, 0));
230 for (std::size_t i = 0; i < qd; ++i)
231 for (std::size_t a = 0; a < p; ++a) {
232 if (L(i, a) == zero)
continue;
233 for (std::size_t b = 0; b < p; ++b)
234 if (b != a && L(i, b) != zero) adj[a][b] = 1;
236 const std::vector<char> none(p, 0);
237 std::vector<std::vector<std::size_t>> bestC = clw_components(adj, none);
238 std::size_t best = 0;
239 for (std::size_t c = 0; c < bestC.size(); ++c) best = std::max(best, bestC[c].size());
240 std::vector<std::size_t> bestD;
241 const std::size_t maxd = std::min<std::size_t>(
242 (opt.dimredMaxD > 0) ?
static_cast<std::size_t
>(opt.dimredMaxD) : 0, p - 1);
243 for (std::size_t dd = 1; dd <= maxd; ++dd) {
244 if (dd >= best)
break;
245 if (clw_binom(p, dd) > 2e5)
break;
246 std::vector<std::size_t> sub(dd);
247 for (std::size_t t = 0; t < dd; ++t) sub[t] = t;
249 std::vector<char> mask(p, 0);
250 for (std::size_t t = 0; t < dd; ++t) mask[sub[t]] = 1;
251 const std::vector<std::vector<std::size_t>> cc = clw_components(adj, mask);
253 for (std::size_t c = 0; c < cc.size(); ++c) mx = std::max(mx, cc[c].size());
254 if (dd + mx < best) {
260 while (t-- > 0 && sub[t] == p - dd + t) {
264 for (std::size_t u = t + 1; u < dd; ++u) sub[u] = sub[u - 1] + 1;
267 if (best >= p)
return trivial;
281inline void clw_defaults(std::size_t pfull,
const std::vector<std::size_t>& keep,
282 const std::vector<std::size_t>& depth,
const ClwOptions& opt,
283 std::vector<int>& l, std::vector<double>& gam) {
284 const std::size_t p = keep.size();
287 for (std::size_t j = 0; j < p; ++j) {
291 }
else if (depth[j] <= 3) {
296 if (!opt.l.empty()) {
297 if (opt.l.size() != pfull)
throw InputError(
"pfqn_clw: options.l has the wrong length");
298 for (std::size_t j = 0; j < p; ++j) l[j] = opt.l[keep[j]];
300 if (!opt.gamma.empty()) {
301 if (opt.gamma.size() != pfull)
302 throw InputError(
"pfqn_clw: options.gamma has the wrong length");
303 for (std::size_t j = 0; j < p; ++j) gam[j] = opt.gamma[keep[j]];
305 for (std::size_t j = 0; j < p; ++j)
306 if (l[j] < 1)
throw InputError(
"pfqn_clw: the lattice parameters must be positive");
314inline void clw_defaults(std::size_t p,
const ClwOptions& opt, std::vector<int>& l,
315 std::vector<double>& gam) {
316 std::vector<std::size_t> keep(p, 0), depth(p, 0);
317 for (std::size_t j = 0; j < p; ++j) {
321 clw_defaults(p, keep, depth, opt, l, gam);
338std::vector<T> clw_scaling(
const Matrix<T>& Lsc,
const Matrix<T>& Lraw,
const std::vector<int>& N,
339 const std::vector<T>& Z,
const std::vector<int>& l,
340 const std::vector<T>& r,
const std::vector<long>& mult,
341 const std::vector<std::size_t>& order,
const std::vector<T>& beta) {
342 const std::size_t qd = Lsc.rows(), p = Lsc.cols();
343 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
344 const T eps = num_traits<T>::from_double(std::numeric_limits<double>::epsilon());
345 std::vector<T> alpha(p, one), used(qd, zero);
347 for (std::size_t t = 0; t < p; ++t) {
348 const std::size_t j = order[t];
349 const long Kj = N[j], lj = l[j];
350 if (Kj == 0)
continue;
351 std::vector<std::size_t> posq;
352 std::vector<T> e(qd, zero);
353 for (std::size_t i = 0; i < qd; ++i) {
354 T den = one - used[i];
355 if (!(den > zero)) den = eps;
356 e[i] = Lsc(i, j) / den;
357 if (Lsc(i, j) > zero) posq.push_back(i);
362 std::stable_sort(posq.begin(), posq.end(),
363 [&](std::size_t a, std::size_t b) { return e[b] < e[a]; });
366 for (std::size_t n = 0; n < posq.size(); ++n) {
367 const std::size_t qi = posq[n];
370 const T rhobar = cum / num_traits<T>::from_int(
static_cast<long>(n) + 1);
372 for (std::size_t u = t + 1; u < p; ++u)
373 if (Lraw(qi, order[u]) != zero) Nn += N[order[u]];
382 for (
long ll = 1; ll <= Nn; ++ll)
383 lp += log(T(num_traits<T>::from_int(Kj + ll) /
384 num_traits<T>::from_int(Kj + 2 * lj * Kj + ll)));
385 an = exp(T(lp / num_traits<T>::from_int(2 * lj * Kj)));
387 const T cand = an / rhobar;
388 if (!have || cand < aj) {
395 const T cand = num_traits<T>::from_int(Kj) / Z[j];
396 if (!have || cand < aj) {
402 alpha[j] = T(beta[j] * aj);
403 for (std::size_t i = 0; i < qd; ++i) used[i] += alpha[j] * Lsc(i, j) * r[j];
410std::vector<T> clw_scaling(
const Matrix<T>& Lsc,
const Matrix<T>& Lraw,
const std::vector<int>& N,
411 const std::vector<T>& Z,
const std::vector<int>& l,
412 const std::vector<T>& r,
const std::vector<long>& mult) {
413 std::vector<std::size_t> order(Lsc.cols(), 0);
414 for (std::size_t j = 0; j < order.size(); ++j) order[j] = j;
415 return clw_scaling(Lsc, Lraw, N, Z, l, r, mult, order,
416 std::vector<T>(order.size(), num_traits<T>::from_int(1)));
426std::vector<T> clw_euler_weights(
int n,
int mm) {
427 const T one = num_traits<T>::from_int(1);
428 std::vector<T> b(
static_cast<std::size_t
>(mm) + 1, one);
429 for (
int k = 1; k <= mm; ++k)
430 b[
static_cast<std::size_t
>(k)] =
431 T(b[
static_cast<std::size_t
>(k) - 1] * num_traits<T>::from_int(mm - k + 1) /
432 num_traits<T>::from_int(k));
433 const T scale = T(one /
num_pow_int(num_traits<T>::from_int(2),
static_cast<unsigned>(mm)));
434 for (
int k = 0; k <= mm; ++k) b[static_cast<std::size_t>(k)] *= scale;
435 std::vector<T> tail(
static_cast<std::size_t
>(mm) + 1, num_traits<T>::from_int(0));
436 T acc = num_traits<T>::from_int(0);
437 for (
int k = mm; k >= 0; --k) {
438 acc += b[
static_cast<std::size_t
>(k)];
439 tail[
static_cast<std::size_t
>(k)] = acc;
441 std::vector<T> w(
static_cast<std::size_t
>(n + mm) + 1, one);
442 for (
int i = n + 1; i <= n + mm; ++i)
443 w[
static_cast<std::size_t
>(i)] = tail[
static_cast<std::size_t
>(i - n)];
456template <
class T,
class EV>
457Cx<T> clw_inner(
long Kj,
const ClwOptions& opt,
const EV& ev) {
458 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
460 int mCur = opt.eulerM;
462 const long TT =
static_cast<long>(opt.eulerN) + mCur;
463 if (!opt.euler || Kj <= TT + 1) {
465 for (
long k = -Kj; k <= Kj - 1; ++k)
466 s = cx_add(s, cx_scale(ev(k), (k % 2 == 0) ? one : T(-one)));
469 const std::vector<T> w1 = clw_euler_weights<T>(opt.eulerN, mCur);
470 const std::vector<T> w2 = clw_euler_weights<T>(opt.eulerN + 1, mCur);
471 Cx<T> e1(zero, zero), e2(zero, zero);
472 for (
long s = 0; s <= TT + 1; ++s) {
473 const Cx<T> dv = cx_sub(ev(s), ev(-(s + 1)));
474 const T sgn = (s % 2 == 0) ? one : T(-one);
475 if (s <= TT) e1 = cx_add(e1, cx_scale(dv, T(sgn * w1[
static_cast<std::size_t
>(s)])));
476 e2 = cx_add(e2, cx_scale(dv, T(sgn * w2[
static_cast<std::size_t
>(s)])));
478 const Cx<T> df = cx_sub(e1, e2);
479 const T dn = sqrt(T(df.re * df.re + df.im * df.im));
480 const T en = sqrt(T(e2.re * e2.re + e2.im * e2.im));
481 if (dn <= num_traits<T>::from_double(opt.eulerTol) * en || mCur >= opt.eulerMaxM)
return e2;
489 const std::vector<int>* N;
490 const std::vector<int>* l;
491 const std::vector<T>* r;
493 const ClwOptions* opt;
497template <
class T,
class F>
498Cx<T> clw_invert_d(std::size_t t, std::vector<Cx<T>>& w,
const ClwCtx<T>& cx,
const F& gbar);
504template <
class T,
class NEXT>
505Cx<T> clw_lattice(std::size_t j, std::vector<Cx<T>>& w,
const ClwCtx<T>& cx,
const NEXT& next) {
506 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
507 const long Kj = (*cx.N)[j], lj = (*cx.l)[j];
508 const T rj = (*cx.r)[j];
512 w[j] = Cx<T>(zero, zero);
515 Cx<T> acc(zero, zero);
516 for (
long k1 = 0; k1 < lj; ++k1) {
518 cx_expi(T(-cx.pi * num_traits<T>::from_int(k1) / num_traits<T>::from_int(lj)));
519 const Cx<T> inner = clw_inner<T>(Kj, *cx.opt, [&](
long k) {
520 const T theta = T(cx.pi * num_traits<T>::from_int(k1 + lj * k) /
521 num_traits<T>::from_int(lj * Kj));
522 w[j] = cx_scale(cx_expi(theta), rj);
525 acc = cx_add(acc, cx_mul(ph, inner));
528 T(num_traits<T>::from_int(2 * lj * Kj) *
num_pow_int(rj,
static_cast<unsigned>(Kj)));
529 return cx_scale(acc, T(one / den));
533template <
class T,
class F>
534Cx<T> clw_invert_c(std::size_t c, std::size_t s, std::vector<Cx<T>>& w,
const ClwCtx<T>& cx,
536 const std::vector<std::size_t>& vars = cx.plan->comps[c];
537 if (s >= vars.size())
return gbar(w,
static_cast<long>(c));
538 Cx<T> val = clw_lattice(vars[s], w, cx, [&]() {
return clw_invert_c(c, s + 1, w, cx, gbar); });
539 if (cx.plan->D.empty() && s == 0) {
542 val.im = num_traits<T>::from_int(0);
548template <
class T,
class F>
549Cx<T> clw_invert_d(std::size_t t, std::vector<Cx<T>>& w,
const ClwCtx<T>& cx,
const F& gbar) {
550 if (t >= cx.plan->D.size()) {
553 Cx<T> val = gbar(w, -1);
554 for (std::size_t c = 0; c < cx.plan->comps.size(); ++c)
555 val = cx_mul(val, clw_invert_c(c, 0, w, cx, gbar));
559 clw_lattice(cx.plan->D[t], w, cx, [&]() { return clw_invert_d(t + 1, w, cx, gbar); });
560 if (t == 0) val.im = num_traits<T>::from_int(0);
569template <
class T,
class F>
570Cx<T> clw_invert(std::size_t j, std::vector<Cx<T>>& w,
const std::vector<int>& N,
571 const std::vector<int>& l,
const std::vector<T>& r, std::size_t p,
574 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
576 plan.comps.resize(1);
577 for (std::size_t u = j; u < p; ++u) plan.comps[0].push_back(u);
587 cx.pi = T(num_traits<T>::from_int(2) * acos(zero));
588 return clw_invert_d(0, w, cx, [&](
const std::vector<Cx<T>>& wv,
long bucket) {
589 return (bucket < 0) ? Cx<T>(one, zero) : gbar(wv);
611 "pfqn_clw requires transcendental arithmetic (contour integration of a "
612 "generating function)");
615 const std::size_t qd = L.
rows();
616 if (L.
cols() != N.size())
throw InputError(
"pfqn_clw: L and N disagree on the chain count");
617 if (Z.size() != N.size())
throw InputError(
"pfqn_clw: Z and N disagree on the chain count");
621 for (std::size_t j = 0; j < N.size(); ++j)
624 res.
lG = T(-std::numeric_limits<T>::infinity());
628 for (std::size_t j = 0; j < N.size(); ++j)
629 if (N[j] > 0) allzero =
false;
637 std::vector<std::size_t> keep;
638 for (std::size_t j = 0; j < N.size(); ++j)
639 if (N[j] > 0) keep.push_back(j);
640 const std::size_t p = keep.size();
642 std::vector<int> Nk(p, 0), l(p, 1);
643 std::vector<T> Zk(p, zero);
644 std::vector<double> gam(p, 0.0);
645 for (std::size_t j = 0; j < p; ++j) {
646 for (std::size_t i = 0; i < qd; ++i) Lk(i, j) = L(i, keep[j]);
653 const detail::ClwPlan plan = detail::clw_plan(Lk,
opt);
654 std::vector<std::size_t> order, depth(p, 0);
655 for (std::size_t t = 0; t < plan.D.size(); ++t) {
656 order.push_back(plan.D[t]);
657 depth[plan.D[t]] = t + 1;
659 for (std::size_t c = 0; c < plan.comps.size(); ++c)
660 for (std::size_t s = 0; s < plan.comps[c].size(); ++s) {
661 order.push_back(plan.comps[c][s]);
662 depth[plan.comps[c][s]] = plan.D.size() + s + 1;
664 detail::clw_defaults(N.size(), keep, depth,
opt, l, gam);
665 std::vector<long> mult(qd, 1);
667 if (m.size() != qd)
throw InputError(
"pfqn_clw: m must have one entry per queue");
668 for (std::size_t i = 0; i < qd; ++i) {
669 if (m[i] < 1)
throw InputError(
"pfqn_clw: the queue multiplicities must be positive");
675 std::vector<T> r(p, one);
676 for (std::size_t j = 0; j < p; ++j)
677 r[j] = detail::clw_pow_real(
682 const std::vector<T> alpha =
683 detail::clw_scaling(Lk, Lk, Nk, Zk, l, r, mult, order, detail::clw_beta<T>(keep,
opt));
685 std::vector<T> arho0(p, zero);
687 for (std::size_t j = 0; j < p; ++j) {
688 arho0[j] = alpha[j] * Zk[j];
689 for (std::size_t i = 0; i < qd; ++i) rhoS(i, j) = Lk(i, j) * alpha[j];
695 std::vector<char> inD(p, 0);
696 for (std::size_t t = 0; t < plan.D.size(); ++t) inD[plan.D[t]] = 1;
697 std::vector<long> compOf(p, 0);
698 for (std::size_t c = 0; c < plan.comps.size(); ++c)
699 for (std::size_t s = 0; s < plan.comps[c].size(); ++s)
700 compOf[plan.comps[c][s]] =
static_cast<long>(c);
701 std::vector<long> qBucket(qd, -1);
702 for (std::size_t i = 0; i < qd; ++i)
703 for (std::size_t j = 0; j < p; ++j)
704 if (Lk(i, j) != zero && !inD[j]) {
705 qBucket[i] = compOf[j];
714 const bool decomposed = !plan.D.empty() || plan.comps.size() > 1;
715 std::vector<T> off(plan.comps.size() + 1, zero);
717 for (std::size_t b = 0; b <= plan.comps.size(); ++b) {
718 const long tag =
static_cast<long>(b) - 1;
720 const std::vector<std::size_t>& ch =
721 (tag < 0) ? plan.D : plan.comps[
static_cast<std::size_t
>(tag)];
722 for (std::size_t t = 0; t < ch.size(); ++t) o += arho0[ch[t]] * T(r[ch[t]] - one);
723 for (std::size_t i = 0; i < qd; ++i) {
724 if (qBucket[i] != tag)
continue;
726 for (std::size_t j = 0; j < p; ++j) x += rhoS(i, j) * r[j];
735 const auto gbar = [&](
const std::vector<detail::Cx<T>>& w,
long bucket) {
736 const std::vector<std::size_t>& ch =
737 (bucket < 0) ? plan.D : plan.comps[
static_cast<std::size_t
>(bucket)];
738 detail::Cx<T> expo(zero, zero);
739 for (std::size_t t = 0; t < ch.size(); ++t) {
740 const std::size_t j = ch[t];
741 expo = detail::cx_add(
742 expo, detail::cx_scale(detail::Cx<T>(T(w[j].re - one), w[j].im), arho0[j]));
744 detail::Cx<T> logden(zero, zero);
745 for (std::size_t i = 0; i < qd; ++i) {
746 if (qBucket[i] != bucket)
continue;
747 detail::Cx<T> a(zero, zero);
748 for (std::size_t j = 0; j < p; ++j)
749 a = detail::cx_add(a, detail::cx_scale(w[j], rhoS(i, j)));
750 const detail::Cx<T> lg = detail::cx_log(detail::Cx<T>(T(one - a.re), T(-a.im)));
751 logden = detail::cx_add(
754 const T o = off[
static_cast<std::size_t
>(bucket + 1)];
755 return detail::cx_exp(detail::Cx<T>(T(expo.re - logden.re - o), T(expo.im - logden.im)));
758 detail::ClwCtx<T> cx;
768 std::vector<detail::Cx<T>> w(p, detail::Cx<T>(zero, zero));
769 const detail::Cx<T> gv = detail::clw_invert_d(0, w, cx, gbar);
771 throw NumericError(
"pfqn_clw: the inverted generating function is not positive");
774 for (std::size_t b = 0; b < off.size(); ++b) lG += off[b];
775 for (std::size_t j = 0; j < p; ++j)
791 const std::vector<long>& m) {
810 "pfqn_clw_lld requires transcendental arithmetic (contour integration of a "
811 "generating function)");
814 const std::size_t qd = L.
rows();
815 if (L.
cols() != N.size())
throw InputError(
"pfqn_clw_lld: L and N disagree on the chain count");
816 if (Z.size() != N.size())
throw InputError(
"pfqn_clw_lld: Z and N disagree on the chain count");
821 for (std::size_t j = 0; j < N.size(); ++j) {
824 res.
lG = T(-std::numeric_limits<T>::infinity());
836 Matrix<T> S(qd,
static_cast<std::size_t
>(Ntot), one);
838 if (mu.
rows() != qd)
throw InputError(
"pfqn_clw_lld: mu must have one row per queue");
839 for (std::size_t i = 0; i < qd; ++i)
840 for (
long k = 0; k < Ntot; ++k) {
841 const std::size_t src = (
static_cast<std::size_t
>(k) < mu.
cols())
842 ?
static_cast<std::size_t
>(k)
844 if (!(mu(i, src) > zero))
845 throw InputError(
"pfqn_clw_lld: the load-dependent rates must be positive");
846 S(i,
static_cast<std::size_t
>(k)) = mu(i, src);
850 std::vector<std::size_t> keep;
851 for (std::size_t j = 0; j < N.size(); ++j)
852 if (N[j] > 0) keep.push_back(j);
853 const std::size_t p = keep.size();
855 std::vector<int> Nk(p, 0), l(p, 1);
856 std::vector<T> Zk(p, zero);
857 std::vector<double> gam(p, 0.0);
858 std::vector<std::size_t> order(p, 0), depth(p, 0);
859 for (std::size_t j = 0; j < p; ++j) {
860 for (std::size_t i = 0; i < qd; ++i) Lk(i, j) = L(i, keep[j]);
872 detail::clw_defaults(N.size(), keep, depth, lopt, l, gam);
873 detail::ClwPlan lplan;
874 lplan.comps.assign(1, order);
877 std::vector<T> cpole(qd, one);
878 std::vector<std::vector<T>> numc(qd);
879 for (std::size_t i = 0; i < qd; ++i) {
880 cpole[i] = S(i,
static_cast<std::size_t
>(Ntot) - 1);
882 for (
long k = 0; k < Ntot; ++k)
883 if (S(i,
static_cast<std::size_t
>(k)) != cpole[i]) last = k;
884 const long li = (last < 0) ? 1 : last + 2;
885 std::vector<T> a(
static_cast<std::size_t
>(li), zero);
888 for (
long n = 1; n < li; ++n) {
889 cp *= S(i,
static_cast<std::size_t
>(n) - 1);
890 a[
static_cast<std::size_t
>(n)] = (cpole[i] - S(i,
static_cast<std::size_t
>(n) - 1)) / cp;
895 std::vector<T> r(p, one);
896 for (std::size_t j = 0; j < p; ++j)
897 r[j] = detail::clw_pow_real(
904 for (std::size_t i = 0; i < qd; ++i)
905 for (std::size_t j = 0; j < p; ++j) Lt(i, j) = Lk(i, j) / cpole[i];
906 const std::vector<long> mult(qd, 1);
907 const std::vector<T> alpha =
908 detail::clw_scaling(Lt, Lk, Nk, Zk, l, r, mult, order, detail::clw_beta<T>(keep, lopt));
910 std::vector<T> arho0(p, zero);
912 for (std::size_t j = 0; j < p; ++j) {
913 arho0[j] = alpha[j] * Zk[j];
914 for (std::size_t i = 0; i < qd; ++i) rhoS(i, j) = Lk(i, j) * alpha[j];
917 const auto gbar = [&](
const std::vector<detail::Cx<T>>& w,
long bucket) {
918 if (bucket < 0)
return detail::Cx<T>(one, zero);
919 detail::Cx<T> expo(zero, zero);
920 for (std::size_t j = 0; j < p; ++j)
921 expo = detail::cx_add(
922 expo, detail::cx_scale(detail::Cx<T>(T(w[j].re - one), w[j].im), arho0[j]));
923 detail::Cx<T> logF(zero, zero);
924 for (std::size_t i = 0; i < qd; ++i) {
925 detail::Cx<T> x(zero, zero);
926 for (std::size_t j = 0; j < p; ++j)
927 x = detail::cx_add(x, detail::cx_scale(w[j], rhoS(i, j)));
928 const std::vector<T>& a = numc[i];
929 detail::Cx<T> num(a.back(), zero);
930 for (std::size_t k = a.size() - 1; k-- > 0;)
931 num = detail::cx_add(detail::cx_mul(num, x), detail::Cx<T>(a[k], zero));
932 logF = detail::cx_add(logF, detail::cx_log(num));
933 logF = detail::cx_sub(
934 logF, detail::cx_log(detail::Cx<T>(T(cpole[i] - x.re), T(-x.im))));
936 return detail::cx_exp(detail::cx_add(expo, logF));
939 detail::ClwCtx<T> cx;
949 std::vector<detail::Cx<T>> w(p, detail::Cx<T>(zero, zero));
950 const detail::Cx<T> gv = detail::clw_invert_d(0, w, cx, gbar);
952 throw NumericError(
"pfqn_clw_lld: the inverted generating function is not positive");
955 for (std::size_t j = 0; j < p; ++j)
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
ClwResult< T > pfqn_clw(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, const std::vector< long > &m, const ClwOptions &opt)
Choudhury-Leung-Whitt normalization constant by numerical inversion of the generating function (JACM ...
ClwResult< T > pfqn_clw_lld(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, const Matrix< T > &mu, const ClwOptions &opt)
Limited load-dependent form (matlab pfqn_clw_lld.m).
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Number-type abstraction for the templated API port.
Shared scalar machinery for the integration / asymptotic members of the pfqn family (pfqn_le,...
Optional lattice and aliasing parameters; empty means "use the CLW defaults".
bool dimred
dimension reduction by decomposition (Section 3)
std::vector< int > l
inner lattice parameters l_j (roundoff control)
bool euler
Euler-sum the inner sums where K_j > eulerN + eulerM.
int eulerN
terms summed exactly before averaging (n in eq. 2.22)
std::vector< double > gamma
aliasing parameters, aliasing ~ 10^-gamma_j
std::vector< double > beta
multipliers on alpha_j, the manual tuning of page 956
int eulerM
starting order of the averaging (m in eq. 2.22)
double eulerTol
relative tolerance on |E(m,n) - E(m,n+1)|
int eulerMaxM
largest Euler order reached by doubling
int dimredMaxD
largest |D| examined when minimizing (3.3)
Return value of pfqn_clw and pfqn_clw_lld, mirroring [G, lG].
T G
normalization constant, +infinity when it overflows the range of T
T lG
its natural logarithm, always finite