5#ifndef LINE_API_PFQN_NCLD_H
6#define LINE_API_PFQN_NCLD_H
88 Default, Exact,
Is,
Clw,
Panald,
Rd,
Nrp,
Nrl,
Nre,
Comomld,
Divdiff
128 throw UnsupportedError(
"pfqn_ncld: unrecognized method for solving load-dependent models: '" +
135 "pfqn_ncld: method '" + method +
136 "' inverts a generating function, samples, or truncates an asymptotic series, and needs "
137 "transcendental arithmetic. Use 'default', 'exact' or 'comomld' for an exact constant.");
168 const Matrix<T>& mu,
const std::string& method_name,
const T& atol,
170 const std::size_t R0 = N.size();
187 if (v < 0)
throw InputError(
"pfqn_ncld: negative population");
190 if (Ntot == 0)
return res;
191 const std::size_t M0 = L.
empty() ? 0 : L.
rows();
192 if (M0 > 0 && L.
cols() != R0)
193 throw InputError(
"pfqn_ncld: L and N disagree on the class count");
194 if (M0 > 0 && mu.
rows() != M0)
195 throw InputError(
"pfqn_ncld: mu and L disagree on the station count");
196 if (M0 > 0 &&
static_cast<long>(mu.
cols()) < Ntot)
197 throw InputError(
"pfqn_ncld: mu has fewer rate columns than the total population");
198 const std::size_t Nt =
static_cast<std::size_t
>(Ntot);
201 std::vector<std::size_t> nnz;
202 for (std::size_t r = 0; r < R0; ++r)
203 if (N[r] > 0) nnz.push_back(r);
204 const std::size_t R1 = nnz.size();
207 std::vector<T> scalevec(R1, one);
209 for (std::size_t k = 0; k < R1; ++k) {
210 const std::size_t r = nnz[k];
212 for (std::size_t i = 0; i < M0; ++i)
213 if (L(i, r) > mx) mx = L(i, r);
214 for (std::size_t i = 0; i < Z1.
rows(); ++i)
215 if (Z(i, r) > mx) mx = Z(i, r);
216 if (mx > zero) scalevec[k] = mx;
217 for (std::size_t i = 0; i < M0; ++i) L1(i, k) = L(i, r) / scalevec[k];
218 for (std::size_t i = 0; i < Z1.
rows(); ++i) Z1(i, k) = Z(i, r) / scalevec[k];
221 for (std::size_t k = 0; k < R1; ++k)
222 Gscale *=
num_pow_int(scalevec[k],
static_cast<unsigned>(N[nnz[k]]));
225 std::vector<std::size_t> demSt;
226 for (std::size_t i = 0; i < M0; ++i) {
228 for (std::size_t k = 0; k < R1; ++k) rs += L1(i, k);
229 if (rs > atol) demSt.push_back(i);
231 const std::size_t M = demSt.size();
233 for (std::size_t a = 0; a < M; ++a) {
234 for (std::size_t k = 0; k < R1; ++k) L2(a, k) = L1(demSt[a], k);
235 for (std::size_t k = 0; k < Nt; ++k) mu2(a, k) = mu(demSt[a], k);
238 std::vector<int> N2(R1, 0);
239 for (std::size_t k = 0; k < R1; ++k) N2[k] = N[nnz[k]];
241 const auto delayG = [&](
const std::vector<std::size_t>& cls) {
243 for (std::size_t k : cls) {
245 for (std::size_t i = 0; i < Z1.
rows(); ++i) zs += Z1(i, k);
246 g *=
num_pow_int(zs,
static_cast<unsigned>(N2[k])) /
251 const auto finish = [&](
const T& gcore) {
252 res.
G = Gscale * gcore;
259 const auto finish_log = [&](
double lgcore) {
266 for (std::size_t i = 0; i < Z1.
rows(); ++i)
267 for (std::size_t k = 0; k < R1; ++k) Ztot += Z1(i, k);
269 for (std::size_t a = 0; a < M; ++a)
270 for (std::size_t k = 0; k < R1; ++k) Lsum += L2(a, k);
272 if (M == 0 || !(Lsum > atol)) {
273 std::vector<std::size_t> all(R1);
274 for (std::size_t k = 0; k < R1; ++k) all[k] = k;
275 return finish(Ztot > atol ? delayG(all) : one);
277 if (M == 1 && !(Ztot > atol)) {
281 for (
int v : N2) tot += v;
283 for (std::size_t k = 0; k < R1; ++k)
284 g *=
num_pow_int(L2(0, k),
static_cast<unsigned>(N2[k])) /
286 for (std::size_t k = 0; k < Nt; ++k) {
287 if (mu2(0, k) == zero)
throw NumericError(
"pfqn_ncld: a load-dependent rate is zero");
294 std::vector<std::size_t> zdem, nzdem;
295 for (std::size_t k = 0; k < R1; ++k) {
297 for (std::size_t a = 0; a < M; ++a) s += L2(a, k);
298 (s > atol ? nzdem : zdem).push_back(k);
300 const T Gzdem = zdem.empty() ? one : delayG(zdem);
302 const std::size_t Rc = nzdem.size();
304 std::vector<int> N3(Rc, 0);
305 for (std::size_t a = 0; a < Rc; ++a) {
306 for (std::size_t i = 0; i < M; ++i) L3(i, a) = L2(i, nzdem[a]);
307 for (std::size_t i = 0; i < Z1.
rows(); ++i) Z3(i, a) = Z1(i, nzdem[a]);
308 N3[a] = N2[nzdem[a]];
311 for (std::size_t i = 0; i < Z3.
rows(); ++i)
312 for (std::size_t a = 0; a < Rc; ++a) Z3tot += Z3(i, a);
324 for (
int v : N3) Nsum3 += v;
325 bool default_clw =
false;
335 std::vector<int> lat;
336 std::vector<double> gam;
343 std::vector<std::size_t> keep(Rc), depth(Rc);
344 for (std::size_t a = 0; a < Rc; ++a) {
348 detail::clw_defaults(Rc, keep, depth,
ClwOptions(), lat, gam);
350 for (std::size_t a = 0; a < Rc; ++a)
351 cost *= 2.0 *
static_cast<double>(lat[a]) *
static_cast<double>(N3[a]);
352 default_clw = cost <= 2e7;
358 const bool comomld_falls_back =
360 const bool logdomain = default_clw || comomld_falls_back || method ==
NcldMethod::Is ||
374 ? std::string(
"comomld (which falls back to 'rd' on a multi-station model "
375 "with a delay, CoMoM-LD carrying only the delay-plus-identical-"
381 std::vector<T> Zv(Rc, zero);
382 for (std::size_t i = 0; i < Z3.
rows(); ++i)
383 for (std::size_t a = 0; a < Rc; ++a) Zv[a] += Z3(i, a);
402 std::string(
"pfqn_ncld: the 'panald' asymptotic expansion does not "
403 "apply to this model: ") +
405 ". Use 'exact', 'clw' or an approximate load-dependent method instead");
410 std::vector<T> Nv(Rc, zero);
411 for (std::size_t a = 0; a < Rc; ++a) Nv[a] = num_traits<T>::from_int(N3[a]);
416 std::vector<T> Nv(Rc, zero);
417 for (std::size_t a = 0; a < Rc; ++a) Nv[a] = num_traits<T>::from_int(N3[a]);
422 std::vector<T> Nv(Rc, zero);
423 for (std::size_t a = 0; a < Rc; ++a) Nv[a] = num_traits<T>::from_int(N3[a]);
436 "pfqn_ncld: the 'divdiff' method requires a model without think time, "
437 "which needs the integral form of Corollary 3.4. Use 'exact' or "
447 "pfqn_ncld: the 'divdiff' closed form was exhausted by cancellation on "
448 "this model (" + std::to_string(ex.
lossDigits) +
449 " decimal digits lost). Use 'exact', 'comomld' or 'rd', or merge the "
450 "near-tied scaled demands with a looser tolerance");
456 return finish_log(lgz +
pfqn_rd(L3, N3, Z3, mu2).lGN);
469 const std::size_t D = Z3tot > atol ? Z3.
rows() : 0;
471 for (std::size_t i = 0; i < M; ++i) {
472 for (std::size_t a = 0; a < Rc; ++a) Lz(i, a) = L3(i, a);
473 for (std::size_t k = 0; k < Nt; ++k) muz(i, k) = mu2(i, k);
475 for (std::size_t d = 0; d < D; ++d) {
476 for (std::size_t a = 0; a < Rc; ++a) Lz(M + d, a) = Z3(d, a);
477 for (std::size_t k = 0; k < Nt; ++k)
486 }
else if (M == 1 && Z3tot > atol) {
488 res.
method =
"exact/comomld";
491 res.
method =
"exact/comomld";
497 return finish(Gzdem * gcore);
511 const Matrix<T>& mu,
const std::string& method_name,
const T& atol) {
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
void pfqn_ncld_refuse(const std::string &method)
Refuse a load-dependent method in an arithmetic it has no meaning in.
RdResult< T > pfqn_rd(const Matrix< T > &L0, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu0, double tol, NcMethod method)
Reduction heuristic (RD) for the normalizing constant of a closed LOAD-DEPENDENT product-form network...
NcResult< T > pfqn_ld_is(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, const Matrix< T > &mu, std::size_t samples, McRng &rng)
Importance-sampling estimate of the normalizing constant of a closed LOAD-DEPENDENT product-form netw...
PanaceaLdResult< T > pfqn_panaceald(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, const Matrix< T > &mu, int terms)
PANACEA normal-usage asymptotic expansion for LOAD-DEPENDENT closed networks (Mitra and McKenna,...
std::mt19937_64 McRng
The generator type every Monte Carlo entry point in this tree accepts.
NcldMethod
The load-dependent methods this port dispatches.
NcResult< T > pfqn_lldsingle(const Matrix< T > &L, int N, const Matrix< T > &mu)
Exact normalizing constant of a SINGLE-CLASS closed network whose stations are LIMITED load dependent...
NcldResult< T > pfqn_ncld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu, const std::string &method_name, const T &atol, const NcOptions &nopt)
Normalizing constant of a LOAD-DEPENDENT closed network: the dispatcher.
T pfqn_nre(const Matrix< T > &L0, const std::vector< T > &N, const std::vector< T > &Z, const Matrix< T > &alpha0)
Saddle-tilted Edgeworth approximation of log G for a limited load-dependent model.
const char * ncld_method_name(NcldMethod m)
NcldMethod ncld_method_of(const std::string &s)
Map a method name to its enum; throws UnsupportedError on an unknown one.
bool ncld_method_try(const std::string &s, NcldMethod &out)
Map a method name to its enum; false when the name is not one of them.
T pfqn_nrp(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const Matrix< T > &alpha)
Norlund-Rice probit approximation of log G.
@ Divdiff
divided-difference closed form; no think time, no load dependence
T pfqn_nrl(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const Matrix< T > &alpha)
Norlund-Rice logit approximation of log G.
NcResult< T > pfqn_gld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &mu)
Exact normalizing constant of a closed product-form network whose stations may be load dependent (gen...
ComomRmResult< T > pfqn_comomrm_ld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu)
CoMoM for the repairman model with an arbitrary LOAD-DEPENDENT rate lattice at the single queueing st...
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).
ExplicitResult< T > pfqn_explicit_ld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &mu, 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 LIMITED LOAD-DEPENDENT network.
Conservation laws of a layered queueing network, enumerated from its structure.
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).
Number-type abstraction for the templated API port.
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 for the repairman model with an arbitrary LOAD-DEPENDENT rate lattice at the single queueing st...
Explicit closed-form normalizing constant of a multiclass LIMITED LOAD-DEPENDENT network.
Exact normalizing constant of a closed product-form network whose stations may be load dependent (gen...
Exact normalizing constant of a SINGLE-CLASS closed network whose stations are load dependent.
Importance-sampling estimate of the normalizing constant of a closed LOAD-DEPENDENT product-form netw...
Exact normalizing constant of a SINGLE-CLASS closed network whose stations are LIMITED load dependent...
Randomness scaffolding shared by the Monte Carlo normalizing-constant estimators (pfqn_mci,...
Normalizing constant of a product-form queueing network: the dispatcher.
Norlund-Rice inversion of the normalizing constant on a SADDLE-TILTED contour, with a second-order Ed...
Norlund-Rice inversion of the normalizing constant, in its logistic (NRL) and probit (NRP) substituti...
PANACEA normal-usage asymptotic expansion for LOAD-DEPENDENT closed networks (Mitra and McKenna,...
Reduction heuristic (RD) for the normalizing constant of a closed LOAD-DEPENDENT product-form network...
Optional lattice and aliasing parameters; empty means "use the CLW defaults".
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
double lossDigits
decimal digits lost to cancellation
bool valid
False when a caller's cancellation budget was exceeded: lG and G are then meaningless and the caller ...
The options fields compute_norm_const reads beyond the method itself.
unsigned long seed
SolverOptions('NC').seed.
std::size_t samples
SolverOptions('NC').samples.
std::string method
the algorithm actually used
Return value of pfqn_panaceald, mirroring [Gn, lGn] plus why it declined.
bool normalUsage
false wherever the reference returns NaN
const char * reason
which condition declined; nullptr when it applies