LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_ncld.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_PFQN_NCLD_H
6#define LINE_API_PFQN_NCLD_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Normalizing constant of a LOAD-DEPENDENT closed network: the dispatcher.
12 *
13 * Templated port of matlab/src/api/pfqn/pfqn_ncld.m. The model reduction is the
14 * same one pfqn_nc performs -- drop the empty classes, rescale per class, drop
15 * the demand-free stations, peel off the classes confined to the delay -- with
16 * one addition: the rate lattice mu follows the stations through the station
17 * filter, so a dropped station takes its rates with it.
18 *
19 * DELAY FOLDING. pfqn_gld and pfqn_lldsingle take no think-time argument: an
20 * infinite server is an ordinary row whose rate lattice is mu(i,k) = k, for
21 * which the factorials cancel. When the model has a delay this port appends
22 * one such row per think-time row, exactly as the reference does with
23 * `Lz = [L;Z]; muz = [mu; repmat(1:size(mu,2),D,1)]`.
24 *
25 * DISPATCH. The exact ladder, which is the reference's `exact` branch and its
26 * `default` branch whenever the Choudhury-Leung-Whitt gate declines:
27 *
28 * R == 1 -> pfqn_lldsingle
29 * M == 1 with a delay -> pfqn_comomrm_ld
30 * M == 1 without a delay -> pfqn_comomrm_ld with a zero think time
31 * otherwise -> pfqn_gld
32 *
33 * and, beside it, the ladder that answers with a LOGARITHM, mirroring pfqn_nc's
34 * split between an exact `finish` and an estimator `finish_log`:
35 *
36 * clw -> pfqn_clw_lld (generating-function inversion)
37 * is -> pfqn_ld_is (sample-an-ordering importance sampling)
38 * panald -> pfqn_panaceald (Mitra-McKenna asymptotic expansion)
39 * rd -> pfqn_rd (recursive decomposition)
40 * nrp / nrl -> pfqn_nrp / pfqn_nrl (Norlund-Rice probit / logit)
41 * comomld -> pfqn_comomrm_ld, or pfqn_rd where CoMoM-LD does not apply
42 * divdiff -> pfqn_explicit_ld (divided difference over the LLD closed form)
43 *
44 * THE CLW GATE ON `default` IS A COST MODEL, NOT AN ACCURACY ONE. CLW and the
45 * exact convolution compute the same constant, so the gate -- at most 5 classes,
46 * at most 200 jobs, at most 2e7 predicted contour points -- only decides which
47 * is cheaper. It is consulted in transcendental arithmetic only; in an exact
48 * field `default` goes straight to the exact ladder, which changes the SPEED on
49 * a subset of models and never the value.
50 *
51 * Arithmetic: the exact ladder and the whole reduction are EXACT-CAPABLE. The
52 * log-domain ladder is refused by name outside transcendental arithmetic, on
53 * pfqn_nc's grounds: a contour inversion, a Monte Carlo estimate and a
54 * truncated asymptotic series have no meaning in the rational field, and
55 * silently substituting the exact convolution would answer with an algorithm
56 * the caller did not ask for.
57 */
58
59#include <cmath>
60#include <cstddef>
61#include <cstdint>
62#include <string>
63#include <vector>
64
79#include "line/num/number.h"
80#include "line/util/error.h"
81#include "line/util/matrix.h"
82
83namespace line {
84namespace pfqn {
85
86/** The load-dependent methods this port dispatches. */
87enum class NcldMethod {
88 Default, Exact, Is, Clw, Panald, Rd, Nrp, Nrl, Nre, Comomld, Divdiff
89};
90
91inline const char* ncld_method_name(NcldMethod m) {
92 switch (m) {
93 case NcldMethod::Default: return "default";
94 case NcldMethod::Exact: return "exact";
95 case NcldMethod::Is: return "is";
96 case NcldMethod::Clw: return "clw";
97 case NcldMethod::Panald: return "panald";
98 case NcldMethod::Rd: return "rd";
99 case NcldMethod::Nrp: return "nrp";
100 case NcldMethod::Nrl: return "nrl";
101 case NcldMethod::Nre: return "nre";
102 case NcldMethod::Comomld: return "comomld";
103 case NcldMethod::Divdiff: return "divdiff";
104 }
105 return "default";
106}
107
108/** Map a method name to its enum; throws UnsupportedError on an unknown one. */
109inline NcldMethod ncld_method_of(const std::string& s) {
110 if (s == "default") return NcldMethod::Default;
111 if (s == "exact") return NcldMethod::Exact;
112 if (s == "is") return NcldMethod::Is;
113 if (s == "clw") return NcldMethod::Clw;
114 if (s == "pana" || s == "panald") return NcldMethod::Panald;
115 if (s == "rd") return NcldMethod::Rd;
116 if (s == "nrp") return NcldMethod::Nrp;
117 if (s == "nrl") return NcldMethod::Nrl;
118 if (s == "nre") return NcldMethod::Nre;
119 if (s == "comomld") return NcldMethod::Comomld;
120 if (s == "divdiff") return NcldMethod::Divdiff;
121 throw UnsupportedError("pfqn_ncld: unrecognized method for solving load-dependent models: '" +
122 s + "'");
123}
124
125/** Refuse a load-dependent method in an arithmetic it has no meaning in. */
126inline void pfqn_ncld_refuse(const std::string& method) {
127 throw UnsupportedError(
128 "pfqn_ncld: method '" + method +
129 "' inverts a generating function, samples, or truncates an asymptotic series, and needs "
130 "transcendental arithmetic. Use 'default', 'exact' or 'comomld' for an exact constant.");
131}
132
133template <class T>
135 T G; ///< normalizing constant
136 double lG; ///< its logarithm
137 std::string method; ///< the algorithm actually used
138};
139
140/**
141 * @brief Normalizing constant of a LOAD-DEPENDENT closed network: the
142 * dispatcher.
143 *
144 * @param L (M x R) service demands
145 * @param N (R) populations, finite and nonnegative
146 * @param Z (K x R) think times
147 * @param mu (M x >=Nt) load-dependent rate lattice
148 * @param method requested algorithm
149 * @param atol threshold below which a demand counts as zero; 0 for exact
150 * @param nopt sample count, seed and tolerance the log-domain ladder reads
151 */
152template <class T>
153NcldResult<T> pfqn_ncld(const Matrix<T>& L, const std::vector<int>& N, const Matrix<T>& Z,
154 const Matrix<T>& mu, NcldMethod method, const T& atol,
155 const NcOptions& nopt) {
156 const std::size_t R0 = N.size();
157 const T zero = num_traits<T>::from_int(0);
158 const T one = num_traits<T>::from_int(1);
159
160 NcldResult<T> res;
161 res.G = one;
162 res.lG = 0.0;
163 res.method = ncld_method_name(method);
164
165 long Ntot = 0;
166 for (int v : N) {
167 if (v < 0) throw InputError("pfqn_ncld: negative population");
168 Ntot += v;
169 }
170 if (Ntot == 0) return res;
171 const std::size_t M0 = L.empty() ? 0 : L.rows();
172 if (M0 > 0 && L.cols() != R0)
173 throw InputError("pfqn_ncld: L and N disagree on the class count");
174 if (M0 > 0 && mu.rows() != M0)
175 throw InputError("pfqn_ncld: mu and L disagree on the station count");
176 if (M0 > 0 && static_cast<long>(mu.cols()) < Ntot)
177 throw InputError("pfqn_ncld: mu has fewer rate columns than the total population");
178 const std::size_t Nt = static_cast<std::size_t>(Ntot);
179
180 // ---- drop the empty classes ----------------------------------------------
181 std::vector<std::size_t> nnz;
182 for (std::size_t r = 0; r < R0; ++r)
183 if (N[r] > 0) nnz.push_back(r);
184 const std::size_t R1 = nnz.size();
185
186 // ---- rescale each class ---------------------------------------------------
187 std::vector<T> scalevec(R1, one);
188 Matrix<T> L1(M0, R1), Z1(Z.empty() ? 0 : Z.rows(), R1);
189 for (std::size_t k = 0; k < R1; ++k) {
190 const std::size_t r = nnz[k];
191 T mx = zero;
192 for (std::size_t i = 0; i < M0; ++i)
193 if (L(i, r) > mx) mx = L(i, r);
194 for (std::size_t i = 0; i < Z1.rows(); ++i)
195 if (Z(i, r) > mx) mx = Z(i, r);
196 if (mx > zero) scalevec[k] = mx;
197 for (std::size_t i = 0; i < M0; ++i) L1(i, k) = L(i, r) / scalevec[k];
198 for (std::size_t i = 0; i < Z1.rows(); ++i) Z1(i, k) = Z(i, r) / scalevec[k];
199 }
200 T Gscale = one;
201 for (std::size_t k = 0; k < R1; ++k)
202 Gscale *= num_pow_int(scalevec[k], static_cast<unsigned>(N[nnz[k]]));
203
204 // ---- drop the stations with no demand, taking their rates with them -------
205 std::vector<std::size_t> demSt;
206 for (std::size_t i = 0; i < M0; ++i) {
207 T rs = zero;
208 for (std::size_t k = 0; k < R1; ++k) rs += L1(i, k);
209 if (rs > atol) demSt.push_back(i);
210 }
211 const std::size_t M = demSt.size();
212 Matrix<T> L2(M, R1), mu2(M, Nt);
213 for (std::size_t a = 0; a < M; ++a) {
214 for (std::size_t k = 0; k < R1; ++k) L2(a, k) = L1(demSt[a], k);
215 for (std::size_t k = 0; k < Nt; ++k) mu2(a, k) = mu(demSt[a], k);
216 }
217
218 std::vector<int> N2(R1, 0);
219 for (std::size_t k = 0; k < R1; ++k) N2[k] = N[nnz[k]];
220
221 const auto delayG = [&](const std::vector<std::size_t>& cls) {
222 T g = one;
223 for (std::size_t k : cls) {
224 T zs = zero;
225 for (std::size_t i = 0; i < Z1.rows(); ++i) zs += Z1(i, k);
226 g *= num_pow_int(zs, static_cast<unsigned>(N2[k])) /
227 num_factorial<T>(static_cast<unsigned>(N2[k]));
228 }
229 return g;
230 };
231 const auto finish = [&](const T& gcore) {
232 res.G = Gscale * gcore;
234 return res;
235 };
236 // The log-domain finish, for pfqn_nc's reason: an estimator answers with lG,
237 // and exponentiating it to take its logarithm again loses the answer once lG
238 // passes ~709.
239 const auto finish_log = [&](double lgcore) {
240 res.lG = num_traits<T>::log_as_double(Gscale) + lgcore;
241 res.G = num_traits<T>::from_double(std::exp(res.lG));
242 return res;
243 };
244
245 T Ztot = zero;
246 for (std::size_t i = 0; i < Z1.rows(); ++i)
247 for (std::size_t k = 0; k < R1; ++k) Ztot += Z1(i, k);
248 T Lsum = zero;
249 for (std::size_t a = 0; a < M; ++a)
250 for (std::size_t k = 0; k < R1; ++k) Lsum += L2(a, k);
251
252 if (M == 0 || !(Lsum > atol)) {
253 std::vector<std::size_t> all(R1);
254 for (std::size_t k = 0; k < R1; ++k) all[k] = k;
255 return finish(Ztot > atol ? delayG(all) : one);
256 }
257 if (M == 1 && !(Ztot > atol)) {
258 // Single load-dependent station, no delay:
259 // G = (sum N)! / prod N_r! * prod L_r^{N_r} / prod_{k<=Nt} mu(k).
260 long tot = 0;
261 for (int v : N2) tot += v;
262 T g = num_factorial<T>(static_cast<unsigned>(tot));
263 for (std::size_t k = 0; k < R1; ++k)
264 g *= num_pow_int(L2(0, k), static_cast<unsigned>(N2[k])) /
265 num_factorial<T>(static_cast<unsigned>(N2[k]));
266 for (std::size_t k = 0; k < Nt; ++k) {
267 if (mu2(0, k) == zero) throw NumericError("pfqn_ncld: a load-dependent rate is zero");
268 g /= mu2(0, k);
269 }
270 return finish(g);
271 }
272
273 // ---- classes confined to the delay ----------------------------------------
274 std::vector<std::size_t> zdem, nzdem;
275 for (std::size_t k = 0; k < R1; ++k) {
276 T s = zero;
277 for (std::size_t a = 0; a < M; ++a) s += L2(a, k);
278 (s > atol ? nzdem : zdem).push_back(k);
279 }
280 const T Gzdem = zdem.empty() ? one : delayG(zdem);
281
282 const std::size_t Rc = nzdem.size();
283 Matrix<T> L3(M, Rc), Z3(Z1.rows(), Rc);
284 std::vector<int> N3(Rc, 0);
285 for (std::size_t a = 0; a < Rc; ++a) {
286 for (std::size_t i = 0; i < M; ++i) L3(i, a) = L2(i, nzdem[a]);
287 for (std::size_t i = 0; i < Z1.rows(); ++i) Z3(i, a) = Z1(i, nzdem[a]);
288 N3[a] = N2[nzdem[a]];
289 }
290 T Z3tot = zero;
291 for (std::size_t i = 0; i < Z3.rows(); ++i)
292 for (std::size_t a = 0; a < Rc; ++a) Z3tot += Z3(i, a);
293
294 // ---- the ladder that answers with a logarithm -----------------------------
295 // The reference's cost model for 'default': CLW is preferred over the exact
296 // convolution only where it is cheaper, so the three gates are class count,
297 // total population and predicted contour points. `clw_defaults` supplies the
298 // same inner lattice l_j the cost is predicted from, so the two cannot drift.
299 long Nsum3 = 0;
300 for (int v : N3) Nsum3 += v;
301 bool default_clw = false;
302 // NOT CONSULTED IN AN EXACT FIELD, and that is what keeps `default` working
303 // there: the gate only decides which of two routes to the SAME constant is
304 // cheaper, so an exact backend takes the exact ladder rather than being
305 // refused for asking for a contour inversion it never named. Every caller of
306 // the four-argument overload -- pfqn_ncldmx among them -- arrives on
307 // `default`, so gating this at run time would refuse them under --arith
308 // exact.
309 if constexpr (num_traits<T>::has_transcendental) {
310 if (method == NcldMethod::Default && M > 1 && Rc >= 2 && Rc <= 5 && Nsum3 <= 200) {
311 std::vector<int> lat;
312 std::vector<double> gam;
313 // This cost estimate runs BEFORE any decomposition plan exists, so
314 // it asks for the defaults of the identity ordering. clw_defaults
315 // used to key the special cases on the chain POSITION and now keys
316 // them on `depth`; depth[j] = j+1 is the same thing for an unsplit
317 // ordering, so this reproduces the pre-decomposition defaults
318 // (l = 1, 2, 2, 3, ...) exactly.
319 std::vector<std::size_t> keep(Rc), depth(Rc);
320 for (std::size_t a = 0; a < Rc; ++a) {
321 keep[a] = a;
322 depth[a] = a + 1;
323 }
324 detail::clw_defaults(Rc, keep, depth, ClwOptions(), lat, gam);
325 double cost = 1.0;
326 for (std::size_t a = 0; a < Rc; ++a)
327 cost *= 2.0 * static_cast<double>(lat[a]) * static_cast<double>(N3[a]);
328 default_clw = cost <= 2e7; // ~2s at ~1e7 points/s
329 }
330 }
331 // CoMoM-LD carries only the delay-plus-identical-stations shape; outside it
332 // the reference warns and runs 'rd'. The substitution is visible in
333 // res.method, which is what the reference's warning conveys.
334 const bool comomld_falls_back =
335 method == NcldMethod::Comomld && M > 1 && Z3tot > num_traits<T>::from_double(1e-14);
336 const bool logdomain = default_clw || comomld_falls_back || method == NcldMethod::Is ||
337 method == NcldMethod::Clw || method == NcldMethod::Panald ||
338 method == NcldMethod::Rd || method == NcldMethod::Nrp ||
339 method == NcldMethod::Nrl || method == NcldMethod::Nre ||
340 method == NcldMethod::Divdiff;
341 if (logdomain) {
342 if constexpr (!num_traits<T>::has_transcendental) {
343 // `default` never reaches here: the gate above is compiled out, so
344 // every name arriving is one the caller asked for explicitly. A
345 // `comomld` that fell back is named WITH its fallback, since the
346 // caller's own word is exact-capable and the algorithm it resolved
347 // to is not.
349 comomld_falls_back
350 ? std::string("comomld (which falls back to 'rd' on a multi-station model "
351 "with a delay, CoMoM-LD carrying only the delay-plus-identical-"
352 "stations shape)")
353 : std::string(ncld_method_name(method)));
354 } else {
355 const double lgz = num_traits<T>::log_as_double(Gzdem);
356 // The aggregate think time per class, the reference's sum(Z,1).
357 std::vector<T> Zv(Rc, zero);
358 for (std::size_t i = 0; i < Z3.rows(); ++i)
359 for (std::size_t a = 0; a < Rc; ++a) Zv[a] += Z3(i, a);
360
361 if (default_clw || method == NcldMethod::Clw) {
362 res.method = "clw";
363 return finish_log(
364 lgz + num_traits<T>::to_double(pfqn_clw_lld(L3, N3, Zv, mu2, ClwOptions()).lG));
365 }
366 if (method == NcldMethod::Is) {
367 McRng rng(static_cast<std::uint64_t>(nopt.seed));
368 res.method = "is";
369 return finish_log(lgz + pfqn_ld_is(L3, N3, Zv, mu2, nopt.samples, rng).lG);
370 }
371 if (method == NcldMethod::Panald) {
372 const PanaceaLdResult<T> pa = pfqn_panaceald(L3, N3, Zv, mu2);
373 if (!pa.normalUsage)
374 // The reference raises here rather than returning the NaN,
375 // because normal usage is the DOMAIN of the expansion and
376 // not a numerical failure it could retry out of.
377 throw UnsupportedError(
378 std::string("pfqn_ncld: the 'panald' asymptotic expansion does not "
379 "apply to this model: ") +
380 (pa.reason ? pa.reason : "the expansion declined") +
381 ". Use 'exact', 'clw' or an approximate load-dependent method instead");
382 res.method = "panald";
383 return finish_log(lgz + num_traits<T>::to_double(pa.lG));
384 }
385 if (method == NcldMethod::Nrp) {
386 std::vector<T> Nv(Rc, zero);
387 for (std::size_t a = 0; a < Rc; ++a) Nv[a] = num_traits<T>::from_int(N3[a]);
388 res.method = "nrp";
389 return finish_log(lgz + num_traits<T>::to_double(pfqn_nrp(L3, Nv, Zv, mu2)));
390 }
391 if (method == NcldMethod::Nrl) {
392 std::vector<T> Nv(Rc, zero);
393 for (std::size_t a = 0; a < Rc; ++a) Nv[a] = num_traits<T>::from_int(N3[a]);
394 res.method = "nrl";
395 return finish_log(lgz + num_traits<T>::to_double(pfqn_nrl(L3, Nv, Zv, mu2)));
396 }
397 if (method == NcldMethod::Nre) {
398 std::vector<T> Nv(Rc, zero);
399 for (std::size_t a = 0; a < Rc; ++a) Nv[a] = num_traits<T>::from_int(N3[a]);
400 res.method = "nre";
401 return finish_log(lgz + num_traits<T>::to_double(pfqn_nre(L3, Nv, Zv, mu2)));
402 }
403 if (method == NcldMethod::Divdiff) {
404 // Divided-difference closed form with the limited load-dependent
405 // kernel of Casale-Harrison-Ong (Perform. Eval. 2021), Theorem 1.
406 // A think time would have to enter g_sigma, whose closed form
407 // covers queues only, so it is refused here as pfqn_nc refuses it
408 // in the fixed-rate case. The delay-CONFINED classes already left
409 // in Gzdem, so lgz still applies.
410 if (Z3tot > zero)
411 throw UnsupportedError(
412 "pfqn_ncld: the 'divdiff' method requires a model without think time, "
413 "which needs the integral form of Corollary 3.4. Use 'exact' or "
414 "'default'");
415 const ExplicitResult<T> ex = pfqn_explicit_ld(L3, N3, mu2);
416 // WARNINGS BECOME FLAGS on this side, so the loss is acted on here
417 // rather than printed: the reference warns past 15 digits and hands
418 // the number back, which is only safe because the user sees the
419 // warning. With no such channel, returning a constant double
420 // precision cannot carry would be a silent wrong answer.
421 if (!ex.valid || ex.lossDigits > 15)
422 throw NumericError(
423 "pfqn_ncld: the 'divdiff' closed form was exhausted by cancellation on "
424 "this model (" + std::to_string(ex.lossDigits) +
425 " decimal digits lost). Use 'exact', 'comomld' or 'rd', or merge the "
426 "near-tied scaled demands with a looser tolerance");
427 res.method = "divdiff.ld/" + ex.method;
428 return finish_log(lgz + num_traits<T>::to_double(ex.lG));
429 }
430 // 'rd', and the CoMoM-LD fallback onto it.
431 res.method = "rd";
432 return finish_log(lgz + pfqn_rd(L3, N3, Z3, mu2).lGN);
433 }
434 }
435
436 // An explicit 'comomld' that did NOT fall back goes straight to CoMoM-LD,
437 // whatever the station count: the reference calls it unconditionally on this
438 // side of the gate, where the exact ladder below would prefer pfqn_gld.
439 if (method == NcldMethod::Comomld) {
440 res.method = "comomld";
441 return finish(Gzdem * pfqn_comomrm_ld(L3, N3, Z3, mu2).G);
442 }
443
444 // ---- fold the delay rows into the demand matrix ----------------------------
445 const std::size_t D = Z3tot > atol ? Z3.rows() : 0;
446 Matrix<T> Lz(M + D, Rc), muz(M + D, Nt);
447 for (std::size_t i = 0; i < M; ++i) {
448 for (std::size_t a = 0; a < Rc; ++a) Lz(i, a) = L3(i, a);
449 for (std::size_t k = 0; k < Nt; ++k) muz(i, k) = mu2(i, k);
450 }
451 for (std::size_t d = 0; d < D; ++d) {
452 for (std::size_t a = 0; a < Rc; ++a) Lz(M + d, a) = Z3(d, a);
453 for (std::size_t k = 0; k < Nt; ++k)
454 muz(M + d, k) = num_traits<T>::from_int(static_cast<long>(k) + 1);
455 }
456
457 T gcore = one;
458 if (Rc == 1) {
459 int n1 = N3[0];
460 gcore = pfqn_lldsingle(Lz, n1, muz).G;
461 res.method = "exact/gld";
462 } else if (M == 1 && Z3tot > atol) {
463 gcore = pfqn_comomrm_ld(L3, N3, Z3, mu2).G;
464 res.method = "exact/comomld";
465 } else if (M == 1) {
466 gcore = pfqn_comomrm_ld(L3, N3, Matrix<T>(), mu2).G;
467 res.method = "exact/comomld";
468 } else {
469 gcore = pfqn_gld(Lz, N3, muz).G;
470 res.method = "exact/gld";
471 }
472
473 return finish(Gzdem * gcore);
474}
475
476/** Overload with the reference's default sample count, seed and tolerance. */
477template <class T>
478NcldResult<T> pfqn_ncld(const Matrix<T>& L, const std::vector<int>& N, const Matrix<T>& Z,
479 const Matrix<T>& mu, NcldMethod method, const T& atol) {
480 return pfqn_ncld(L, N, Z, mu, method, atol, NcOptions());
481}
482
483/** Overload with the exact (zero-tolerance) filters. */
484template <class T>
485NcldResult<T> pfqn_ncld(const Matrix<T>& L, const std::vector<int>& N, const Matrix<T>& Z,
486 const Matrix<T>& mu) {
488}
489
490} // namespace pfqn
491} // namespace line
492
493#endif // LINE_API_PFQN_NCLD_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
bool empty() const
Definition matrix.h:92
NumericError(const std::string &what)
Definition error.h:45
UnsupportedError(const std::string &what)
Definition error.h:51
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.
Definition pfqn_ncld.h:126
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...
Definition pfqn_rd.h:101
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...
Definition pfqn_ld_is.h:81
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.
Definition pfqn_ncld.h:87
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...
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.
Definition pfqn_nre.h:466
const char * ncld_method_name(NcldMethod m)
Definition pfqn_ncld.h:91
NcldMethod ncld_method_of(const std::string &s)
Map a method name to its enum; throws UnsupportedError on an unknown one.
Definition pfqn_ncld.h:109
NcldResult< T > pfqn_ncld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu, NcldMethod method, const T &atol, const NcOptions &nopt)
Normalizing constant of a LOAD-DEPENDENT closed network: the dispatcher.
Definition pfqn_ncld.h:153
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.
Definition pfqn_nrl.h:140
@ Divdiff
divided-difference closed form; no think time, no load dependence
Definition pfqn_nc.h:133
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.
Definition pfqn_nrl.h:130
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...
Definition pfqn_gld.h:150
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).
Definition pfqn_clw.h:807
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.
T num_factorial(unsigned n)
Factorial as a value of T.
Definition number.h:184
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition number.h:192
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".
Definition pfqn_clw.h:104
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.
Definition pfqn_nc.h:215
unsigned long seed
SolverOptions('NC').seed.
Definition pfqn_nc.h:217
std::size_t samples
SolverOptions('NC').samples.
Definition pfqn_nc.h:216
T G
normalizing constant
Definition pfqn_ncld.h:135
std::string method
the algorithm actually used
Definition pfqn_ncld.h:137
double lG
its logarithm
Definition pfqn_ncld.h:136
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