5#ifndef LINE_API_PFQN_NC_H
6#define LINE_API_PFQN_NC_H
235 "' is an asymptotic or Monte Carlo estimator formed in logarithms and "
236 "needs transcendental arithmetic. "
237 "Use 'ca', 'exact', 'recal', 'mva' or 'comom' for an exact constant.");
279 const std::size_t R = N.size();
293 res.
lG = -std::numeric_limits<double>::infinity();
297 throw InputError(
"pfqn_nc: L and N disagree on the class count");
299 const std::size_t M0 = L.
empty() ? 0 : L.
rows();
300 std::vector<T> lam(R, zero);
301 for (std::size_t r = 0; r < R && r < lambda.size(); ++r) lam[r] = lambda[r];
306 std::vector<std::size_t> ocl;
307 for (std::size_t r = 0; r < R; ++r)
308 if (N[r] < 0) ocl.push_back(r);
309 for (std::size_t i = 0; i < M0; ++i) {
311 for (std::size_t r = 0; r < R; ++r) u -= lam[r] * L(i, r);
319 for (std::size_t r = 0; r < R; ++r) Lw(i, r) = zero;
322 for (std::size_t r = 0; r < R; ++r) {
323 Lw(i, r) = L(i, r) / u;
324 Qopen(i, r) = lam[r] * Lw(i, r) / u;
329 std::vector<int> Nc(R, 0);
330 for (std::size_t r = 0; r < R; ++r) Nc[r] = N[r] > 0 ? N[r] : 0;
332 for (
int v : Nc) Ntot += v;
333 if (Ntot == 0)
return res;
335 const auto colsum = [&](
const Matrix<T>& A, std::size_t r) {
337 for (std::size_t i = 0; i < A.
rows(); ++i) s += A(i, r);
342 std::vector<std::size_t> nnz;
343 for (std::size_t r = 0; r < R; ++r)
344 if (Nc[r] > 0) nnz.push_back(r);
345 const std::size_t R1 = nnz.size();
348 std::vector<T> scalevec(R1, one);
350 for (std::size_t k = 0; k < R1; ++k) {
351 const std::size_t r = nnz[k];
353 for (std::size_t i = 0; i < M0; ++i)
354 if (Lw(i, r) > mx) mx = Lw(i, r);
355 for (std::size_t i = 0; i < Z1.
rows(); ++i)
356 if (Z(i, r) > mx) mx = Z(i, r);
357 if (mx > zero) scalevec[k] = mx;
358 for (std::size_t i = 0; i < M0; ++i) L1(i, k) = Lw(i, r) / scalevec[k];
359 for (std::size_t i = 0; i < Z1.
rows(); ++i) Z1(i, k) = Z(i, r) / scalevec[k];
363 for (std::size_t k = 0; k < R1; ++k)
364 Gscale *=
num_pow_int(scalevec[k],
static_cast<unsigned>(Nc[nnz[k]]));
370 double lGscale = 0.0;
372 for (std::size_t k = 0; k < R1; ++k)
373 lGscale +=
static_cast<double>(Nc[nnz[k]]) *
378 std::vector<std::size_t> demSt;
379 for (std::size_t i = 0; i < M0; ++i) {
381 for (std::size_t k = 0; k < R1; ++k) rs += L1(i, k);
382 if (rs > atol) demSt.push_back(i);
385 for (std::size_t a = 0; a < demSt.size(); ++a)
386 for (std::size_t k = 0; k < R1; ++k) L2(a, k) = L1(demSt[a], k);
387 const std::size_t M = demSt.size();
389 std::vector<int> N2(R1, 0);
390 for (std::size_t k = 0; k < R1; ++k) N2[k] = Nc[nnz[k]];
395 for (std::size_t k = 0; k < R1; ++k) {
397 for (std::size_t i = 0; i < Z1.
rows(); ++i) s += Z1(i, k);
398 if (Zag.
rows() > 0) Zag(0, k) = s;
403 const auto delayG = [&](
const std::vector<std::size_t>& cls) {
405 for (std::size_t k : cls) {
407 for (std::size_t i = 0; i < Z1.
rows(); ++i) zs += Z1(i, k);
408 g *=
num_pow_int(zs,
static_cast<unsigned>(N2[k])) /
419 bool have_measures =
false;
420 const auto attach_open = [&]() {
421 if (ocl.empty() || !have_measures)
return;
423 for (std::size_t i = 0; i < M0; ++i)
424 for (std::size_t r : ocl) res.
Q(i, r) = Qopen(i, r);
425 res.
X.assign(R, zero);
426 for (std::size_t r : ocl) res.
X[r] = lam[r];
428 const auto finish = [&](
const T& gcore) {
429 res.
G = Gscale * gcore;
436 const auto finish_log = [&](
double lgcore) {
437 res.
lG = lGscale + lgcore;
445 for (std::size_t i = 0; i < M; ++i)
446 for (std::size_t k = 0; k < R1; ++k) Lsum += L2(i, k);
448 if (M == 0 || !(Lsum > atol)) {
450 std::vector<std::size_t> all(R1);
451 for (std::size_t k = 0; k < R1; ++k) all[k] = k;
452 return finish(Ztot > atol ? delayG(all) : one);
454 if (M == 1 && !(Ztot > atol)) {
457 for (
int v : N2) tot += v;
459 for (std::size_t k = 0; k < R1; ++k)
460 g *=
num_pow_int(L2(0, k),
static_cast<unsigned>(N2[k])) /
464 if (!(Ztot > atol)) {
466 bool identical =
true;
467 for (std::size_t i = 1; i < M && identical; ++i)
468 for (std::size_t k = 0; k < R1; ++k)
469 if (L2(i, k) != L2(0, k)) {
475 for (
int v : N2) tot += v;
478 for (std::size_t k = 0; k < R1; ++k)
479 g *=
num_pow_int(L2(0, k),
static_cast<unsigned>(N2[k])) /
486 std::vector<std::size_t> zdem, nzdem;
487 for (std::size_t k = 0; k < R1; ++k) {
489 for (std::size_t i = 0; i < M; ++i) s += L2(i, k);
490 (s > atol ? nzdem : zdem).push_back(k);
492 const T Gzdem = zdem.empty() ? one : delayG(zdem);
496 std::vector<int> N3(nzdem.size(), 0);
497 for (std::size_t a = 0; a < nzdem.size(); ++a) {
498 for (std::size_t i = 0; i < M; ++i) L3(i, a) = L2(i, nzdem[a]);
499 for (std::size_t i = 0; i < Zag.
rows(); ++i) Z3(i, a) = Zag(i, nzdem[a]);
500 N3[a] = N2[nzdem[a]];
507 const auto attach_measures = [&](
const std::vector<T>& Xr,
const Matrix<T>& Qr) {
510 for (std::size_t a = 0; a < nzdem.size() && a < Xr.size(); ++a) {
511 const std::size_t r = nnz[nzdem[a]];
512 res.
X[r] = Xr[a] / scalevec[nzdem[a]];
513 for (std::size_t i = 0; i < M && i < Qr.rows(); ++i) res.
Q(demSt[i], r) = Qr(i, a);
517 const std::size_t Rc = nzdem.size();
519 for (std::size_t i = 0; i < Z3.
rows(); ++i)
520 for (std::size_t a = 0; a < Rc; ++a) Z3tot += Z3(i, a);
524 std::vector<T> Zv(Rc, zero);
525 for (std::size_t i = 0; i < Z3.
rows(); ++i)
526 for (std::size_t a = 0; a < Rc; ++a) Zv[a] += Z3(i, a);
527 std::vector<T> Nv(Rc, zero);
528 for (std::size_t a = 0; a < Rc; ++a) Nv[a] = num_traits<T>::from_int(N3[a]);
530 for (
int v : N3) Nsum3 += v;
538 const bool default_multi =
542 M == 1 && Z3tot > atol && Nsum3 >= 10000;
543 const bool estimator =
544 default_multi || default_big_repairman || method ==
NcMethod::Clw ||
565 ? std::string(
"default (the multi-station cub / le branch)")
572 const auto cub_budget_order = [&]() {
573 const double Cmax =
static_cast<double>(M * Rc) * 125000.0;
575 static_cast<int>(std::min<double>(std::ceil((Nsum3 - 1) / 2.0), 16.0));
578 while (order < maxorder) {
580 static_cast<double>(Rc) *
581 nck(
static_cast<int>(M) + 2 * (order + 1),
static_cast<int>(M) - 1);
582 if (tot + next > Cmax)
break;
590 for (std::size_t a = 0; a < Rc; ++a) Zsum += num_traits<T>::to_double(Zv[a]);
615 if (M > 1 && Nsum3 < 1000) {
619 pfqn_cub(L3, N3, Zv, cub_budget_order(), fineTol).lG));
630 if (L3.
rows() >= 10 && L3.
cols() > 1) {
631 bool closed = !Nv.empty();
633 for (std::size_t r = 0; r < Nv.size(); ++r) {
639 attach_measures(thin.
X, thin.
Q);
643 return finish_log(lgz +
651 const int order =
static_cast<int>(std::ceil((Nsum3 - 1) / 2.0));
679 std::vector<T> Dv(L3.
rows());
680 for (std::size_t i = 0; i < L3.
rows(); ++i) Dv[i] = L3(i, 0);
694 L3, Nv, Zv, method ==
NcMethod::LcUe ?
"ue" :
"mva", 1e-10, 1000);
695 attach_measures(thin.
X, thin.
Q);
740 attach_measures(
mc.X,
mc.Q);
744 return finish_log(lgz +
pfqn_comomrm(L3, N3, Z3, 1).lG);
773 std::vector<T> Lrow(Rc, zero);
774 for (std::size_t a = 0; a < Rc; ++a) Lrow[a] = L3(0, a);
795 return finish_log(lgz +
806 "pfqn_nc: the 'divdiff' method requires a model without think time, "
807 "which needs the integral form of Corollary 3.4. Use 'ca' or "
815 std::vector<T> Lcol(M, zero);
816 for (std::size_t i = 0; i < M; ++i) Lcol[i] = L3(i, 0);
832 for (std::size_t r = 0; r < Rc; ++r) Zm(0, r) = Zv[r];
875 res.
X.assign(R, zero);
877 for (std::size_t a = 0; a < Rc; ++a) {
878 const std::size_t r = nnz[nzdem[a]];
879 res.
X[r] =
mva.XN[a] / scalevec[nzdem[a]];
880 for (std::size_t i = 0; i < M; ++i) res.
Q(demSt[i], r) =
mva.QN(i, a);
883 have_measures =
true;
889 "pfqn_nc: RECAL is available only for models with zero think time; this model "
897 for (
int v : N3) tot += v;
898 if (M >= Rc || tot > 10 || Z3tot > atol) {
903 res.
method =
"exact/recal";
922 "pfqn_nc: the 'comom' method supports a single queueing station, but this "
923 "model has more. Use 'default', 'ca' or 'exact'.");
937 }
else if (!(Z3tot > atol)) {
940 for (
int v : N3) tot += v;
942 for (std::size_t a = 0; a < Rc; ++a)
943 g *=
num_pow_int(L3(0, a),
static_cast<unsigned>(N3[a])) /
957 return finish(Gzdem * gcore);
UnsupportedError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
NcResult< T > pfqn_mci(const Matrix< T > &D, const std::vector< int > &N, const std::vector< T > &Z, std::size_t samples, MciVariant variant, McRng &rng)
Monte Carlo Integration estimate of the normalizing constant of a closed product-form network (Ross,...
NcResult< T > pfqn_recal(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &m0)
RECAL (REcursive CALculation) for the exact normalizing constant of a closed product-form network (Co...
BktResult< T > pfqn_bkt(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Knessl-Tier expansion with the Stirling-remainder correction (BKT).
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.
std::mt19937_64 McRng
The generator type every Monte Carlo entry point in this tree accepts.
MvaResult< T > pfqn_mva(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi)
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
AghqResult< T > pfqn_aghq(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, std::size_t q)
NcResult< T > pfqn_mmsample2(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, std::size_t samples, McRng &rng)
Sampled McKenna-Mitra integral form of the normalizing constant of a repairman (single-queue plus del...
const char * nc_method_name(NcMethod m)
PropfairResult< T > pfqn_propfair(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Proportionally fair allocation estimate of the normalizing constant (Schweitzer 1979; Walton,...
LektResult< T > pfqn_lekt(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
The common corrected asymptotic expansion (LE-KT), computed on the cheaper side.
NcMethod nc_method_of(const std::string &s)
Map a method name to its enum; throws UnsupportedError on an unknown one.
BkResult< T > pfqn_bk(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Birman-Kogan saddle point normalizing constant with bottleneck detection.
LsResult< T > pfqn_ls(const Matrix< T > &L0, const std::vector< T > &N, const std::vector< T > &Z, std::size_t I, McRng &rng)
Logistic-sampling estimate of the normalizing constant of a closed product-form network.
BkLcResult< T > pfqn_bklc(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const std::string &method="mva", double tol=1e-10, int maxiter=1000)
Birman-Kogan load concealment algorithm (Algorithm 2).
constexpr double CUB_MAX_EVALS
GlobalConstants.CubMaxEvals: the integrand-evaluation budget above which pfqn_nc lowers the cubature ...
ComomResult< T > pfqn_comomrm(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, int m)
CoMoM (class-oriented method of moments) for the finite repairman model: one queueing station of mult...
void pfqn_nc_refuse(const std::string &method)
Refuse a method in an arithmetic it has no meaning in.
constexpr double MCMC_DEFAULT_BURNIN
Warm-up fraction discarded before accumulation starts.
MmintResult< T > pfqn_mmint2_gausslegendre(const std::vector< T > &L, const std::vector< T > &N, const std::vector< T > &Z, int m, std::size_t nodecap)
Gauss-Legendre form on [0, 1e6] (MATLAB pfqn_mmint2_gausslegendre).
BleResult< T > pfqn_ble(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Logistic expansion estimate of the normalizing constant, bias-corrected.
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...
PanaceaResult< T > pfqn_panacea(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, int terms)
PANACEA normal-usage asymptotic expansion of the normalizing constant (Ramakrishnan and Mitra,...
@ Imci
gamma = max(0.01, 1 - U), the MonteQueue 2.0 recommendation
@ Mci
gamma = 1/sqrt(max N) where U > 0.9, else 1 - U
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...
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 ...
constexpr std::size_t MCMC_DEFAULT_BATCHES
Schmeiser (1982), the batch count used in the tables of the paper.
BkResult< T > pfqn_bkue(const std::vector< T > &L, const T &N, const T &Z)
Birman-Kogan uniform (van der Waerden) expansion for a single chain.
NcMethod
The methods this port dispatches, one per compute_norm_const case.
@ Rgf
recursion by generating functions; residues beyond one class
@ Adaptive
the reference groups 'adaptive' with 'default'
@ Gm
the reference's alias of 'cub'
@ Bkt
KT minus the exact Stirling remainder of each Laplaced class (BKT).
@ Bkue
Birman-Kogan uniform (van der Waerden) expansion, single chain.
@ Aghq
adaptive Gauss-Hermite over the simplex; q=1 is Le
@ Lekt
the estimator Ble and Bkt both compute, on the cheaper side
@ Gleint
the reference's alias of 'mmint2'
@ Ger
residue closed form; free in the eliminated class populations
@ Mcmc
Chen-O'Cinneide regularization; supplies X and Q, never a constant.
@ Bk
Birman-Kogan saddle point with bottleneck detection.
@ Ble
LE plus the empirical eps->0 correction.
@ Divdiff
divided-difference closed form; no think time, no load dependence
@ Lc
Birman-Kogan Algorithm 2, single chain subproblems by MVA.
@ LcUe
Algorithm 2 with the uniform expansion as the single chain solver.
McmcResult< T > pfqn_mcmc(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, const std::vector< double > &s, std::size_t samples, std::size_t nbatches, double burnin, McRng &rng)
Chen-O'Cinneide REGULARIZATION: a Markov chain Monte Carlo estimator of the class throughputs X(r) = ...
KtResult< T > pfqn_kt(const Matrix< T > &L0, const std::vector< T > &N0, const std::vector< T > &Z0)
Knessl-Tier asymptotic expansion of the normalizing constant.
ExplicitResult< T > pfqn_explicit(const Matrix< T > &L, const std::vector< int > &N, double tol=std::numeric_limits< double >::epsilon(), const std::string &method="auto", double maxloss=std::numeric_limits< double >::infinity())
Explicit closed-form normalizing constant of a multiclass closed network.
LeResult< T > pfqn_le(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Logistic expansion estimate of the normalizing constant.
double pfqn_cub_evals(int M, int order, double Zsum)
Integrand-evaluation count of pfqn_cub, and the budget pfqn_nc prices it against.
NcResult< T > pfqn_is(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, std::size_t samples, McRng &rng)
Importance-sampling estimate of the normalizing constant of a closed LOAD-INDEPENDENT product-form ne...
NcResult< T > pfqn_gerasimov(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, double tol=1e-12, std::size_t maxterms=200000)
Exact normalizing constant of a closed multiclass product-form network by ITERATED RESIDUES of its ra...
CubResult< T > pfqn_cub(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, int order, const T &atol)
Normalizing constant by Grundmann-Moeller cubature over the simplex.
NcDispatchResult< T > pfqn_nc(const std::vector< T > &lambda, const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, NcMethod method, const T &atol, const NcOptions &nopt)
Normalizing constant of a product-form queueing network: the dispatcher.
T num_factorial(unsigned n)
Factorial as a value of T.
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
double nck(int n, int k)
Binomial coefficient with a thread-local memo table (mp_pfqn util/nck.c).
Number-type abstraction for the templated API port.
Adaptive Gauss-Hermite quadrature of the simplex factor of the McKenna-Mitra integral.
Birman-Kogan asymptotic evaluation of closed networks with many stations.
Knessl-Tier expansion with the Stirling-remainder correction (BKT).
Logistic expansion with the eps->0 bias correction (BLE).
Convolution algorithm for the exact normalizing constant of a closed product-form network (Buzen 1973...
Choudhury-Leung-Whitt normalization constant by numerical inversion of the generating function (JACM ...
CoMoM (class-oriented method of moments) for the finite repairman model: one queueing station of mult...
Normalizing constant by Grundmann-Moeller cubature over the simplex.
Integrand-evaluation count of pfqn_cub, and the budget pfqn_nc prices it against.
Explicit closed-form normalizing constant of a multiclass closed network.
Gerasimov's residue (closed-form) normalizing constant, generalized to R classes.
Importance-sampling estimate of the normalizing constant of a closed LOAD-INDEPENDENT product-form ne...
Knessl-Tier asymptotic expansion of the normalizing constant.
Logistic expansion (LE) asymptotic approximation of the normalizing constant of a closed product-form...
The common corrected asymptotic expansion (LE-KT), computed on the cheaper side.
Logistic-sampling estimate of the normalizing constant of a closed product-form network.
Monte Carlo Integration estimate of the normalizing constant of a closed product-form network (Ross,...
Chen-O'Cinneide REGULARIZATION: a Markov chain Monte Carlo estimator of the class throughputs X(r) = ...
McKenna-Mitra integral form of the normalizing constant of a repairman model (one queueing station,...
Sampled McKenna-Mitra integral form of the normalizing constant of a repairman (single-queue plus del...
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
PANACEA normal-usage asymptotic expansion of the normalizing constant (Ramakrishnan and Mitra,...
Proportionally fair allocation estimate of the normalizing constant (Schweitzer 1979; Walton,...
RECAL (REcursive CALculation) for the exact normalizing constant of a closed product-form network (Co...
Recursion by Generating Functions (RGF) for the normalizing constant of a SINGLE-CLASS closed product...
Multiclass Recursion by Generating Functions (RGF), with think times.
Population-vector enumeration and combinatorics.
Return value of pfqn_bklc.
Return value of pfqn_explicit, mirroring [lG, G, method, lossDigits].
std::string method
expression used, "distinct" (Eq. 15) or "repeated" (Eq. 16)
T lG
logarithm of the normalizing constant
Estimates of pfqn_mcmc together with their batch-means intervals.
std::vector< T > X
(R) per-class throughput, empty when not produced
std::string method
the algorithm actually used
Matrix< T > Q
(M x R) queue lengths, empty when not produced
bool valid
False when the METHOD DECLINED THE MODEL, which is the reference's lG = []: pana outside normal usage...
T G
normalizing constant, exact when the method is
The options fields compute_norm_const reads beyond the method itself.
double mcmc_burnin
options.config.mcmc_burnin: warm-up fraction pfqn_mcmc discards
unsigned long seed
SolverOptions('NC').seed.
std::size_t mcmc_batches
options.config.mcmc_batches: batches pfqn_mcmc splits its run into
std::size_t aghq_nodes
options.config.aghq_nodes: nodes per simplex direction of the adaptive Gauss-Hermite rule.
std::size_t samples
SolverOptions('NC').samples.
double tol
handed to pfqn_comomrm
Return value of pfqn_panacea, mirroring [Gn, lGn].
bool normalUsage
false where MATLAB returns NaN (min alpha < 0)