LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
api_dispatch_pfqn_a.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 *
5 * --api dispatch entries for the 'pfqn_a' group (see api_dispatch_detail.h).
6 *
7 * Conventions shared by every entry below:
8 * - argument keys are the MATLAB parameter names of matlab/src/api/pfqn/<fn>.m,
9 * output keys its output names (or the C++ field name where they agree);
10 * - an ABSENT optional argument selects the port's own shorter overload, or
11 * leaves a trailing C++ default argument unwritten. No default value is
12 * restated here. When the port takes optional arguments POSITIONALLY and the
13 * caller sets a later one while leaving an earlier one out, the earlier one
14 * has no way to stay at its default and the call is refused, naming it
15 * (`depth` below). An empty matrix or vector is the port's own "not given"
16 * for the arguments marked fillable, so those never block a later one;
17 * - node, station, class and path indices are 0-based and passed through
18 * unchanged, as read_partition/encode_indices do in api_dispatch.cpp;
19 * - a scheduling vector is a list of names, "PS", "FCFS" or "INF";
20 * - a convergence flag is reported whenever the port returns one.
21 */
22
23#include <cmath>
24#include <cstdint>
25#include <initializer_list>
26#include <string>
27#include <utility>
28#include <vector>
29
30#include "api_dispatch_detail.h"
31
90
91namespace line {
92namespace reg {
93
94namespace {
95
96// ---------------------------------------------------------------------------
97// Shared helpers
98// ---------------------------------------------------------------------------
99
100/** A positional optional argument: its key, and whether an empty value is the
101 * port's own "not given" (so leaving it out never blocks a later one). */
102struct Pos {
103 const char* key;
104 bool fillable;
105};
106
107/**
108 * How many of the trailing positional optionals to hand the port: one past the
109 * last one present. An absent one before it that the port cannot take as empty
110 * is refused, since writing its default here would restate the port's value.
111 */
112std::size_t depth(const Args& a, const char* fn, std::initializer_list<Pos> keys) {
113 const std::vector<Pos> k(keys);
114 std::size_t d = 0;
115 for (std::size_t i = 0; i < k.size(); ++i)
116 if (a.has(k[i].key)) d = i + 1;
117 for (std::size_t i = 0; i + 1 < d; ++i) {
118 if (k[i].fillable || a.has(k[i].key)) continue;
119 std::string later;
120 for (std::size_t j = d; j-- > i + 1;)
121 if (a.has(k[j].key)) later = k[j].key;
122 throw InputError(std::string(fn) + ": '" + later + "' is given but '" + k[i].key +
123 "' is not; the C++ port takes '" + k[i].key + "' positionally before '" +
124 later + "', so it cannot be left to its default while '" + later +
125 "' is set. Give '" + k[i].key + "' as well.");
126 }
127 return d;
128}
129
130/**
131 * The long form of a port whose short overload fixes some arguments itself:
132 * selected as soon as any of `triggers` is given, and then every one of
133 * `needed` must be given too, because the long form has no default for it.
134 */
135bool long_form(const Args& a, const char* fn, std::initializer_list<const char*> triggers,
136 std::initializer_list<const char*> needed) {
137 std::string trig;
138 for (const char* t : triggers)
139 if (a.has(t)) {
140 trig = t;
141 break;
142 }
143 if (trig.empty()) return false;
144 for (const char* n : needed)
145 if (!a.has(n))
146 throw InputError(std::string(fn) + ": '" + trig + "' selects the long form of the C++ "
147 "port, which has no default for '" + n + "'; give '" + n +
148 "' as well, or drop '" + trig + "' to use the port's own defaults.");
149 return true;
150}
151
152/** A scheduling name, case-insensitive. */
153int sched_code(const Json& e, const std::string& where) {
154 if (!e.is_string())
155 throw InputError(where + ": a scheduling entry must be one of \"PS\", \"FCFS\" or \"INF\", got " +
156 std::string(e.type_name()));
157 std::string s = e.get<std::string>();
158 for (char& c : s) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
159 if (s == "PS") return 0;
160 if (s == "FCFS") return 1;
161 if (s == "INF") return 2;
162 throw InputError(where + ": unknown scheduling strategy '" + e.get<std::string>() +
163 "'; expected \"PS\", \"FCFS\" or \"INF\"");
164}
165
166/** A per-station scheduling vector; absent reads as empty, the ports' "not given". */
167template <class E>
168std::vector<E> sched_vector(Args& a, const char* key, E ps, E fcfs, E inf) {
169 std::vector<E> out;
170 const Json* j = a.opt(key);
171 if (!j) return out;
172 const std::string where = std::string("'") + key + "'";
173 std::vector<Json> flat;
174 if (j->is_array())
175 for (const Json& e : *j) flat.push_back(e);
176 else
177 flat.push_back(*j);
178 for (const Json& e : flat) {
179 const int c = sched_code(e, where);
180 out.push_back(c == 0 ? ps : (c == 1 ? fcfs : inf));
181 }
182 return out;
183}
184
185std::vector<pfqn::AmvaSched> amva_sched(Args& a, const char* key) {
187}
188
189std::vector<pfqn::SchedStrategy> lin_sched(Args& a, const char* key) {
190 return sched_vector(a, key, pfqn::SchedStrategy::PS, pfqn::SchedStrategy::FCFS,
192}
193
194/** A double option; with `cn` the string "cn" selects the Chandy-Neuse test (NaN). */
195double real_option(Args& a, const char* key, bool cn) {
196 const Json& j = a.get(key);
197 if (cn && j.is_string()) {
198 std::string s = j.get<std::string>();
199 for (char& c : s) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
200 if (s == "cn") return std::nan("");
201 }
202 return number_from_json<double>(j, std::string("'") + key + "'");
203}
204
205/** A scalar integer that must be given. */
206int int_arg(Args& a, const char* key) { return a.required_integer(key); }
207
208/** A list of 0-based indices, passed through unchanged. */
209std::vector<std::size_t> index_list(const Json& j, const std::string& where) {
210 const std::vector<int> v = int_vector_from_json(j, where);
211 std::vector<std::size_t> out;
212 out.reserve(v.size());
213 for (int x : v) {
214 if (x < 0) throw InputError(where + ": an index cannot be negative, got " + std::to_string(x));
215 out.push_back(static_cast<std::size_t>(x));
216 }
217 return out;
218}
219
220std::vector<std::size_t> indices(Args& a, const char* key) {
221 return index_list(a.get(key), std::string("'") + key + "'");
222}
223
224std::vector<std::size_t> indices_or_empty(Args& a, const char* key) {
225 const Json* j = a.opt(key);
226 return j ? index_list(*j, std::string("'") + key + "'") : std::vector<std::size_t>();
227}
228
229/** A list of index lists; a flat list is a single one when `flat_is_one`. */
230std::vector<std::vector<std::size_t> > index_lists(const Json& j, const std::string& where,
231 bool flat_is_one) {
232 std::vector<std::vector<std::size_t> > out;
233 if (!j.is_array()) throw InputError(where + ": expected an array of index arrays");
234 bool nested = false;
235 for (const Json& e : j)
236 if (e.is_array()) nested = true;
237 if (!nested && !j.empty()) {
238 if (!flat_is_one) throw InputError(where + ": expected an array of index arrays");
239 out.push_back(index_list(j, where));
240 return out;
241 }
242 for (std::size_t b = 0; b < j.size(); ++b) {
243 if (!j[b].is_array())
244 throw InputError(where + ": entry " + std::to_string(b) + " must be an array");
245 out.push_back(index_list(j[b], where + "[" + std::to_string(b) + "]"));
246 }
247 return out;
248}
249
250/** An integer matrix (2-D array as rows, 1-D array as one row). */
251Matrix<int> int_matrix(Args& a, const char* key) {
252 const Json& j = a.get(key);
253 const std::string where = std::string("'") + key + "'";
254 if (j.is_array() && !j.empty() && j[0].is_array()) {
255 const std::size_t rows = j.size(), cols = j[0].size();
256 Matrix<int> m(rows, cols, 0);
257 for (std::size_t i = 0; i < rows; ++i) {
258 if (!j[i].is_array() || j[i].size() != cols)
259 throw InputError(where + ": row " + std::to_string(i) + " has the wrong length");
260 const std::vector<int> r = int_vector_from_json(j[i], where);
261 for (std::size_t k = 0; k < cols; ++k) m(i, k) = r[k];
262 }
263 return m;
264 }
265 const std::vector<int> r = int_vector_from_json(j, where);
266 Matrix<int> m(1, r.size(), 0);
267 for (std::size_t k = 0; k < r.size(); ++k) m(0, k) = r[k];
268 return m;
269}
270
271/** A population vector in which an infinite entry marks an open class. */
272std::vector<int> mixed_population(Args& a, const char* key) {
273 const Json& j = a.get(key);
274 const std::string where = std::string("'") + key + "'";
275 std::vector<int> out;
276 for (double d : vector_from_json<double>(j, where)) {
277 if (std::isinf(d) && d > 0) {
278 out.push_back(pfqn::kOpenClass);
279 continue;
280 }
281 if (d != std::floor(d) || d < 0)
282 throw InputError(where + ": a closed-class population must be a non-negative integer");
283 out.push_back(static_cast<int>(d));
284 }
285 return out;
286}
287
288/** A boolean vector, from true/false or 0/1 entries. */
289std::vector<bool> bool_vector(const Json& j, const std::string& where) {
290 std::vector<bool> out;
291 std::vector<Json> flat;
292 if (j.is_array())
293 for (const Json& e : j) flat.push_back(e);
294 else
295 flat.push_back(j);
296 for (const Json& e : flat) {
297 if (e.is_boolean()) {
298 out.push_back(e.get<bool>());
299 continue;
300 }
301 const std::vector<int> v = int_vector_from_json(e, where);
302 if (v.size() != 1 || (v[0] != 0 && v[0] != 1))
303 throw InputError(where + ": entries must be true, false, 0 or 1");
304 out.push_back(v[0] != 0);
305 }
306 return out;
307}
308
309Json encode_index_list(const std::vector<std::size_t>& v) {
310 Json a = Json::array();
311 for (std::size_t x : v) a.push_back(static_cast<std::uint64_t>(x));
312 return a;
313}
314
315/** exp of a log normalizing constant, at an arithmetic that has it. */
316template <class T>
317T exp_of_log(Args& a, const char* key) {
318 if constexpr (num_traits<T>::has_transcendental) {
319 using std::exp;
320 const T l = a.number<T>(key);
321 return T(exp(l));
322 } else {
323 throw UnsupportedError(ErrorKind{"Arithmetic"},
324 std::string("'") + key + "' is a logarithm, and exp() is not in the exact "
325 "rational field; omit it to have the port compute G(N) exactly, or rerun "
326 "with --arith double or --arith real:<digits>.");
327 }
328}
329
330// ---------------------------------------------------------------------------
331// Result shapes
332// ---------------------------------------------------------------------------
333
334template <class T>
335Json amva_results(const pfqn::AmvaResult<T>& r, const char* itkey) {
336 Json out;
337 out["XN"] = encode_vector(r.XN);
338 out["QN"] = encode_matrix(r.QN);
339 out["UN"] = encode_matrix(r.UN);
340 out["RN"] = encode_matrix(r.RN);
341 out[itkey] = encode_count(r.iterations);
342 out["converged"] = r.converged;
343 return out;
344}
345
346/** The six Linearizer outputs, under the names the MATLAB function gives them. */
347template <class T>
348Json lin_results(const pfqn::LinearizerResult<T>& r, const char* q, const char* u, const char* w,
349 const char* c, const char* x) {
350 Json out;
351 out[q] = encode_matrix(r.Q);
352 out[u] = encode_matrix(r.U);
353 out[w] = encode_matrix(r.W);
354 out[c] = encode_vector(r.C);
355 out[x] = encode_vector(r.X);
356 out["totiter"] = r.totiter;
357 return out;
358}
359
360template <class T>
361Json lin_results(const pfqn::LinearizerResult<T>& r) {
362 return lin_results(r, "Q", "U", "W", "C", "X");
363}
364
365template <class G, class L>
366Json g_lg(const G& g, const L& lg, const char* gk = "G", const char* lk = "lG") {
367 Json out;
368 out[gk] = encode_scalar(g);
369 out[lk] = encode_scalar(lg);
370 return out;
371}
372
373// ---------------------------------------------------------------------------
374// Entries
375// ---------------------------------------------------------------------------
376
377struct OpCycletOfree {
378 static Json run(Args& a) {
379 const std::vector<double> v = a.vector<double>("v");
380 const std::vector<double> mu = a.vector<double>("mu");
381 const std::size_t N = a.count("N");
382 const std::vector<std::vector<std::size_t> > paths =
383 index_lists(a.get("path"), "'path'", true);
384 const std::vector<double> tset = a.vector<double>("tset");
385 pfqn::CycletResult r;
386 const Json* oj = a.opt("options");
387 a.done();
388 if (!oj) {
389 r = pfqn::pfqn_cyclet_ofree(v, mu, N, paths, tset);
390 } else {
391 Args o(*oj, "pfqn_cyclet_ofree options");
392 const std::size_t d = depth(o, "pfqn_cyclet_ofree options",
393 {{"method", false}, {"nmom", false}, {"pathprob", true},
394 {"lti_method", false}, {"tol", false}});
395 const std::string method = o.has("method") ? o.text("method", "") : std::string();
396 const std::size_t nmom = o.has("nmom") ? o.count("nmom") : 0;
397 const std::vector<double> pathprob = o.vector_or_empty<double>("pathprob");
398 const std::string lti = o.has("lti_method") ? o.text("lti_method", "") : std::string();
399 const double tol = o.has("tol") ? o.number<double>("tol") : 0.0;
400 o.done();
401 switch (d) {
402 case 0: r = pfqn::pfqn_cyclet_ofree(v, mu, N, paths, tset); break;
403 case 1: r = pfqn::pfqn_cyclet_ofree(v, mu, N, paths, tset, method); break;
404 case 2: r = pfqn::pfqn_cyclet_ofree(v, mu, N, paths, tset, method, nmom); break;
405 case 3:
406 r = pfqn::pfqn_cyclet_ofree(v, mu, N, paths, tset, method, nmom, pathprob);
407 break;
408 case 4:
409 r = pfqn::pfqn_cyclet_ofree(v, mu, N, paths, tset, method, nmom, pathprob, lti);
410 break;
411 default:
412 r = pfqn::pfqn_cyclet_ofree(v, mu, N, paths, tset, method, nmom, pathprob, lti,
413 tol);
414 }
415 }
416 Json out;
417 out["f"] = encode_vector(r.f);
418 out["F"] = encode_vector(r.F);
419 out["mom"] = encode_vector(r.mom);
420 Json info = Json::array();
421 for (const pfqn::CycletPathInfo& p : r.info) {
422 Json e;
423 e["method"] = p.method;
424 e["lG"] = p.lG;
425 e["path"] = encode_index_list(p.path);
426 info.push_back(e);
427 }
428 out["out"] = info;
429 return out;
430 }
431};
432
433struct OpExpand {
434 template <class T>
435 static Json run(Args& a) {
436 const Matrix<T> QN = a.matrix<T>("QN");
437 const Matrix<T> UN = a.matrix<T>("UN");
438 const Matrix<T> CN = a.matrix<T>("CN");
439 std::vector<std::size_t> mapping = indices(a, "mapping");
440 const std::size_t M = a.count("M_original");
441 a.done();
442 // MATLAB fills rows 1..M_original from mapping(1..M_original).
443 if (mapping.size() < M)
444 throw InputError("pfqn_expand: mapping has " + std::to_string(mapping.size()) +
445 " entries, fewer than M_original = " + std::to_string(M));
446 mapping.resize(M);
447 const pfqn::ExpandResult<T> r = pfqn::pfqn_expand(QN, UN, CN, mapping);
448 Json out;
449 out["QN_full"] = encode_matrix(r.QN);
450 out["UN_full"] = encode_matrix(r.UN);
451 out["CN_full"] = encode_matrix(r.CN);
452 return out;
453 }
454};
455
456template <class T>
457Json explicit_results(const pfqn::ExplicitResult<T>& r) {
458 Json out;
459 out["lG"] = encode_scalar(r.lG);
460 out["G"] = encode_scalar(r.G);
461 out["method"] = r.method;
462 out["lossDigits"] = r.lossDigits;
463 out["valid"] = r.valid;
464 return out;
465}
466
467struct OpExplicit {
468 template <class T>
469 static Json run(Args& a) {
470 const Matrix<T> L = a.matrix<T>("L");
471 const std::vector<int> N = a.ints("N");
472 const std::size_t d =
473 depth(a, "pfqn_explicit", {{"tol", false}, {"method", false}, {"maxloss", false}});
474 const double tol = a.has("tol") ? a.number<double>("tol") : 0.0;
475 const std::string method = a.text("method", "");
476 const double maxloss = a.has("maxloss") ? a.number<double>("maxloss") : 0.0;
477 a.done();
478 switch (d) {
479 case 0: return explicit_results(pfqn::pfqn_explicit(L, N));
480 case 1: return explicit_results(pfqn::pfqn_explicit(L, N, tol));
481 case 2: return explicit_results(pfqn::pfqn_explicit(L, N, tol, method));
482 default: return explicit_results(pfqn::pfqn_explicit(L, N, tol, method, maxloss));
483 }
484 }
485};
486
487struct OpExplicitLd {
488 template <class T>
489 static Json run(Args& a) {
490 const Matrix<T> L = a.matrix<T>("L");
491 const std::vector<int> N = a.ints("N");
492 const Matrix<T> mu = a.matrix<T>("mu");
493 const std::size_t d =
494 depth(a, "pfqn_explicit_ld", {{"tol", false}, {"method", false}, {"maxloss", false}});
495 const double tol = a.has("tol") ? a.number<double>("tol") : 0.0;
496 const std::string method = a.text("method", "");
497 const double maxloss = a.has("maxloss") ? a.number<double>("maxloss") : 0.0;
498 a.done();
499 switch (d) {
500 case 0: return explicit_results(pfqn::pfqn_explicit_ld(L, N, mu));
501 case 1: return explicit_results(pfqn::pfqn_explicit_ld(L, N, mu, tol));
502 case 2: return explicit_results(pfqn::pfqn_explicit_ld(L, N, mu, tol, method));
503 default:
504 return explicit_results(pfqn::pfqn_explicit_ld(L, N, mu, tol, method, maxloss));
505 }
506 }
507};
508
509struct OpDivdiffLd {
510 template <class T>
511 static Json run(Args& a) {
512 const Matrix<T> L = a.matrix<T>("L");
513 const std::vector<int> N = a.ints("N");
514 const bool hasZ = a.has("Z"), hasMu = a.has("mu");
515 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
516 const Matrix<T> mu = a.matrix_or_empty<T>("mu");
517 a.unsupported("options", "the C++ port of pfqn_divdiff_ld takes no solver options");
518 a.done();
519 pfqn::DivdiffLdResult<T> r = hasMu ? pfqn::pfqn_divdiff_ld(L, N, Z, mu)
520 : (hasZ ? pfqn::pfqn_divdiff_ld(L, N, Z)
521 : pfqn::pfqn_divdiff_ld(L, N));
522 Json out;
523 out["lG"] = encode_scalar(r.lG);
524 out["G"] = encode_scalar(r.G);
525 out["lossDigits"] = r.lossDigits;
526 return out;
527 }
528};
529
530/** pfqn_bs, pfqn_lcp and pfqn_chow share MATLAB's (L,N,Z,tol,maxiter,QN0,type). */
531template <class T>
532struct AmvaArgs {
533 Matrix<T> L;
534 std::vector<T> N, Z;
535 bool hasZ;
536 std::vector<pfqn::AmvaSched> type;
537 double tol;
538 std::size_t maxiter;
539 Matrix<T> QN0;
540 std::size_t d; // how many of tol, maxiter, QN0 to pass
541 bool longf;
542};
543
544template <class T>
545AmvaArgs<T> read_amva(Args& a, const char* fn, bool cn, std::initializer_list<Pos> extra) {
546 AmvaArgs<T> s;
547 s.L = a.matrix<T>("L");
548 s.N = a.vector<T>("N");
549 s.hasZ = a.has("Z");
550 s.Z = a.vector_or_empty<T>("Z");
551 std::vector<Pos> keys = {{"tol", false}, {"maxiter", false}, {"QN0", true}};
552 keys.insert(keys.end(), extra.begin(), extra.end());
553 // depth() takes an initializer_list; spell out the two shapes used here.
554 if (keys.size() == 3)
555 s.d = depth(a, fn, {{"tol", false}, {"maxiter", false}, {"QN0", true}});
556 else
557 s.d = depth(a, fn, {{"tol", false}, {"maxiter", false}, {"QN0", true}, keys[3]});
558 s.longf = s.d > 0 || a.has("type");
559 s.type = amva_sched(a, "type");
560 s.tol = a.has("tol") ? real_option(a, "tol", cn) : 0.0;
561 s.maxiter = a.has("maxiter") ? a.count("maxiter") : 0;
562 s.QN0 = a.matrix_or_empty<T>("QN0");
563 return s;
564}
565
566struct OpBs {
567 template <class T>
568 static Json run(Args& a) {
569 AmvaArgs<T> s = read_amva<T>(a, "pfqn_bs", true, {});
570 a.done();
571 pfqn::AmvaResult<T> r;
572 if (!s.longf)
573 r = s.hasZ ? pfqn::pfqn_bs(s.L, s.N, s.Z) : pfqn::pfqn_bs(s.L, s.N);
574 else if (s.d == 0)
575 r = pfqn::pfqn_bs(s.L, s.N, s.Z, s.type);
576 else if (s.d == 1)
577 r = pfqn::pfqn_bs(s.L, s.N, s.Z, s.type, s.tol);
578 else if (s.d == 2)
579 r = pfqn::pfqn_bs(s.L, s.N, s.Z, s.type, s.tol, s.maxiter);
580 else
581 r = pfqn::pfqn_bs(s.L, s.N, s.Z, s.type, s.tol, s.maxiter, s.QN0);
582 return amva_results(r, "it");
583 }
584};
585
586struct OpLcp {
587 template <class T>
588 static Json run(Args& a) {
589 AmvaArgs<T> s = read_amva<T>(a, "pfqn_lcp", false, {});
590 a.done();
591 pfqn::AmvaResult<T> r;
592 if (!s.longf)
593 r = s.hasZ ? pfqn::pfqn_lcp(s.L, s.N, s.Z) : pfqn::pfqn_lcp(s.L, s.N);
594 else if (s.d == 0)
595 r = pfqn::pfqn_lcp(s.L, s.N, s.Z, s.type);
596 else if (s.d == 1)
597 r = pfqn::pfqn_lcp(s.L, s.N, s.Z, s.type, s.tol);
598 else if (s.d == 2)
599 r = pfqn::pfqn_lcp(s.L, s.N, s.Z, s.type, s.tol, s.maxiter);
600 else
601 r = pfqn::pfqn_lcp(s.L, s.N, s.Z, s.type, s.tol, s.maxiter, s.QN0);
602 return amva_results(r, "it");
603 }
604};
605
606pfqn::ChowVariant chow_variant(Args& a) {
607 std::string v = a.text("variant", "");
608 for (char& c : v) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
609 if (v == "forward") return pfqn::ChowVariant::Forward;
610 if (v == "backward") return pfqn::ChowVariant::Backward;
611 throw InputError("pfqn_chow: unknown variant '" + v + "'; expected \"forward\" or \"backward\"");
612}
613
614struct OpChow {
615 template <class T>
616 static Json run(Args& a) {
617 AmvaArgs<T> s = read_amva<T>(a, "pfqn_chow", false, {{"variant", false}});
618 const bool hasVariant = a.has("variant");
619 const pfqn::ChowVariant variant = hasVariant ? chow_variant(a) : pfqn::ChowVariant::Forward;
620 a.done();
621 pfqn::AmvaResult<T> r;
622 if (!s.longf && !hasVariant)
623 r = s.hasZ ? pfqn::pfqn_chow(s.L, s.N, s.Z) : pfqn::pfqn_chow(s.L, s.N);
624 else if (s.d == 0)
625 r = pfqn::pfqn_chow(s.L, s.N, s.Z, s.type);
626 else if (s.d == 1)
627 r = pfqn::pfqn_chow(s.L, s.N, s.Z, s.type, s.tol);
628 else if (s.d == 2)
629 r = pfqn::pfqn_chow(s.L, s.N, s.Z, s.type, s.tol, s.maxiter);
630 else if (s.d == 3)
631 r = pfqn::pfqn_chow(s.L, s.N, s.Z, s.type, s.tol, s.maxiter, s.QN0);
632 else
633 r = pfqn::pfqn_chow(s.L, s.N, s.Z, s.type, s.tol, s.maxiter, s.QN0, variant);
634 return amva_results(r, "it");
635 }
636};
637
638struct OpCntol {
639 template <class T>
640 static Json run(Args& a) {
641 const std::vector<T> N = a.vector<T>("N");
642 a.done();
643 Json out;
644 out["tol"] = pfqn::pfqn_cntol(N);
645 return out;
646 }
647};
648
649struct OpAql {
650 template <class T>
651 static Json run(Args& a) {
652 const Matrix<T> L = a.matrix<T>("L");
653 const std::vector<T> N = a.vector<T>("N");
654 const bool hasZ = a.has("Z");
655 const std::vector<T> Z = a.vector_or_empty<T>("Z");
656 const std::size_t d = depth(a, "pfqn_aql", {{"TOL", false}, {"MAXITER", false}});
657 const double tol = a.has("TOL") ? a.number<double>("TOL") : 0.0;
658 const std::size_t maxiter = a.has("MAXITER") ? a.count("MAXITER") : 0;
659 a.unsupported("QN0", "the C++ port of pfqn_aql takes no warm start; omit QN0");
660 a.done();
661 pfqn::AmvaResult<T> r;
662 if (d == 0)
663 r = hasZ ? pfqn::pfqn_aql(L, N, Z) : pfqn::pfqn_aql(L, N);
664 else if (d == 1)
665 r = pfqn::pfqn_aql(L, N, Z, tol);
666 else
667 r = pfqn::pfqn_aql(L, N, Z, tol, maxiter);
668 return amva_results(r, "numIters");
669 }
670};
671
672/** The long-form arguments of the Linearizer family. */
673template <class T>
674struct LinArgs {
675 std::vector<pfqn::SchedStrategy> type;
676 double tol = 0.0;
677 int maxiter = 0;
678 Matrix<T> QN0;
679};
680
681template <class T>
682LinArgs<T> read_lin(Args& a, bool cn) {
683 LinArgs<T> s;
684 s.type = lin_sched(a, "type");
685 if (a.has("tol")) s.tol = real_option(a, "tol", cn);
686 if (a.has("maxiter")) s.maxiter = static_cast<int>(a.count("maxiter"));
687 s.QN0 = a.matrix_or_empty<T>("QN0");
688 return s;
689}
690
691struct OpLinearizer {
692 template <class T>
693 static Json run(Args& a) {
694 const Matrix<T> L = a.matrix<T>("L");
695 const std::vector<int> N = a.ints("N");
696 const bool hasZ = a.has("Z");
697 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
698 const bool lf = long_form(a, "pfqn_linearizer", {"type", "tol", "maxiter", "QN0"},
699 {"tol", "maxiter"});
700 const LinArgs<T> s = read_lin<T>(a, true);
701 a.done();
702 if (lf) return lin_results(pfqn::pfqn_linearizer(L, N, Z, s.type, s.tol, s.maxiter, s.QN0));
703 return lin_results(hasZ ? pfqn::pfqn_linearizer(L, N, Z) : pfqn::pfqn_linearizer(L, N));
704 }
705};
706
707struct OpLinearizerms {
708 template <class T>
709 static Json run(Args& a) {
710 const Matrix<T> L = a.matrix<T>("L");
711 const std::vector<int> N = a.ints("N");
712 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
713 const std::vector<int> nservers = a.ints("nservers");
714 const bool lf = long_form(a, "pfqn_linearizerms", {"type", "tol", "maxiter", "QN0"},
715 {"tol", "maxiter"});
716 const LinArgs<T> s = read_lin<T>(a, false);
717 a.done();
718 const pfqn::LinearizerResult<T> r =
719 lf ? pfqn::pfqn_linearizerms(L, N, Z, nservers, s.type, s.tol, s.maxiter, s.QN0)
720 : pfqn::pfqn_linearizerms(L, N, Z, nservers);
721 return lin_results(r, "Q", "U", "R", "C", "X");
722 }
723};
724
725struct OpConwayms {
726 template <class T>
727 static Json run(Args& a) {
728 const Matrix<T> L = a.matrix<T>("L");
729 const std::vector<int> N = a.ints("N");
730 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
731 const std::vector<int> nservers = a.ints("nservers");
732 const bool lf = long_form(a, "pfqn_conwayms", {"type", "tol", "maxiter", "QN0"},
733 {"tol", "maxiter"});
734 const LinArgs<T> s = read_lin<T>(a, false);
735 a.done();
736 const pfqn::LinearizerResult<T> r =
737 lf ? pfqn::pfqn_conwayms(L, N, Z, nservers, s.type, s.tol, s.maxiter, s.QN0)
738 : pfqn::pfqn_conwayms(L, N, Z, nservers);
739 return lin_results(r, "Q", "U", "R", "C", "X");
740 }
741};
742
743pfqn::LinearizerMxMethod mx_method(Args& a) {
744 std::string m = a.text("method", "");
745 for (char& c : m) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
746 if (m == "lin") return pfqn::LinearizerMxMethod::Lin;
747 if (m == "gflin") return pfqn::LinearizerMxMethod::Gflin;
748 if (m == "egflin") return pfqn::LinearizerMxMethod::Egflin;
749 throw InputError("pfqn_linearizermx: unknown method '" + m +
750 "'; expected \"lin\", \"gflin\" or \"egflin\"");
751}
752
753struct OpLinearizermx {
754 template <class T>
755 static Json run(Args& a) {
756 const std::vector<T> lambda = a.vector<T>("lambda");
757 const Matrix<T> L = a.matrix<T>("L");
758 const std::vector<int> N = mixed_population(a, "N");
759 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
760 const std::vector<int> nservers = a.ints("nservers");
761 const bool lf = long_form(a, "pfqn_linearizermx", {"type", "tol", "maxiter", "QN0"},
762 {"tol", "maxiter", "method"});
763 const LinArgs<T> s = read_lin<T>(a, false);
764 const bool hasMethod = a.has("method");
765 const pfqn::LinearizerMxMethod method =
766 hasMethod ? mx_method(a) : pfqn::LinearizerMxMethod::Egflin;
767 a.done();
768 pfqn::LinearizerResult<T> r;
769 if (lf)
770 r = pfqn::pfqn_linearizermx(lambda, L, N, Z, nservers, s.type, s.tol, s.maxiter, method,
771 s.QN0);
772 else if (hasMethod)
773 r = pfqn::pfqn_linearizermx(lambda, L, N, Z, nservers, method);
774 else
775 r = pfqn::pfqn_linearizermx(lambda, L, N, Z, nservers);
776 return lin_results(r, "QN", "UN", "WN", "CN", "XN");
777 }
778};
779
780struct OpGflinearizer {
781 template <class T>
782 static Json run(Args& a) {
783 const Matrix<T> L = a.matrix<T>("L");
784 const std::vector<int> N = a.ints("N");
785 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
786 const bool lf = long_form(a, "pfqn_gflinearizer", {"type", "tol", "maxiter", "QN0"},
787 {"tol", "maxiter"});
788 const LinArgs<T> s = read_lin<T>(a, true);
789 const T alpha = a.number<T>("alpha");
790 a.done();
791 if (lf)
792 return lin_results(
793 pfqn::pfqn_gflinearizer(L, N, Z, s.type, s.tol, s.maxiter, alpha, s.QN0));
794 return lin_results(pfqn::pfqn_gflinearizer(L, N, Z, alpha));
795 }
796};
797
798struct OpEgflinearizer {
799 template <class T>
800 static Json run(Args& a) {
801 const Matrix<T> L = a.matrix<T>("L");
802 const std::vector<int> N = a.ints("N");
803 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
804 const bool lf = long_form(a, "pfqn_egflinearizer",
805 {"type", "tol", "maxiter", "QN0", "npasses"}, {"tol", "maxiter"});
806 const LinArgs<T> s = read_lin<T>(a, true);
807 const std::vector<T> alpha = a.vector<T>("alpha");
808 const bool hasNp = a.has("npasses");
809 const int npasses = hasNp ? int_arg(a, "npasses") : 0;
810 a.done();
811 if (!lf) return lin_results(pfqn::pfqn_egflinearizer(L, N, Z, alpha));
812 if (hasNp)
813 return lin_results(pfqn::pfqn_egflinearizer(L, N, Z, s.type, s.tol, s.maxiter, alpha,
814 s.QN0, npasses));
815 return lin_results(
816 pfqn::pfqn_egflinearizer(L, N, Z, s.type, s.tol, s.maxiter, alpha, s.QN0));
817 }
818};
819
820struct OpDmlin {
821 template <class T>
822 static Json run(Args& a) {
823 const Matrix<T> L = a.matrix<T>("L");
824 const std::vector<int> N = a.ints("N");
825 const bool hasZ = a.has("Z");
826 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
827 const bool lf = long_form(a, "pfqn_dmlin", {"type", "tol", "maxiter", "QN0", "npasses"},
828 {"tol", "maxiter"});
829 const LinArgs<T> s = read_lin<T>(a, false);
830 const bool hasNp = a.has("npasses");
831 const int npasses = hasNp ? int_arg(a, "npasses") : 0;
832 a.done();
833 if (!lf) return lin_results(hasZ ? pfqn::pfqn_dmlin(L, N, Z) : pfqn::pfqn_dmlin(L, N));
834 if (hasNp)
835 return lin_results(
836 pfqn::pfqn_dmlin(L, N, Z, s.type, s.tol, s.maxiter, s.QN0, npasses));
837 return lin_results(pfqn::pfqn_dmlin(L, N, Z, s.type, s.tol, s.maxiter, s.QN0));
838 }
839};
840
841struct OpLldfun {
842 template <class T>
843 static Json run(Args& a) {
844 const std::vector<T> n = a.vector<T>("n");
845 const Matrix<T> lldscaling = a.matrix_or_empty<T>("lldscaling");
846 const bool hasC = a.has("nservers");
847 const std::vector<double> nservers = a.vector_or_empty<double>("nservers");
848 a.done();
849 Json out;
850 out["r"] = encode_vector(hasC ? pfqn::pfqn_lldfun(n, lldscaling, nservers)
851 : pfqn::pfqn_lldfun(n, lldscaling));
852 return out;
853 }
854};
855
856struct OpComomrmLd {
857 template <class T>
858 static Json run(Args& a) {
859 const Matrix<T> L = a.matrix<T>("L");
860 const std::vector<int> N = a.ints("N");
861 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
862 const Matrix<T> mu = a.matrix<T>("mu");
863 a.unsupported("options", "the C++ port of pfqn_comomrm_ld takes no solver options "
864 "(MATLAB reads options.tol there)");
865 a.done();
866 const pfqn::ComomRmResult<T> r = pfqn::pfqn_comomrm_ld(L, N, Z, mu);
867 Json out = g_lg(r.G, r.lG);
868 out["prob"] = encode_vector(r.prob);
869 return out;
870 }
871};
872
873struct OpGldsingle {
874 template <class T>
875 static Json run(Args& a) {
876 const Matrix<T> L = a.matrix<T>("L");
877 const int N = int_arg(a, "N");
878 const Matrix<T> mu = a.matrix<T>("mu");
879 a.unsupported("options", "the C++ port of pfqn_gldsingle takes no solver options");
880 a.done();
881 const pfqn::NcResult<T> r = pfqn::pfqn_gldsingle(L, N, mu);
882 return g_lg(r.G, r.lG);
883 }
884};
885
886struct OpLldsingle {
887 template <class T>
888 static Json run(Args& a) {
889 const Matrix<T> L = a.matrix<T>("L");
890 const int N = int_arg(a, "N");
891 const Matrix<T> mu = a.matrix<T>("mu");
892 a.unsupported("options", "the C++ port of pfqn_lldsingle takes no solver options");
893 a.done();
894 const pfqn::NcResult<T> r = pfqn::pfqn_lldsingle(L, N, mu);
895 return g_lg(r.G, r.lG);
896 }
897};
898
899struct OpLld {
900 template <class T>
901 static Json run(Args& a) {
902 const Matrix<T> L = a.matrix<T>("L");
903 const std::vector<int> N = a.ints("N");
904 const bool hasMu = a.has("mu");
905 const Matrix<T> mu = a.matrix_or_empty<T>("mu");
906 a.unsupported("options", "the C++ port of pfqn_lld takes no solver options");
907 a.done();
908 const pfqn::LldResult<T> r = hasMu ? pfqn::pfqn_lld(L, N, mu) : pfqn::pfqn_lld(L, N);
909 Json out = g_lg(r.G, r.lG);
910 out["s"] = encode_ints(r.s);
911 return out;
912 }
913};
914
915struct OpJoint {
916 template <class T>
917 static Json run(Args& a) {
918 const Matrix<int> n = int_matrix(a, "n");
919 const Matrix<T> L = a.matrix<T>("L");
920 const std::vector<int> N = a.ints("N");
921 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
922 const bool hasG = a.has("lGN");
923 const T G = hasG ? exp_of_log<T>(a, "lGN") : num_traits<T>::from_int(0);
924 a.done();
925 T p;
926 // MATLAB: a single column is the per-station total form, R columns the per-class form.
927 if (n.cols() == 1) {
928 std::vector<int> m(n.rows());
929 for (std::size_t i = 0; i < n.rows(); ++i) m[i] = n(i, 0);
930 p = hasG ? pfqn::pfqn_joint_total(m, L, N, Z, G) : pfqn::pfqn_joint_total(m, L, N, Z);
931 } else {
932 p = hasG ? pfqn::pfqn_joint(n, L, N, Z, G) : pfqn::pfqn_joint(n, L, N, Z);
933 }
934 Json out;
935 out["pjoint"] = encode_scalar(p);
936 return out;
937 }
938};
939
940struct OpJointmarg {
941 template <class T>
942 static Json run(Args& a) {
943 const std::vector<int> n = a.ints("n");
944 const Matrix<T> L = a.matrix<T>("L");
945 const std::vector<int> N = a.ints("N");
946 const std::vector<std::size_t> infset = indices_or_empty(a, "infset");
947 const bool hasG = a.has("lGN");
948 const T G = hasG ? exp_of_log<T>(a, "lGN") : num_traits<T>::from_int(0);
949 const bool hasE = a.has("engine");
950 const std::string engine = a.text("engine", "");
951 a.done();
952 T p;
953 if (hasG)
954 p = hasE ? pfqn::pfqn_jointmarg(n, L, N, infset, G, engine)
955 : pfqn::pfqn_jointmarg(n, L, N, infset, G);
956 else
957 p = hasE ? pfqn::pfqn_jointmarg(n, L, N, infset, engine)
958 : pfqn::pfqn_jointmarg(n, L, N, infset);
959 Json out;
960 out["pjoint"] = encode_scalar(p);
961 if constexpr (num_traits<T>::has_transcendental) {
962 using std::log;
963 out["lpjoint"] = encode_scalar(T(log(p)));
964 } else {
965 out["lpjoint"] = std::log(num_traits<T>::to_double(p));
966 }
967 return out;
968 }
969};
970
971struct OpLcfsqnCa {
972 template <class T>
973 static Json run(Args& a) {
974 const std::vector<T> alpha = a.vector<T>("alpha");
975 const std::vector<T> beta = a.vector<T>("beta");
976 const std::vector<int> N = a.ints("N");
977 a.done();
978 const pfqn::LcfsQnResult<T> r = pfqn::pfqn_lcfsqn_ca(alpha, beta, N);
979 Json out;
980 out["G"] = encode_scalar(r.G);
981 out["V"] = encode_scalar(r.V);
982 return out;
983 }
984};
985
986struct OpLcfsqnMva {
987 template <class T>
988 static Json run(Args& a) {
989 const std::vector<T> alpha = a.vector<T>("alpha");
990 const std::vector<T> beta = a.vector<T>("beta");
991 const std::vector<int> N = a.ints("N");
992 a.done();
993 const pfqn::LcfsMvaResult<T> r = pfqn::pfqn_lcfsqn_mva(alpha, beta, N);
994 Json out;
995 out["T"] = encode_vector(r.T_);
996 out["Q"] = encode_matrix(r.Q);
997 out["U"] = encode_matrix(r.U);
998 out["B"] = encode_matrix(r.B);
999 return out;
1000 }
1001};
1002
1003struct OpLcfsqnNc {
1004 template <class T>
1005 static Json run(Args& a) {
1006 const std::vector<T> alpha = a.vector<T>("alpha");
1007 const std::vector<T> beta = a.vector<T>("beta");
1008 const std::vector<int> N = a.ints("N");
1009 a.done();
1010 Json out;
1011 out["G"] = encode_scalar(pfqn::pfqn_lcfsqn_nc(alpha, beta, N));
1012 return out;
1013 }
1014};
1015
1016struct OpLdmxEc {
1017 template <class T>
1018 static Json run(Args& a) {
1019 const std::vector<T> lambda = a.vector<T>("lambda");
1020 const Matrix<T> D = a.matrix<T>("D");
1021 const Matrix<T> mu = a.matrix<T>("mu");
1022 a.done();
1023 const pfqn::LdmxEcResult<T> r = pfqn::pfqn_ldmx_ec(lambda, D, mu);
1024 Json out;
1025 out["EC"] = encode_matrix(r.EC);
1026 out["E"] = encode_matrix(r.E);
1027 out["Eprime"] = encode_matrix(r.Eprime);
1028 out["Lo"] = encode_vector(r.Lo);
1029 return out;
1030 }
1031};
1032
1033struct OpCbh {
1034 template <class T>
1035 static Json run(Args& a) {
1036 const std::vector<T> L = a.vector<T>("L");
1037 const int N = int_arg(a, "N");
1038 const T Z = a.number<T>("Z");
1039 const bool hasLevel = a.has("level");
1040 const int level = hasLevel ? int_arg(a, "level") : 0;
1041 a.done();
1042 const pfqn::CbhBounds<T> r = hasLevel ? pfqn::pfqn_cbh(L, N, Z, level) : pfqn::pfqn_cbh(L, N, Z);
1043 Json out;
1044 out["Xlo"] = encode_scalar(r.Xlo);
1045 out["Xhi"] = encode_scalar(r.Xhi);
1046 return out;
1047 }
1048};
1049
1050struct OpCub {
1051 template <class T>
1052 static Json run(Args& a) {
1053 const Matrix<T> L = a.matrix<T>("L");
1054 const std::vector<int> N = a.ints("N");
1055 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1056 const bool lf = long_form(a, "pfqn_cub", {"order", "atol"}, {"order", "atol"});
1057 const int order = lf ? int_arg(a, "order") : 0;
1058 const T atol = lf ? a.number<T>("atol") : num_traits<T>::from_int(0);
1059 a.done();
1060 const pfqn::CubResult<T> r = lf ? pfqn::pfqn_cub(L, N, Z, order, atol) : pfqn::pfqn_cub(L, N, Z);
1061 return g_lg(r.G, r.lG, "Gn", "lGn");
1062 }
1063};
1064
1065struct OpDac {
1066 template <class T>
1067 static Json run(Args& a) {
1068 const Matrix<T> L = a.matrix<T>("L");
1069 const std::vector<int> N = a.ints("N");
1070 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1071 const bool hasMu = a.has("mu");
1072 const Matrix<T> mu = a.matrix_or_empty<T>("mu");
1073 a.done();
1074 const pfqn::DacResult<T> r = hasMu ? pfqn::pfqn_dac(L, N, Z, mu) : pfqn::pfqn_dac(L, N, Z);
1075 Json out;
1076 out["Pjoint"] = encode_vector(r.Pjoint);
1077 Json st = Json::array();
1078 for (const std::vector<int>& s : r.states) st.push_back(encode_ints(s));
1079 out["states"] = st;
1080 out["XN"] = encode_vector(r.XN);
1081 out["QN"] = encode_matrix(r.QN);
1082 out["UN"] = encode_vector(r.UN);
1083 out["CN"] = encode_vector(r.CN);
1084 out["pi"] = encode_matrix(r.PI);
1085 return out;
1086 }
1087};
1088
1089struct OpFnc {
1090 template <class T>
1091 static Json run(Args& a) {
1092 const Matrix<T> alpha = a.matrix<T>("alpha");
1093 const bool hasC = a.has("c");
1094 const std::vector<T> c = a.vector_or_empty<T>("c");
1095 a.done();
1096 const pfqn::FncResult<T> r = hasC ? pfqn::pfqn_fnc(alpha, c) : pfqn::pfqn_fnc(alpha);
1097 Json out;
1098 out["mu"] = encode_matrix(r.mu);
1099 out["c"] = encode_vector(r.c);
1100 return out;
1101 }
1102};
1103
1104struct OpGrnmol {
1105 template <class T>
1106 static Json run(Args& a) {
1107 const Matrix<T> L = a.matrix<T>("L");
1108 const std::vector<int> N = a.ints("N");
1109 a.done();
1110 Json out;
1111 out["G"] = encode_scalar(pfqn::pfqn_grnmol(L, N));
1112 return out;
1113 }
1114};
1115
1116/** The (L, N[, Z]) shape of the asymptotic expansions, returning G and lG. */
1117#define LINE_PFQN_A_LNZ(Name, fn, gk, lk) \
1118 struct Name { \
1119 template <class T> \
1120 static Json run(Args& a) { \
1121 const Matrix<T> L = a.matrix<T>("L"); \
1122 const std::vector<T> N = a.vector<T>("N"); \
1123 const bool hasZ = a.has("Z"); \
1124 const std::vector<T> Z = a.vector_or_empty<T>("Z"); \
1125 a.done(); \
1126 const auto r = hasZ ? pfqn::fn(L, N, Z) : pfqn::fn(L, N); \
1127 return g_lg(r.G, r.lG, gk, lk); \
1128 } \
1129 };
1130
1131LINE_PFQN_A_LNZ(OpBkt, pfqn_bkt, "Gn", "lGn")
1132LINE_PFQN_A_LNZ(OpLe, pfqn_le, "Gn", "lGn")
1133LINE_PFQN_A_LNZ(OpBle, pfqn_ble, "Gn", "lGn")
1134#undef LINE_PFQN_A_LNZ
1135
1136struct OpKt {
1137 template <class T>
1138 static Json run(Args& a) {
1139 const Matrix<T> L = a.matrix<T>("L");
1140 const std::vector<T> N = a.vector<T>("N");
1141 const bool hasZ = a.has("Z");
1142 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1143 a.done();
1144 const pfqn::KtResult<T> r = hasZ ? pfqn::pfqn_kt(L, N, Z) : pfqn::pfqn_kt(L, N);
1145 Json out = g_lg(r.G, r.lG);
1146 out["X"] = encode_vector(r.X);
1147 out["Q"] = encode_matrix(r.Q);
1148 return out;
1149 }
1150};
1151
1152struct OpLekt {
1153 template <class T>
1154 static Json run(Args& a) {
1155 const Matrix<T> L = a.matrix<T>("L");
1156 const std::vector<T> N = a.vector<T>("N");
1157 const bool hasZ = a.has("Z");
1158 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1159 a.done();
1160 const pfqn::LektResult<T> r = hasZ ? pfqn::pfqn_lekt(L, N, Z) : pfqn::pfqn_lekt(L, N);
1161 Json out = g_lg(r.G, r.lG, "Gn", "lGn");
1162 out["route"] = r.route;
1163 return out;
1164 }
1165};
1166
1167struct OpAghq {
1168 template <class T>
1169 static Json run(Args& a) {
1170 const Matrix<T> L = a.matrix<T>("L");
1171 const std::vector<T> N = a.vector<T>("N");
1172 const std::size_t d = depth(a, "pfqn_aghq", {{"Z", true}, {"q", false}});
1173 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1174 const std::size_t q = a.has("q") ? a.count("q") : 0;
1175 a.done();
1176 pfqn::LeResult<T> r;
1177 if (d == 0)
1178 r = pfqn::pfqn_aghq(L, N);
1179 else if (d == 1)
1180 r = pfqn::pfqn_aghq(L, N, Z);
1181 else
1182 r = pfqn::pfqn_aghq(L, N, Z, q);
1183 return g_lg(r.G, r.lG, "Gn", "lGn");
1184 }
1185};
1186
1187struct OpLap {
1188 template <class T>
1189 static Json run(Args& a) {
1190 const std::vector<T> L = a.vector<T>("L");
1191 const std::vector<T> N = a.vector<T>("N");
1192 const std::vector<T> Z = a.vector<T>("Z");
1193 a.done();
1194 Json out;
1195 out["logI"] = encode_scalar(pfqn::pfqn_lap(L, N, Z));
1196 return out;
1197 }
1198};
1199
1200struct OpLdbcmp {
1201 template <class T>
1202 static Json run(Args& a) {
1203 const std::vector<T> L = a.vector<T>("L");
1204 const T N = a.number<T>("N");
1205 const T Z = a.number<T>("Z");
1206 const bool lf = long_form(a, "pfqn_ldbcmp", {"c", "tol"}, {"c", "tol"});
1207 const std::vector<T> c = a.vector_or_empty<T>("c");
1208 const T tol = lf ? a.number<T>("tol") : num_traits<T>::from_int(0);
1209 a.done();
1210 const pfqn::LdBcmpBound<T> r = lf ? pfqn::pfqn_ldbcmp(L, N, Z, c, tol) : pfqn::pfqn_ldbcmp(L, N, Z);
1211 Json out;
1212 out["Xlo"] = encode_scalar(r.Xlo);
1213 out["Rhi"] = encode_scalar(r.Rhi);
1214 out["Qhat"] = encode_scalar(r.Qhat);
1215 out["applicable"] = r.applicable;
1216 return out;
1217 }
1218};
1219
1220struct OpAbAmva {
1221 template <class T>
1222 static Json run(Args& a) {
1223 const Matrix<T> D = a.matrix<T>("D");
1224 const std::vector<int> N = a.ints("N");
1225 const Matrix<T> V = a.matrix<T>("V");
1226 const std::vector<int> nservers = a.ints("nservers");
1227 if (!a.has("sched")) (void)a.get("sched"); // required: the port has no default
1228 const std::vector<pfqn::SchedStrategy> sched = lin_sched(a, "sched");
1229 const bool lf = long_form(a, "pfqn_ab_amva", {"fcfsSchmidt", "marginalProbMethod"},
1230 {"fcfsSchmidt", "marginalProbMethod"});
1231 const bool schmidt = a.boolean("fcfsSchmidt", false);
1233 if (lf) {
1234 std::string m = a.text("marginalProbMethod", "");
1235 for (char& ch : m) ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
1236 if (m == "ab")
1238 else if (m == "scat")
1240 else
1241 throw InputError("pfqn_ab_amva: unknown marginalProbMethod '" + m +
1242 "'; expected \"ab\" or \"scat\"");
1243 }
1244 a.done();
1245 const pfqn::AbAmvaResult<T> r =
1246 lf ? pfqn::pfqn_ab_amva(D, N, V, nservers, sched, schmidt, method)
1247 : pfqn::pfqn_ab_amva(D, N, V, nservers, sched);
1248 Json out;
1249 out["QN"] = encode_matrix(r.QN);
1250 out["UN"] = encode_matrix(r.UN);
1251 out["RN"] = encode_matrix(r.RN);
1252 out["CN"] = encode_vector(r.CN);
1253 out["XN"] = encode_vector(r.XN);
1254 out["totiter"] = encode_count(r.totiter);
1255 return out;
1256 }
1257};
1258
1259struct OpCubEvals {
1260 template <class T>
1261 static Json run(Args& a) {
1262 const int M = int_arg(a, "M");
1263 const int order = int_arg(a, "order");
1264 const bool hasZ = a.has("Z");
1265 const Matrix<double> Z = a.matrix_or_empty<double>("Z");
1266 a.done();
1267 double Zsum = 0.0; // MATLAB reads sum(Z(:))
1268 for (std::size_t i = 0; i < Z.size(); ++i) Zsum += Z[i];
1269 Json out;
1270 out["nevals"] = hasZ ? pfqn::pfqn_cub_evals(M, order, Zsum) : pfqn::pfqn_cub_evals(M, order);
1271 return out;
1272 }
1273};
1274
1275struct OpDnc {
1276 template <class T>
1277 static Json run(Args& a) {
1278 const std::vector<T> L = a.vector<T>("L");
1279 const T N = a.number<T>("N");
1280 a.done();
1281 const pfqn::DncResult<T> r = pfqn::pfqn_dnc(L, N);
1282 Json out = g_lg(r.G, r.lG);
1283 out["X"] = encode_scalar(r.X);
1284 return out;
1285 }
1286};
1287
1288struct OpHst {
1289 template <class T>
1290 static Json run(Args& a) {
1291 const std::vector<T> L = a.vector<T>("L");
1292 const int N = int_arg(a, "N");
1293 const std::size_t d = depth(a, "pfqn_hst", {{"Z", false}, {"ist", false}});
1294 const T Z = a.has("Z") ? a.number<T>("Z") : num_traits<T>::from_int(0);
1295 const std::size_t ist = a.has("ist") ? a.count("ist") : 0;
1296 a.done();
1297 pfqn::HstResult<T> r;
1298 if (d == 0)
1299 r = pfqn::pfqn_hst(L, N);
1300 else if (d == 1)
1301 r = pfqn::pfqn_hst(L, N, Z);
1302 else
1303 r = pfqn::pfqn_hst(L, N, Z, ist);
1304 Json s;
1305 s["station"] = encode_count(r.station);
1306 s["X"] = encode_scalar(r.X);
1307 s["U"] = encode_scalar(r.U);
1308 s["Q"] = encode_scalar(r.Q);
1309 s["Pgeq"] = encode_vector(r.Pgeq);
1310 s["p"] = encode_vector(r.p);
1311 s["c"] = encode_vector(r.c);
1312 s["total"] = encode_scalar(r.total);
1313 s["worst"] = encode_scalar(r.worst);
1314 s["astar"] = encode_vector(r.astar);
1315 Json out;
1316 out["sens"] = s;
1317 return out;
1318 }
1319};
1320
1321pfqn::ClustInner clust_inner(Args& a) {
1322 std::string m = a.text("inner", "");
1323 for (char& c : m) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
1324 if (m == "lin") return pfqn::ClustInner::Linearizer;
1325 if (m == "bs") return pfqn::ClustInner::ProportionalEstimation;
1326 throw InputError("pfqn_clust: unknown inner method '" + m + "'; expected \"lin\" or \"bs\"");
1327}
1328
1329struct OpClust {
1330 template <class T>
1331 static Json run(Args& a) {
1332 const Matrix<T> L = a.matrix<T>("L");
1333 const std::vector<T> N = a.vector<T>("N");
1334 const bool hasZ = a.has("Z");
1335 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1336 const std::size_t d = depth(a, "pfqn_clust",
1337 {{"subnets", true}, {"localclasses", true}, {"inner", false},
1338 {"tol", false}, {"maxiter", false}});
1339 std::vector<std::vector<std::size_t> > subnets, localclasses;
1340 if (const Json* j = a.opt("subnets")) subnets = index_lists(*j, "'subnets'", false);
1341 if (const Json* j = a.opt("localclasses"))
1342 localclasses = index_lists(*j, "'localclasses'", false);
1343 const pfqn::ClustInner inner = a.has("inner") ? clust_inner(a) : pfqn::ClustInner::Linearizer;
1344 const double tol = a.has("tol") ? a.number<double>("tol") : 0.0;
1345 const std::size_t maxiter = a.has("maxiter") ? a.count("maxiter") : 0;
1346 a.done();
1347 pfqn::AmvaResult<T> r;
1348 if (d == 0)
1349 r = hasZ ? pfqn::pfqn_clust(L, N, Z) : pfqn::pfqn_clust(L, N);
1350 else if (d <= 2)
1351 r = pfqn::pfqn_clust(L, N, Z, subnets, localclasses);
1352 else if (d == 3)
1353 r = pfqn::pfqn_clust(L, N, Z, subnets, localclasses, inner);
1354 else if (d == 4)
1355 r = pfqn::pfqn_clust(L, N, Z, subnets, localclasses, inner, tol);
1356 else
1357 r = pfqn::pfqn_clust(L, N, Z, subnets, localclasses, inner, tol, maxiter);
1358 return amva_results(r, "it");
1359 }
1360};
1361
1362struct OpGerasimov {
1363 template <class T>
1364 static Json run(Args& a) {
1365 const Matrix<T> L = a.matrix<T>("L");
1366 const std::vector<int> N = a.ints("N");
1367 const bool hasZ = a.has("Z");
1368 const Matrix<T> Z = a.matrix_or_empty<T>("Z");
1369 const Json* oj = a.opt("options");
1370 a.done();
1371 pfqn::NcResult<T> r;
1372 if (!oj) {
1373 r = hasZ ? pfqn::pfqn_gerasimov(L, N, Z) : pfqn::pfqn_gerasimov(L, N);
1374 } else {
1375 Args o(*oj, "pfqn_gerasimov options");
1376 const std::size_t d =
1377 depth(o, "pfqn_gerasimov options", {{"tol", false}, {"maxterms", false}});
1378 const double tol = o.has("tol") ? o.number<double>("tol") : 0.0;
1379 const std::size_t maxterms = o.has("maxterms") ? o.count("maxterms") : 0;
1380 o.done();
1381 if (d == 0)
1382 r = pfqn::pfqn_gerasimov(L, N, Z);
1383 else if (d == 1)
1384 r = pfqn::pfqn_gerasimov(L, N, Z, tol);
1385 else
1386 r = pfqn::pfqn_gerasimov(L, N, Z, tol, maxterms);
1387 }
1388 return g_lg(r.G, r.lG);
1389 }
1390};
1391
1392struct OpLooping {
1393 template <class T>
1394 static Json run(Args& a) {
1395 const Matrix<T> L = a.matrix<T>("L");
1396 const std::vector<T> N = a.vector<T>("N");
1397 const std::size_t d =
1398 depth(a, "pfqn_looping", {{"Z", true}, {"tol", false}, {"maxiter", false}});
1399 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1400 const double tol = a.has("tol") ? a.number<double>("tol") : 0.0;
1401 const std::size_t maxiter = a.has("maxiter") ? a.count("maxiter") : 0;
1402 a.done();
1403 pfqn::LoopingBounds<T> r;
1404 if (d == 0)
1405 r = pfqn::pfqn_looping(L, N);
1406 else if (d == 1)
1407 r = pfqn::pfqn_looping(L, N, Z);
1408 else if (d == 2)
1409 r = pfqn::pfqn_looping(L, N, Z, tol);
1410 else
1411 r = pfqn::pfqn_looping(L, N, Z, tol, maxiter);
1412 Json out;
1413 out["Xlo"] = encode_vector(r.Xlo);
1414 out["Xup"] = encode_vector(r.Xup);
1415 out["QN"] = encode_matrix(r.Q);
1416 out["RN"] = encode_matrix(r.R);
1417 out["it"] = encode_count(r.iterations);
1418 out["converged"] = r.converged;
1419 return out;
1420 }
1421};
1422
1423std::vector<std::size_t> busy_orders(Args& a) {
1424 const std::vector<int> v = a.ints("n");
1425 std::vector<std::size_t> out;
1426 for (int x : v) {
1427 if (x < 0) throw InputError("'n': a busy period order cannot be negative");
1428 out.push_back(static_cast<std::size_t>(x));
1429 }
1430 return out;
1431}
1432
1433struct OpBusyp {
1434 static Json run(Args& a) {
1435 const std::vector<double> alpha = a.vector<double>("alpha");
1436 const Matrix<double> mu = a.matrix<double>("mu");
1437 const Matrix<double> P = a.matrix<double>("P");
1438 const double N = a.number<double>("N");
1439 const std::vector<std::size_t> subnet = indices(a, "subnet");
1440 const std::vector<std::size_t> n = busy_orders(a);
1441 const std::size_t d = depth(a, "pfqn_busyp", {{"gamma", true}, {"tol", false}});
1442 const std::vector<double> gamma = a.vector_or_empty<double>("gamma");
1443 const double tol = a.has("tol") ? a.number<double>("tol") : 0.0;
1444 a.done();
1445 pfqn::BusyPeriodResult r;
1446 if (d == 0)
1447 r = pfqn::pfqn_busyp(alpha, mu, P, N, subnet, n);
1448 else if (d == 1)
1449 r = pfqn::pfqn_busyp(alpha, mu, P, N, subnet, n, gamma);
1450 else
1451 r = pfqn::pfqn_busyp(alpha, mu, P, N, subnet, n, gamma, tol);
1452 Json out;
1453 out["b"] = encode_vector(r.b);
1454 out["lG"] = encode_vector(r.lG);
1455 out["lH"] = encode_vector(r.lH);
1456 return out;
1457 }
1458};
1459
1460struct OpBusypClw {
1461 static Json run(Args& a) {
1462 const Matrix<double> alpha = a.matrix<double>("alpha");
1463 const Matrix<double> mu = a.matrix<double>("mu");
1464 const std::vector<Matrix<double> > P = a.matrices<double>("P");
1465 const std::vector<double> N = a.vector<double>("N");
1466 const std::vector<std::size_t> subnet = indices(a, "subnet");
1467 const std::vector<std::size_t> n = busy_orders(a);
1468 const std::size_t d = depth(a, "pfqn_busyp_clw",
1469 {{"gamma", true}, {"isdelay", true}, {"method", false}});
1470 const Matrix<double> gamma = a.matrix_or_empty<double>("gamma");
1471 std::vector<bool> isdelay;
1472 if (const Json* j = a.opt("isdelay")) isdelay = bool_vector(*j, "'isdelay'");
1473 const std::string method = a.text("method", "");
1474 a.done();
1475 Json out;
1476 if (d == 0)
1477 out["b"] = encode_vector(pfqn::pfqn_busyp_clw(alpha, mu, P, N, subnet, n));
1478 else if (d == 1)
1479 out["b"] = encode_vector(pfqn::pfqn_busyp_clw(alpha, mu, P, N, subnet, n, gamma));
1480 else if (d == 2)
1481 out["b"] = encode_vector(pfqn::pfqn_busyp_clw(alpha, mu, P, N, subnet, n, gamma, isdelay));
1482 else
1483 out["b"] = encode_vector(
1484 pfqn::pfqn_busyp_clw(alpha, mu, P, N, subnet, n, gamma, isdelay, method));
1485 return out;
1486 }
1487};
1488
1489struct OpBusypMulticlass {
1490 static Json run(Args& a) {
1491 const Matrix<double> alpha = a.matrix<double>("alpha");
1492 const Matrix<double> mu = a.matrix<double>("mu");
1493 const std::vector<Matrix<double> > P = a.matrices<double>("P");
1494 const std::vector<double> N = a.vector<double>("N");
1495 const std::vector<std::size_t> subnet = indices(a, "subnet");
1496 const std::vector<std::size_t> n = busy_orders(a);
1497 const std::size_t d =
1498 depth(a, "pfqn_busyp_multiclass",
1499 {{"gamma", true}, {"phi", true}, {"tol", false}, {"jobclass", false}});
1500 const Matrix<double> gamma = a.matrix_or_empty<double>("gamma");
1501 const Matrix<double> phi = a.matrix_or_empty<double>("phi");
1502 const double tol = a.has("tol") ? a.number<double>("tol") : 0.0;
1503 const int jobclass = a.has("jobclass") ? int_arg(a, "jobclass") : 0;
1504 a.done();
1505 std::vector<double> b;
1506 if (d == 0)
1507 b = pfqn::pfqn_busyp_multiclass(alpha, mu, P, N, subnet, n);
1508 else if (d == 1)
1509 b = pfqn::pfqn_busyp_multiclass(alpha, mu, P, N, subnet, n, gamma);
1510 else if (d == 2)
1511 b = pfqn::pfqn_busyp_multiclass(alpha, mu, P, N, subnet, n, gamma, phi);
1512 else if (d == 3)
1513 b = pfqn::pfqn_busyp_multiclass(alpha, mu, P, N, subnet, n, gamma, phi, tol);
1514 else
1515 b = pfqn::pfqn_busyp_multiclass(alpha, mu, P, N, subnet, n, gamma, phi, tol, jobclass);
1516 Json out;
1517 out["b"] = encode_vector(b);
1518 return out;
1519 }
1520};
1521
1522struct OpBk {
1523 template <class T>
1524 static Json run(Args& a) {
1525 const Matrix<T> L = a.matrix<T>("L");
1526 const std::vector<T> N = a.vector<T>("N");
1527 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1528 a.done();
1529 const pfqn::BkResult<T> r = pfqn::pfqn_bk(L, N, Z);
1530 Json out = g_lg(r.G, r.lG);
1531 out["X"] = encode_vector(r.X);
1532 out["U"] = encode_matrix(r.U);
1533 out["A"] = encode_index_list(r.A);
1534 out["B"] = encode_index_list(r.B);
1535 return out;
1536 }
1537};
1538
1539struct OpClw {
1540 template <class T>
1541 static Json run(Args& a) {
1542 const Matrix<T> L = a.matrix<T>("L");
1543 const std::vector<int> N = a.ints("N");
1544 const std::vector<T> Z = a.vector<T>("Z");
1545 const bool hasM = a.has("m");
1546 std::vector<long> m;
1547 for (int x : a.ints_or_empty("m")) m.push_back(static_cast<long>(x));
1548 const Json* oj = a.opt("options");
1549 a.done();
1550 pfqn::ClwResult<T> r;
1551 if (oj) {
1552 // Every field the caller leaves out stays at the port's ClwOptions default.
1553 pfqn::ClwOptions opt;
1554 Args o(*oj, "pfqn_clw options");
1555 if (o.has("l")) opt.l = o.ints("l");
1556 if (o.has("gamma")) opt.gamma = o.vector<double>("gamma");
1557 if (o.has("euler")) opt.euler = o.boolean("euler", opt.euler);
1558 if (o.has("euler_n")) opt.eulerN = int_arg(o, "euler_n");
1559 if (o.has("euler_m")) opt.eulerM = int_arg(o, "euler_m");
1560 if (o.has("euler_tol")) opt.eulerTol = o.number<double>("euler_tol");
1561 if (o.has("euler_maxm")) opt.eulerMaxM = int_arg(o, "euler_maxm");
1562 if (o.has("dimred")) opt.dimred = o.boolean("dimred", opt.dimred);
1563 if (o.has("dimred_maxd")) opt.dimredMaxD = int_arg(o, "dimred_maxd");
1564 if (o.has("beta")) opt.beta = o.vector<double>("beta");
1565 o.done();
1566 r = pfqn::pfqn_clw(L, N, Z, m, opt);
1567 } else {
1568 r = hasM ? pfqn::pfqn_clw(L, N, Z, m) : pfqn::pfqn_clw(L, N, Z);
1569 }
1570 return g_lg(r.G, r.lG);
1571 }
1572};
1573
1574struct OpHarelBounds {
1575 template <class T>
1576 static Json run(Args& a) {
1577 const std::vector<T> rho = a.vector<T>("rho");
1578 const int N = int_arg(a, "N");
1579 const bool lf = a.has("Z") || a.has("maxUB");
1580 const T Z = a.has("Z") ? a.number<T>("Z") : num_traits<T>::from_int(0);
1581 // maxUB <= 0 is the port's documented "not given" (min(N,7)), as [] is in MATLAB.
1582 const int maxUB = a.has("maxUB") ? int_arg(a, "maxUB") : 0;
1583 a.done();
1584 if (lf && !a.has("Z"))
1585 throw InputError("pfqn_harel_bounds: 'maxUB' is given but 'Z' is not; the C++ port "
1586 "takes 'Z' positionally before 'maxUB'. Give 'Z' as well.");
1587 const pfqn::HarelBoundsResult<T> r =
1588 lf ? pfqn::pfqn_harel_bounds(rho, N, Z, maxUB) : pfqn::pfqn_harel_bounds(rho, N);
1589 // The port indexes UB and TH by population, leaving slot 0 unused; MATLAB's
1590 // UB(n) and TH(n) run over n = 1..maxUB, so slot 0 is dropped.
1591 const std::vector<T> UB(r.UB.begin() + 1, r.UB.end());
1592 const std::vector<T> TH(r.TH.begin() + 1, r.TH.end());
1593 Json out;
1594 out["LB"] = encode_scalar(r.LB);
1595 out["UB"] = encode_vector(UB);
1596 out["TH"] = encode_vector(TH);
1597 return out;
1598 }
1599};
1600
1601
1602// ---------------------------------------------------------------------------
1603// Seeded Monte Carlo estimators. Each port draws from a caller-owned
1604// pfqn::McRng; the JSON form names it by a required seed (required_seed).
1605// ---------------------------------------------------------------------------
1606
1607/**
1608 * pfqn_cftp(L, N, S, nsamples, method, seed): MATLAB's [Q, X, T]. S lists the
1609 * servers per station, Inf for a delay; method is 'cftp' (exact coupling from
1610 * the past) or 'approx'.
1611 */
1612struct OpCftp {
1613 template <class T>
1614 static Json run(Args& a) {
1615 const std::vector<T> L = a.vector<T>("L");
1616 const int N = int_arg(a, "N");
1617 std::vector<int> S;
1618 if (const Json* sj = a.opt("S")) {
1619 const std::vector<double> sd = vector_from_json<double>(*sj, "pfqn_cftp: S");
1620 for (double v : sd) {
1621 if (std::isinf(v) && v > 0) {
1622 S.push_back(pfqn::cftp_inf_servers);
1623 } else if (!(v >= 1) || v != std::floor(v)) {
1624 throw InputError("pfqn_cftp: S must hold positive integer server counts or "
1625 "Inf, got " + shortest_decimal(v));
1626 } else {
1627 S.push_back(static_cast<int>(v));
1628 }
1629 }
1630 }
1631 const std::size_t nsamples = a.count("nsamples", 1);
1632 const std::string m = a.text("method", "cftp");
1633 pfqn::CftpMethod method;
1634 if (m == "cftp") method = pfqn::CftpMethod::Cftp;
1635 else if (m == "approx") method = pfqn::CftpMethod::Approx;
1636 else throw InputError("pfqn_cftp: method must be 'cftp' or 'approx', got '" + m + "'");
1637 pfqn::McRng rng(required_seed(a, "pfqn_cftp"));
1638 a.done();
1639 const pfqn::CftpResult<T> r = pfqn::pfqn_cftp(L, N, S, nsamples, method, rng);
1640 Matrix<double> X(r.X.rows(), r.X.cols());
1641 for (std::size_t i = 0; i < X.rows(); ++i)
1642 for (std::size_t j = 0; j < X.cols(); ++j) X(i, j) = r.X(i, j);
1643 std::vector<double> horizon(r.horizon.begin(), r.horizon.end());
1644 Json out;
1645 out["Q"] = encode_vector(r.Q);
1646 out["X"] = encode_matrix(X);
1647 out["T"] = encode_vector(horizon);
1648 return out;
1649 }
1650};
1651
1652/**
1653 * The options object of pfqn_is and pfqn_ld_is: {samples (1e4), seed}. The
1654 * seed is required there as everywhere else in this family.
1655 */
1656struct IsOptions {
1657 std::size_t samples = 10000;
1658 std::uint64_t seed = 0;
1659};
1660
1661inline IsOptions read_is_options(Args& a, const std::string& fn) {
1662 const Json* oj = a.opt("options");
1663 if (!oj)
1664 throw InputError(fn + ": options.seed is required; the port draws from a seeded "
1665 "generator and has no session RNG to fall back on");
1666 Args op(*oj, fn + " options");
1667 IsOptions o;
1668 o.samples = op.count("samples", o.samples);
1669 o.seed = required_seed(op, fn, "options.seed");
1670 op.done();
1671 return o;
1672}
1673
1674/** pfqn_is(L, N, Z, options): importance-sampling estimate of [G, lG]. */
1675struct OpIs {
1676 template <class T>
1677 static Json run(Args& a) {
1678 const Matrix<T> L = a.matrix<T>("L");
1679 const std::vector<int> N = a.ints("N");
1680 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1681 const IsOptions o = read_is_options(a, "pfqn_is");
1682 a.done();
1683 pfqn::McRng rng(o.seed);
1684 const pfqn::NcResult<T> r = pfqn::pfqn_is(L, N, Z, o.samples, rng);
1685 return g_lg(r.G, r.lG);
1686 }
1687};
1688
1689/** pfqn_ld_is(L, N, Z, mu, options): the load-dependent estimator. */
1690struct OpLdIs {
1691 template <class T>
1692 static Json run(Args& a) {
1693 const Matrix<T> L = a.matrix<T>("L");
1694 const std::vector<int> N = a.ints("N");
1695 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1696 const Matrix<T> mu = a.matrix_or_empty<T>("mu");
1697 const IsOptions o = read_is_options(a, "pfqn_ld_is");
1698 a.done();
1699 pfqn::McRng rng(o.seed);
1700 const pfqn::NcResult<T> r = pfqn::pfqn_ld_is(L, N, Z, mu, o.samples, rng);
1701 return g_lg(r.G, r.lG);
1702 }
1703};
1704
1705/** pfqn_ls(L, N, Z, I, seed): logistic-sampling estimate, MATLAB's [Gn, lGn]. */
1706struct OpLs {
1707 template <class T>
1708 static Json run(Args& a) {
1709 const Matrix<T> L = a.matrix<T>("L");
1710 const std::vector<T> N = a.vector<T>("N");
1711 const std::vector<T> Z = a.vector_or_empty<T>("Z");
1712 const std::size_t I = a.count("I", 100000);
1713 pfqn::McRng rng(required_seed(a, "pfqn_ls"));
1714 a.done();
1715 const pfqn::LsResult<T> r = pfqn::pfqn_ls(L, N, Z, I, rng);
1716 return g_lg(r.G, r.lG, "Gn", "lGn");
1717 }
1718};
1719
1720} // namespace
1721
1723 // double only
1724 t["pfqn_cyclet_ofree"] = make<DoubleOnly<OpCycletOfree> >();
1725 t["pfqn_busyp"] = make<DoubleOnly<OpBusyp> >();
1726 t["pfqn_busyp_clw"] = make<DoubleOnly<OpBusypClw> >();
1727 t["pfqn_busyp_multiclass"] = make<DoubleOnly<OpBusypMulticlass> >();
1728 // double, exact and real
1729 t["pfqn_expand"] = make<OpExpand>();
1730 // The registry lists exact, but the port static_asserts transcendental arithmetic.
1731 t["pfqn_lldfun"] = make<NeedsTranscendental<OpLldfun> >();
1732 t["pfqn_comomrm_ld"] = make<OpComomrmLd>();
1733 t["pfqn_gldsingle"] = make<OpGldsingle>();
1734 t["pfqn_lld"] = make<OpLld>();
1735 t["pfqn_joint"] = make<OpJoint>();
1736 t["pfqn_lcfsqn_ca"] = make<OpLcfsqnCa>();
1737 t["pfqn_lcfsqn_mva"] = make<OpLcfsqnMva>();
1738 t["pfqn_lcfsqn_nc"] = make<OpLcfsqnNc>();
1739 t["pfqn_ldmx_ec"] = make<OpLdmxEc>();
1740 t["pfqn_lldsingle"] = make<OpLldsingle>();
1741 t["pfqn_cbh"] = make<OpCbh>();
1742 t["pfqn_dac"] = make<OpDac>();
1743 t["pfqn_fnc"] = make<OpFnc>();
1744 t["pfqn_grnmol"] = make<OpGrnmol>();
1745 t["pfqn_ldbcmp"] = make<OpLdbcmp>();
1746 t["pfqn_cub_evals"] = make<OpCubEvals>();
1747 t["pfqn_gerasimov"] = make<OpGerasimov>();
1748 t["pfqn_jointmarg"] = make<OpJointmarg>();
1749 // double and real
1750 t["pfqn_explicit"] = make<NeedsTranscendental<OpExplicit> >();
1751 t["pfqn_explicit_ld"] = make<NeedsTranscendental<OpExplicitLd> >();
1752 t["pfqn_divdiff_ld"] = make<NeedsTranscendental<OpDivdiffLd> >();
1753 t["pfqn_bs"] = make<NeedsTranscendental<OpBs> >();
1754 t["pfqn_cntol"] = make<NeedsTranscendental<OpCntol> >();
1755 t["pfqn_aql"] = make<NeedsTranscendental<OpAql> >();
1756 t["pfqn_linearizer"] = make<NeedsTranscendental<OpLinearizer> >();
1757 t["pfqn_linearizerms"] = make<NeedsTranscendental<OpLinearizerms> >();
1758 t["pfqn_linearizermx"] = make<NeedsTranscendental<OpLinearizermx> >();
1759 t["pfqn_gflinearizer"] = make<NeedsTranscendental<OpGflinearizer> >();
1760 t["pfqn_egflinearizer"] = make<NeedsTranscendental<OpEgflinearizer> >();
1761 t["pfqn_conwayms"] = make<NeedsTranscendental<OpConwayms> >();
1762 t["pfqn_cub"] = make<NeedsTranscendental<OpCub> >();
1763 t["pfqn_kt"] = make<NeedsTranscendental<OpKt> >();
1764 t["pfqn_bkt"] = make<NeedsTranscendental<OpBkt> >();
1765 t["pfqn_lekt"] = make<NeedsTranscendental<OpLekt> >();
1766 t["pfqn_lap"] = make<NeedsTranscendental<OpLap> >();
1767 t["pfqn_le"] = make<NeedsTranscendental<OpLe> >();
1768 t["pfqn_ble"] = make<NeedsTranscendental<OpBle> >();
1769 t["pfqn_aghq"] = make<NeedsTranscendental<OpAghq> >();
1770 t["pfqn_ab_amva"] = make<NeedsTranscendental<OpAbAmva> >();
1771 t["pfqn_dnc"] = make<NeedsTranscendental<OpDnc> >();
1772 t["pfqn_hst"] = make<NeedsTranscendental<OpHst> >();
1773 t["pfqn_chow"] = make<NeedsTranscendental<OpChow> >();
1774 t["pfqn_clust"] = make<NeedsTranscendental<OpClust> >();
1775 t["pfqn_dmlin"] = make<NeedsTranscendental<OpDmlin> >();
1776 t["pfqn_lcp"] = make<NeedsTranscendental<OpLcp> >();
1777 t["pfqn_looping"] = make<NeedsTranscendental<OpLooping> >();
1778 t["pfqn_bk"] = make<NeedsTranscendental<OpBk> >();
1779 t["pfqn_clw"] = make<NeedsTranscendental<OpClw> >();
1780 t["pfqn_harel_bounds"] = make<NeedsTranscendental<OpHarelBounds> >();
1781 t["pfqn_cftp"] = make<NeedsTranscendental<OpCftp> >();
1782 t["pfqn_is"] = make<NeedsTranscendental<OpIs> >();
1783 t["pfqn_ld_is"] = make<NeedsTranscendental<OpLdIs> >();
1784 t["pfqn_ls"] = make<NeedsTranscendental<OpLs> >();
1785}
1786
1788 const std::string callables =
1789 "its scaling argument is a vector of callables (one std::function per station), which "
1790 "has no JSON representation.";
1791 t["pfqn_cdfun"] = "pfqn_cdfun: " + callables;
1792 t["pfqn_jdfun"] = "pfqn_jdfun: " + callables;
1793 const std::string oirate =
1794 "its rate argument mu is a vector of OiRate callables (std::function of the occupancy "
1795 "vector), which has no JSON representation.";
1796 t["pfqn_clwjd"] = "pfqn_clwjd: " + oirate;
1797 t["pfqn_clwoi"] = "pfqn_clwoi: " + oirate;
1798}
1799
1800} // namespace reg
1801} // namespace line
Internal to the –api dispatcher: what a per-domain translation unit needs to add entries to the table...
#define LINE_PFQN_A_LNZ(Name, fn, gk, lk)
The (L, N[, Z]) shape of the asymptotic expansions, returning G and lG.
InputError(const std::string &what)
Definition error.h:105
UnsupportedError(const std::string &what)
Definition error.h:119
Named-argument reader over the parsed JSON object.
Definition api_json.h:391
DacResult< T > pfqn_dac(const Matrix< T > &L, const std::vector< int > &N, const std::vector< T > &Z, const Matrix< T > &mu)
Distribution Analysis by Chain (de Souza e Silva, UCLA CSD-870023, 1987): the JOINT queue-length dist...
Definition pfqn_dac.h:118
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
BusyPeriodResult pfqn_busyp(const std::vector< double > &alpha, const RateSource &mu, const Matrix< T > &P, double N, const std::vector< std::size_t > &subnet, const std::vector< std::size_t > &n, const std::vector< double > &gamma={}, double tol=PFQN_BUSYP_DEFAULT_TOL)
Mean busy period of order n for the subnetwork.
Definition pfqn_busyp.h:186
std::mt19937_64 McRng
The generator type every Monte Carlo entry point in this tree accepts.
AghqResult< T > pfqn_aghq(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, std::size_t q)
Definition pfqn_aghq.h:59
AmvaResult< T > pfqn_clust(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const std::vector< std::vector< std::size_t > > &subnets, const std::vector< std::vector< std::size_t > > &localclasses, ClustInner inner=ClustInner::Linearizer, double tol=1e-6, std::size_t maxiter=1000)
de Souza e Silva-Lavenberg-Muntz Clustering Approximation (CA).
Definition pfqn_clust.h:214
T pfqn_grnmol(const Matrix< T > &L, const std::vector< int > &N)
Normalizing constant by the closed-form Grundmann-Moeller rule.
Definition pfqn_grnmol.h:82
NcResult< T > pfqn_gldsingle(const Matrix< T > &L, int N, const Matrix< T > &mu)
Exact normalizing constant of a SINGLE-CLASS closed network whose stations are load dependent.
LinearizerResult< T > pfqn_gflinearizer(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< SchedStrategy > &type, double tol, int maxiter, const T &alpha, const Matrix< T > &QN0)
Generalized fixed-point Linearizer with a single scaling exponent shared by every class (De Souza e S...
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...
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.
Definition pfqn_lekt.h:87
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.
Definition pfqn_bk.h:162
LinearizerMxMethod
Which Linearizer variant solves the closed subnetwork.
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.
Definition pfqn_ls.h:118
LinearizerResult< T > pfqn_conwayms(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &nservers, const std::vector< SchedStrategy > &type, double tol, int maxiter, const Matrix< T > &QN0)
Conway's multiserver Linearizer for chain-dependent FCFS queues (Conway 1989, "Fast Approximate Solut...
std::vector< double > pfqn_busyp_clw(const Matrix< T > &alpha, const Matrix< T > &mu, const std::vector< Matrix< T > > &P, const std::vector< double > &N, const std::vector< std::size_t > &subnet, const std::vector< std::size_t > &n, const Matrix< T > &gamma=Matrix< T >(), const std::vector< bool > &isdelay=std::vector< bool >(), const std::string &method="clw")
Mean busy period of order n for the subnetwork, via NC point evaluations.
LinearizerResult< T > pfqn_dmlin(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< SchedStrategy > &type, double tol, int maxiter, const Matrix< T > &QN0, int npasses=3)
de Souza e Silva-Muntz Improved Linearizer (IL).
Definition pfqn_dmlin.h:132
LdmxEcResult< T > pfqn_ldmx_ec(const std::vector< T > &lambda, const Matrix< T > &D, const Matrix< T > &mu)
Bruell-Balbo-Afshari effective-capacity terms for a MIXED open/closed network with limited load depen...
ChowVariant
Legacy compatibility selector; JMT-compatible Chow ignores it.
Definition pfqn_chow.h:27
std::vector< T > pfqn_lldfun(const std::vector< T > &n, const Matrix< T > &lldscaling, const std::vector< double > &nservers)
AMVA-QD limited-load-dependence function.
Definition pfqn_lldfun.h:81
constexpr int cftp_inf_servers
Sentinel for an infinite-server (delay) station, the reference's S = Inf.
Definition pfqn_cftp.h:67
HarelBoundsResult< T > pfqn_harel_bounds(const std::vector< T > &rho, int N, const T &Z, int maxUB)
Both bounds, plus the exact throughputs the upper bounds extrapolate from.
HstResult< T > pfqn_hst(const std::vector< T > &L, int N, const T &Z, std::size_t ist)
Operational sensitivity of throughput to homogeneous-service-time (HST) violations,...
Definition pfqn_hst.h:86
ExpandResult< T > pfqn_expand(const Matrix< T > &QN, const Matrix< T > &UN, const Matrix< T > &CN, const std::vector< std::size_t > &mapping)
Expand per-station metrics from a reduced model back to the original station set.
Definition pfqn_expand.h:52
T pfqn_lcfsqn_nc(const std::vector< T > &alpha, const std::vector< T > &beta, const std::vector< int > &N)
Normalizing constant of the two-station multiclass LCFS network as a sum of PERMANENTS,...
AmvaResult< T > pfqn_chow(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const std::vector< AmvaSched > &type, double tol=1e-6, std::size_t maxiter=1000, const Matrix< T > &QN0=Matrix< T >(), ChowVariant variant=ChowVariant::Forward)
JMT-compatible Chow approximate MVA.
Definition pfqn_chow.h:42
LldResult< T > pfqn_lld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &mu)
Normalizing constant of a multiclass LIMITED load-dependent closed model.
Definition pfqn_lld.h:72
constexpr int kOpenClass
Population sentinel marking an open class, standing in for MATLAB's Inf.
T pfqn_jointmarg(const std::vector< int > &n, const Matrix< T > &L, const std::vector< int > &N, const std::vector< std::size_t > &infset, const T &G, const std::string &engine="exact", std::uint64_t seed=0)
Joint probability of the per-station TOTAL queue lengths.
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 ...
Definition pfqn_clw.h:608
ClustInner
Which algorithm runs inside a subnetwork.
Definition pfqn_clust.h:61
DncResult< T > pfqn_dnc(const std::vector< T > &L, const T &N)
Distinct-load Normalizing Constant (DNC) at a nonintegral population.
Definition pfqn_dnc.h:103
LinearizerResult< T > pfqn_linearizermx(const std::vector< T > &lambda, const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &nservers, const std::vector< SchedStrategy > &type, double tol, int maxiter, LinearizerMxMethod method, const Matrix< T > &QN0)
Linearizer for mixed open/closed queueing networks.
LdBcmpBound< T > pfqn_ldbcmp(const std::vector< T > &L, const T &N, const T &Z, const std::vector< T > &c, const T &tol)
Anselmi-Cremonesi (2008) lower throughput bound for a closed single-class BCMP network with load-depe...
Definition pfqn_ldbcmp.h:64
AmvaResult< T > pfqn_bs(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const std::vector< AmvaSched > &type, double tol=1e-6, std::size_t maxiter=1000, const Matrix< T > &QN0=Matrix< T >())
Bard-Schweitzer approximate MVA.
Definition pfqn_bs.h:73
AbAmvaResult< T > pfqn_ab_amva(const Matrix< T > &S, const std::vector< int > &N, const Matrix< T > &v, const std::vector< int > &nservers, const std::vector< SchedStrategy > &sched, bool fcfsSchmidt, AbMarginalMethod method)
Akyildiz-Bolch approximate MVA for multi-server BCMP networks.
LoopingBounds< T > pfqn_looping(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, double tol=1e-6, std::size_t maxiter=1000)
Eager Looping bounds for closed multiclass product-form networks.
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.
Definition pfqn_kt.h:172
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.
AmvaResult< T > pfqn_lcp(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const std::vector< AmvaSched > &type, double tol=1e-6, std::size_t maxiter=1000, const Matrix< T > &QN0=Matrix< T >())
Bard Large Customer Population (LCP) approximate MVA.
Definition pfqn_lcp.h:57
AmvaResult< T > pfqn_aql(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, double tol=1e-7, std::size_t maxiter=1000)
Aggregate Queue Length (AQL) approximate MVA.
Definition pfqn_aql.h:51
FncResult< T > pfqn_fnc(const Matrix< T > &alpha)
Automatic offset search (the one-argument MATLAB branch).
Definition pfqn_fnc.h:151
LinearizerResult< T > pfqn_egflinearizer(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< SchedStrategy > &type, double tol, int maxiter, const std::vector< T > &alpha, const Matrix< T > &QN0, int npasses=3)
Extended generalized fixed-point Linearizer (De Souza e Silva and Muntz's generalization of Chandy an...
LinearizerResult< T > pfqn_linearizerms(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &nservers, const std::vector< SchedStrategy > &type, double tol, int maxiter, const Matrix< T > &QN0)
Multiserver Linearizer (Krzesinski's Linearizer as described in Conway 1989, with De Souza e Silva an...
LinearizerResult< T > pfqn_linearizer(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< SchedStrategy > &type, double tol, int maxiter, const Matrix< T > &QN0)
Chandy-Neuse Linearizer for single-server stations.
CbhBounds< T > pfqn_cbh(const std::vector< T > &L, int N, const T &Z, int level)
Convolutional Bound Hierarchy (Dowdy, Eager, Gordon and Saxton 1984) on the throughput of a single-cl...
Definition pfqn_cbh.h:118
T pfqn_lap(const std::vector< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Laplace approximation of the normalizing constant of a repairman (single-queue, multiclass) model.
Definition pfqn_lap.h:69
std::vector< double > pfqn_busyp_multiclass(const Matrix< T > &alpha, const Matrix< T > &mu, const std::vector< Matrix< T > > &P, const std::vector< double > &N, const std::vector< std::size_t > &subnet, const std::vector< std::size_t > &n, const Matrix< T > &gamma=Matrix< T >(), const Matrix< T > &phi=Matrix< T >(), double tol=PFQN_BUSYP_DEFAULT_TOL, int jobclass=-1)
Mean busy period of order n for the subnetwork, multichain.
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...
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...
Definition pfqn_is.h:50
DivdiffLdResult< T > pfqn_divdiff_ld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu)
Exact load-dependent normalizing constant by divided differences of a single-class constant.
CftpMethod
Which sampler to run.
Definition pfqn_cftp.h:70
@ Cftp
exact, monotone coupling from the past
Definition pfqn_cftp.h:71
@ Approx
the rapidly-mixing approximate sampler M_A
Definition pfqn_cftp.h:72
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.
Definition pfqn_cub.h:131
LcfsQnResult< T > pfqn_lcfsqn_ca(const std::vector< T > &alpha, const std::vector< T > &beta, const std::vector< int > &N)
Convolution algorithm for the two-station multiclass LCFS queueing network of Casale,...
CftpResult< T > pfqn_cftp(const std::vector< T > &L, int N, const std::vector< int > &S, std::size_t nsamples, CftpMethod method, McRng &rng)
Perfect stationary state sampling for closed single-class multiserver product-form networks,...
Definition pfqn_cftp.h:144
double pfqn_cntol(const std::vector< T > &N)
Termination cutoff at the given population vector.
Definition pfqn_cntol.h:58
CycletResult pfqn_cyclet_ofree(const std::vector< double > &v, const std::vector< double > &mu, std::size_t N, const std::vector< std::vector< std::size_t > > &paths, const std::vector< double > &tset, const std::string &method="auto", std::size_t nmom=3, const std::vector< double > &pathprob={}, const std::string &lti_method="euler", double tol=1e-8)
Exact passage-time density, CDF and moments along the overtake-free paths paths, mixed by pathprob.
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.
LcfsMvaResult< T > pfqn_lcfsqn_mva(const std::vector< T > &alpha, const std::vector< T > &beta, const std::vector< int > &N)
Exact mean value analysis of the two-station multiclass LCFS network of Casale, QUESTA 2026 (station ...
AbMarginalMethod
Which marginal-probability rule the multiserver correction uses.
@ Ab
the Akyildiz-Bolch weight function
@ Scat
two-point scatter around floor(Qtot)
nlohmann::json Json
Definition api_json.h:63
std::uint64_t required_seed(Args &a, const std::string &fn, const char *spelled="seed")
The seed of a function whose port draws from a caller-owned generator.
std::vector< int > int_vector_from_json(const Json &j, const std::string &where)
An integer vector; a non-integral entry is an error, never a truncation.
Definition api_json.h:267
void register_library_only_pfqn_a(LibraryOnlyTable &t)
T number_from_json(const Json &j, const std::string &where)
Definition api_json.h:210
std::vector< T > vector_from_json(const Json &j, const std::string &where)
A flat numeric vector: 1-D array, or a 1-row / 1-column 2-D array.
Definition api_json.h:255
Json encode_vector(const std::vector< T > &v)
Definition api_json.h:333
Json encode_matrix(const Matrix< T > &m)
Definition api_json.h:340
std::string shortest_decimal(double v)
The shortest decimal literal that round-trips to v.
Definition api_json.h:76
Json encode_scalar(const T &v)
Definition api_json.h:328
Json encode_count(std::size_t n)
A count.
Definition api_json.h:378
std::map< std::string, std::string > LibraryOnlyTable
name -> why the function has no named-JSON-argument form.
Json encode_ints(const std::vector< int > &v)
Definition api_json.h:350
std::map< std::string, Invoker > DispatchTable
void register_api_pfqn_a(DispatchTable &t)
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Akyildiz-Bolch approximate MVA for multi-server BCMP networks.
Adaptive Gauss-Hermite quadrature of the simplex factor of the McKenna-Mitra integral.
Aggregate Queue Length (AQL) approximate MVA.
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).
Bard-Schweitzer approximate MVA.
Mean busy period of order n for a subnetwork of a product-form network.
Busy period of a subnetwork from point evaluations of the normalizing constant.
Multichain generalization of pfqn_busyp.
Convolutional Bound Hierarchy (Dowdy, Eager, Gordon and Saxton 1984) on the throughput of a single-cl...
Perfect stationary state sampling for closed single-class multiserver product-form networks,...
JMT-compatible Chow approximate MVA.
de Souza e Silva-Lavenberg-Muntz Clustering Approximation (CA).
Choudhury-Leung-Whitt normalization constant by numerical inversion of the generating function (JACM ...
Chandy-Neuse population-scaled termination cutoff for approximate MVA.
CoMoM for the repairman model with an arbitrary LOAD-DEPENDENT rate lattice at the single queueing st...
Conway's multiserver Linearizer for chain-dependent FCFS queues (Conway 1989, "Fast Approximate Solut...
Normalizing constant by Grundmann-Moeller cubature over the simplex.
Integrand-evaluation count of pfqn_cub, and the budget pfqn_nc prices it against.
Exact passage-time law along an OVERTAKE-FREE PATH of a closed single-chain tree-like product-form ne...
Distribution Analysis by Chain (de Souza e Silva, UCLA CSD-870023, 1987): the JOINT queue-length dist...
Exact load-dependent normalizing constant by divided differences of a single-class constant.
de Souza e Silva-Muntz Improved Linearizer (IL).
Distinct-load Normalizing Constant (DNC) at a nonintegral population.
Extended generalized fixed-point Linearizer (De Souza e Silva and Muntz's generalization of Chandy an...
Expand per-station metrics from a reduced model back to the original station set.
Explicit closed-form normalizing constant of a multiclass closed network.
Explicit closed-form normalizing constant of a multiclass LIMITED LOAD-DEPENDENT network.
Load-dependent rates of the functional server f(n) = n + c.
Gerasimov's residue (closed-form) normalizing constant, generalized to R classes.
Generalized fixed-point Linearizer with a single scaling exponent shared by every class (De Souza e S...
Exact normalizing constant of a SINGLE-CLASS closed network whose stations are load dependent.
Normalizing constant by the closed-form Grundmann-Moeller rule.
Harel-Namn-Sturm throughput bounds for a single-class closed network.
Operational sensitivity of throughput to homogeneous-service-time (HST) violations,...
Importance-sampling estimate of the normalizing constant of a closed LOAD-INDEPENDENT product-form ne...
Joint queue-length probability of a closed product-form network.
Joint probability of the per-station TOTAL queue lengths.
Knessl-Tier asymptotic expansion of the normalizing constant.
Laplace approximation of the normalizing constant of a repairman (single-queue, multiclass) model.
Convolution algorithm for the two-station multiclass LCFS queueing network of Casale,...
Exact mean value analysis of the two-station multiclass LCFS network of Casale, QUESTA 2026 (station ...
Normalizing constant of the two-station multiclass LCFS network as a sum of PERMANENTS,...
Bard Large Customer Population (LCP) approximate MVA.
Importance-sampling estimate of the normalizing constant of a closed LOAD-DEPENDENT product-form netw...
Anselmi-Cremonesi (2008) lower throughput bound for a closed single-class BCMP network with load-depe...
Bruell-Balbo-Afshari effective-capacity terms for a MIXED open/closed network with limited load depen...
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.
Chandy-Neuse Linearizer for single-server stations.
Multiserver Linearizer (Krzesinski's Linearizer as described in Conway 1989, with De Souza e Silva an...
Linearizer for mixed open/closed queueing networks.
Normalizing constant of a multiclass LIMITED load-dependent closed model.
AMVA-QD limited-load-dependence function.
Exact normalizing constant of a SINGLE-CLASS closed network whose stations are LIMITED load dependent...
Eager Looping bounds for closed multiclass product-form networks.
Logistic-sampling estimate of the normalizing constant of a closed product-form network.