LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
api_dispatch_qsys.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 'qsys' group (see api_dispatch_detail.h):
6 * the remaining closed-form queueing systems, the fork-join (fj) family, the
7 * age-of-information transforms, the moment conversions and the simulation
8 * planning helpers.
9 *
10 * CONVENTIONS, shared with api_dispatch.cpp:
11 * - argument keys are the MATLAB parameter names, output keys the MATLAB
12 * output names (struct fields where MATLAB returns a struct);
13 * - an absent optional argument calls the port's shorter overload, or leaves
14 * the port's own default in place; no default is restated here. Where the
15 * port takes its optional arguments POSITIONALLY, a later one cannot be
16 * given without the earlier ones (prefix_count), and where it takes a group
17 * of tuning knobs only together, the group is all-or-none (all_or_none);
18 * - a function handle cannot cross JSON. Where the port offers a constructor
19 * for a NAMED law, the handle is replaced by that law (a model.json
20 * distribution record, or the aoi_lst_* constructors); otherwise the
21 * argument is refused by name, and a function whose handle is REQUIRED is
22 * listed as library-only below.
23 */
24
25#include "api_dispatch_detail.h"
26
27#include <cmath>
28#include <complex>
29#include <initializer_list>
30#include <string>
31#include <vector>
32
38#include "line/api/fj/fj_amva.h"
50#include "line/api/fj/fj_qgb.h"
67// qsys_bmapm1.h names lang::GlobalConstants without including the header that
68// declares it, so lang_types.h must come first.
113
114namespace line {
115namespace reg {
116
117namespace {
118
119// ---------------------------------------------------------------------------
120// Shared readers
121// ---------------------------------------------------------------------------
122
123/**
124 * How many of the port's POSITIONAL optional arguments the caller gave. They
125 * must form a prefix: naming a later one without the earlier would make this
126 * boundary restate the earlier defaults, which it does not do.
127 */
128std::size_t prefix_count(const Args& a, std::initializer_list<const char*> keys,
129 const std::string& fn) {
130 std::size_t n = 0;
131 bool gap = false;
132 const char* missing = nullptr;
133 for (const char* k : keys) {
134 if (a.has(k)) {
135 if (gap)
136 throw InputError(fn + ": '" + k + "' follows '" + missing +
137 "' positionally in the port; give '" + missing + "' as well");
138 ++n;
139 } else if (!gap) {
140 gap = true;
141 missing = k;
142 }
143 }
144 return n;
145}
146
147/** A group of tuning knobs the port takes only together: all of them or none. */
148bool all_or_none(const Args& a, std::initializer_list<const char*> keys, const std::string& fn) {
149 std::size_t n = 0;
150 std::string names;
151 for (const char* k : keys) {
152 if (a.has(k)) ++n;
153 if (!names.empty()) names += ", ";
154 names += std::string("'") + k + "'";
155 }
156 if (n != 0 && n != keys.size())
157 throw InputError(fn + ": " + names + " tune one computation together; give all of them "
158 "or none");
159 return n != 0;
160}
161
162std::vector<unsigned> read_uints(Args& a, const char* key, const std::string& fn) {
163 const std::vector<int> v = a.ints(key);
164 std::vector<unsigned> out;
165 for (std::size_t i = 0; i < v.size(); ++i) {
166 if (v[i] < 0)
167 throw InputError(fn + ": '" + key + "' holds counts and cannot be negative");
168 out.push_back(static_cast<unsigned>(v[i]));
169 }
170 return out;
171}
172
173/** A required flag, which Args::boolean only offers with a fallback. */
174bool required_flag(Args& a, const char* key, const std::string& fn) {
175 if (!a.has(key)) throw InputError(fn + ": missing required argument '" + key + "'");
176 return a.boolean(key, false);
177}
178
179/** A MAP as the (D0, D1) pair the reference passes around, under any two keys. */
180template <class T>
181mam::Map<T> read_map(Args& a, const char* d0, const char* d1) {
182 mam::Map<T> m;
183 m.D0 = a.matrix<T>(d0);
184 m.D1 = a.matrix<T>(d1);
185 return m;
186}
187
188/** A MAP given as MATLAB's cell {D0, D1}: a two-element list of matrices. */
189template <class T>
190mam::Map<T> map_from_pair(const Json& j, const std::string& where) {
191 if (!j.is_array() || j.size() != 2)
192 throw InputError(where + ": expected the MAP as a list [D0, D1] of two matrices");
193 mam::Map<T> m;
194 m.D0 = matrix_from_json<T>(j[0], where + "[0]");
195 m.D1 = matrix_from_json<T>(j[1], where + "[1]");
196 return m;
197}
198
199/** A list of MAPs, each a [D0, D1] pair. */
200template <class T>
201std::vector<mam::Map<T> > maps_from_json(const Json& j, const std::string& where) {
202 if (!j.is_array()) throw InputError(where + ": expected a list of [D0, D1] pairs");
203 std::vector<mam::Map<T> > out;
204 for (std::size_t i = 0; i < j.size(); ++i)
205 out.push_back(map_from_pair<T>(j[i], where + "[" + std::to_string(i) + "]"));
206 return out;
207}
208
209/**
210 * A distribution given as its model.json record, e.g. {"type":"Exp",
211 * "params":{"lambda":2}}. The record reader parses its numbers through double,
212 * so at --arith real a law is as precise as a double literal, and a malformed
213 * record is reported as the caller's input rather than as a JSON library fault.
214 */
215template <class T>
216lang::Distrib<T> dist_record(const Json& j, const std::string& where) {
217 if (!j.is_object() || !j.contains("type"))
218 throw InputError(where + ": expected a distribution record {\"type\": ..., ...} as "
219 "written to model.json");
220 try {
221 return io::detail::dist_from_json<T>(j);
222 } catch (const Json::exception& e) {
223 throw InputError(where + ": malformed distribution record: " + e.what());
224 }
225}
226
227/**
228 * The service law of the MAP/G/1/K family, MATLAB's svc struct: {"type":"det",
229 * "d"}, {"type":"gamma","alpha","theta"} or {"type":"ph","alpha","T"}. The
230 * 'density' type carries a function handle and has no JSON form.
231 */
232template <class T>
233qsys::ServiceLaw<T> read_svc(Args& a, const std::string& fn) {
234 Args sv(a.get("svc"), fn + ": svc");
235 const std::string type = sv.text("type", "");
236 qsys::ServiceLaw<T> svc;
237 if (type == "det") {
238 svc = qsys::ServiceLaw<T>::deterministic(sv.number<T>("d"));
239 } else if (type == "gamma") {
240 const T al = sv.number<T>("alpha");
241 svc = qsys::ServiceLaw<T>::gamma(al, sv.number<T>("theta"));
242 } else if (type == "ph") {
243 const std::vector<T> al = sv.vector<T>("alpha");
244 svc = qsys::ServiceLaw<T>::phase_type(al, sv.matrix<T>("T"));
245 } else if (type == "density") {
246 throw UnsupportedError(ErrorKind{"Api"},
247 fn + ": a 'density' service law is a function handle and has no named-JSON form; "
248 "use 'det', 'gamma' or 'ph', or call it from the library");
249 } else {
250 throw UnsupportedError(ErrorKind{"Distribution"},
251 fn + ": svc.type must be 'det', 'gamma' or 'ph', got '" + type + "'");
252 }
253 sv.done();
254 return svc;
255}
256
257/** A vector output that MATLAB reshapes to the scalar it was given. */
258template <class T>
259Json encode_shaped(const std::vector<T>& v, bool scalar) {
260 if (scalar && v.size() == 1) return encode_scalar(v[0]);
261 return encode_vector(v);
262}
263
264/** The MATLAB message every exact refusal of the sim family shares. */
265std::string sim_exact_refusal(const char* fn, const char* why) {
266 return std::string(fn) + ": " + why +
267 ", so exact rational arithmetic has nothing to preserve and is refused; rerun with "
268 "--arith double or --arith real";
269}
270
271// ---------------------------------------------------------------------------
272// Moment tensors: nested JSON arrays, outer index = dimension 1
273// ---------------------------------------------------------------------------
274
275template <class T>
276void tensor_fill(const Json& node, std::size_t depth, std::size_t offset,
277 const std::vector<std::size_t>& sz, const std::vector<std::size_t>& stride,
278 std::vector<T>& data, const std::string& where) {
279 if (depth == sz.size()) {
280 if (node.is_array())
281 throw InputError(where + ": the tensor is nested deeper than its first element");
282 data[offset] = number_from_json<T>(node, where);
283 return;
284 }
285 if (!node.is_array() || node.size() != sz[depth])
286 throw InputError(where + ": ragged tensor, dimension " + std::to_string(depth + 1) +
287 " must have " + std::to_string(sz[depth]) + " entries throughout");
288 for (std::size_t i = 0; i < sz[depth]; ++i)
289 tensor_fill<T>(node[i], depth + 1, offset + i * stride[depth], sz, stride, data, where);
290}
291
292/**
293 * A moment tensor from nested arrays: the nesting depth is the order and the
294 * outermost index runs over dimension 1, so A[i][j] is MATLAB's A(i,j). A flat
295 * array is an order-1 tensor, a bare number a one-entry one.
296 */
297template <class T>
298moment::MomentTensor<T> tensor_from_json(const Json& j, const std::string& where) {
299 std::vector<std::size_t> sz;
300 if (!j.is_array()) {
301 sz.push_back(1);
302 } else {
303 const Json* p = &j;
304 while (p->is_array()) {
305 sz.push_back(p->size());
306 if (p->empty()) break;
307 p = &(*p)[0];
308 }
309 }
310 std::vector<std::size_t> stride(sz.size(), 1);
311 for (std::size_t l = 1; l < sz.size(); ++l) stride[l] = stride[l - 1] * sz[l - 1];
312 moment::MomentTensor<T> t(sz);
313 if (!j.is_array())
314 t.data[0] = number_from_json<T>(j, where);
315 else if (t.numel() > 0)
316 tensor_fill<T>(j, 0, 0, sz, stride, t.data, where);
317 return t;
318}
319
320template <class T>
321Json tensor_nest(const moment::MomentTensor<T>& t, std::size_t depth, std::size_t offset) {
322 Json out = Json::array();
323 const std::size_t st = t.stride(depth);
324 for (std::size_t i = 0; i < t.sz[depth]; ++i) {
325 if (depth + 1 == t.order())
326 out.push_back(encode_scalar(t.data[offset + i * st]));
327 else
328 out.push_back(tensor_nest(t, depth + 1, offset + i * st));
329 }
330 return out;
331}
332
333/** The inverse of tensor_from_json; an order-1 tensor is a flat array. */
334template <class T>
335Json encode_tensor(const moment::MomentTensor<T>& t) {
336 return tensor_nest(t, 0, 0);
337}
338
339Json encode_sizes(const std::vector<std::size_t>& v) {
340 Json out = Json::array();
341 for (std::size_t i = 0; i < v.size(); ++i) out.push_back(encode_count(v[i]));
342 return out;
343}
344
345// ---------------------------------------------------------------------------
346// moment: one-dimensional sequence conversions, vector in and vector out
347// ---------------------------------------------------------------------------
348
349#define LINE_MOMENT_VEC_OP(OPNAME, FN, IN, OUT) \
350 struct OPNAME { \
351 template <class T> \
352 static Json run(Args& a) { \
353 const std::vector<T> x = a.vector<T>(IN); \
354 a.done(); \
355 Json out; \
356 out[OUT] = encode_vector(moment::FN<T>(x)); \
357 return out; \
358 } \
359 };
360
361LINE_MOMENT_VEC_OP(OpMomentCumulantFromRaw, moment_cumulant_from_raw, "m", "kappa")
362LINE_MOMENT_VEC_OP(OpMomentRawFromCumulant, moment_raw_from_cumulant, "kappa", "m")
363LINE_MOMENT_VEC_OP(OpMomentFactcumulantFromFactorial, moment_factcumulant_from_factorial, "f",
364 "kappa")
365LINE_MOMENT_VEC_OP(OpMomentFactorialFromFactcumulant, moment_factorial_from_factcumulant,
366 "kappa", "f")
367LINE_MOMENT_VEC_OP(OpMomentBinomialFromTail, moment_binomial_from_tail, "t", "b")
368LINE_MOMENT_VEC_OP(OpMomentTailFromBinomial, moment_tail_from_binomial, "b", "t")
369#undef LINE_MOMENT_VEC_OP
370
371// ---------------------------------------------------------------------------
372// moment: joint (tensor) conversions, tensor in and tensor out
373// ---------------------------------------------------------------------------
374
375#define LINE_MOMENT_TENSOR_OP(OPNAME, FN, IN, OUT) \
376 struct OPNAME { \
377 template <class T> \
378 static Json run(Args& a) { \
379 const moment::MomentTensor<T> x = \
380 tensor_from_json<T>(a.get(IN), std::string(#FN ": ") + IN); \
381 a.done(); \
382 Json out; \
383 out[OUT] = encode_tensor(moment::FN<T>(x)); \
384 return out; \
385 } \
386 };
387
388LINE_MOMENT_TENSOR_OP(OpMjBinomialFromFactorial, moment_joint_binomial_from_factorial, "f", "b")
389LINE_MOMENT_TENSOR_OP(OpMjBinomialFromNegbinomial, moment_joint_binomial_from_negbinomial, "bm",
390 "b")
391LINE_MOMENT_TENSOR_OP(OpMjBinomialFromTail, moment_joint_binomial_from_tail, "t", "b")
392LINE_MOMENT_TENSOR_OP(OpMjCentralFromRaw, moment_joint_central_from_raw, "m", "mc")
393LINE_MOMENT_TENSOR_OP(OpMjCentralFromTail, moment_joint_central_from_tail, "t", "mc")
394LINE_MOMENT_TENSOR_OP(OpMjCumulantFromRaw, moment_joint_cumulant_from_raw, "m", "kappa")
395LINE_MOMENT_TENSOR_OP(OpMjFactcumulantFromFactorial, moment_joint_factcumulant_from_factorial,
396 "f", "kappa")
397LINE_MOMENT_TENSOR_OP(OpMjFactorialFromBinomial, moment_joint_factorial_from_binomial, "b", "f")
398LINE_MOMENT_TENSOR_OP(OpMjFactorialFromFactcumulant, moment_joint_factorial_from_factcumulant,
399 "kappa", "f")
400LINE_MOMENT_TENSOR_OP(OpMjFactorialFromRaw, moment_joint_factorial_from_raw, "m", "f")
401LINE_MOMENT_TENSOR_OP(OpMjFactorialFromUpfactorial, moment_joint_factorial_from_upfactorial, "fp",
402 "f")
403LINE_MOMENT_TENSOR_OP(OpMjNegbinomialFromBinomial, moment_joint_negbinomial_from_binomial, "b",
404 "bm")
405LINE_MOMENT_TENSOR_OP(OpMjNegbinomialFromUpfactorial, moment_joint_negbinomial_from_upfactorial,
406 "fp", "bm")
407LINE_MOMENT_TENSOR_OP(OpMjRawFromCumulant, moment_joint_raw_from_cumulant, "kappa", "m")
408LINE_MOMENT_TENSOR_OP(OpMjRawFromFactorial, moment_joint_raw_from_factorial, "f", "m")
409LINE_MOMENT_TENSOR_OP(OpMjRawFromUpfactorial, moment_joint_raw_from_upfactorial, "fp", "m")
410LINE_MOMENT_TENSOR_OP(OpMjTailFromBinomial, moment_joint_tail_from_binomial, "b", "t")
411LINE_MOMENT_TENSOR_OP(OpMjUpfactorialFromFactorial, moment_joint_upfactorial_from_factorial, "f",
412 "fp")
413LINE_MOMENT_TENSOR_OP(OpMjUpfactorialFromNegbinomial, moment_joint_upfactorial_from_negbinomial,
414 "bm", "fp")
415LINE_MOMENT_TENSOR_OP(OpMjUpfactorialFromRaw, moment_joint_upfactorial_from_raw, "m", "fp")
416#undef LINE_MOMENT_TENSOR_OP
417
418struct OpMjCentralFromRawMean {
419 template <class T>
420 static Json run(Args& a) {
421 const moment::MomentTensor<T> m =
422 tensor_from_json<T>(a.get("m"), "moment_joint_central_from_raw_mean: m");
423 const std::vector<T> mu = a.vector<T>("mu");
424 a.done();
425 Json out;
426 out["mc"] = encode_tensor(moment::moment_joint_central_from_raw_mean<T>(m, mu));
427 return out;
428 }
429};
430
431struct OpMjRawFromCentral {
432 template <class T>
433 static Json run(Args& a) {
434 const moment::MomentTensor<T> mc =
435 tensor_from_json<T>(a.get("mc"), "moment_joint_raw_from_central: mc");
436 const std::vector<T> mu = a.vector<T>("mu");
437 a.done();
438 Json out;
439 out["m"] = encode_tensor(moment::moment_joint_raw_from_central<T>(mc, mu));
440 return out;
441 }
442};
443
444struct OpMjAggregate {
445 template <class T>
446 static Json run(Args& a) {
447 const moment::MomentTensor<T> F =
448 tensor_from_json<T>(a.get("F"), "moment_joint_aggregate: F");
449 a.done();
450 Json out;
452 return out;
453 }
454};
455
456struct OpMjMarking {
457 template <class T>
458 static Json run(Args& a) {
459 const std::vector<T> f = a.vector<T>("f");
460 const std::vector<T> p = a.vector<T>("p");
461 const std::vector<unsigned> d = read_uints(a, "dims", "moment_joint_marking");
462 a.done();
463 const std::vector<std::size_t> dims(d.begin(), d.end());
464 Json out;
465 out["F"] = encode_tensor(moment::moment_joint_marking<T>(f, p, dims));
466 return out;
467 }
468};
469
470struct OpMomentHousematrix {
471 template <class T>
472 static Json run(Args& a) {
473 const std::string edge = a.text("edge", "");
474 if (edge.empty()) throw InputError("moment_housematrix: missing required argument 'edge'");
475 const int n = a.required_integer("n");
476 a.done();
477 Json out;
478 out["T"] = encode_matrix(moment::moment_housematrix<T>(edge, n));
479 return out;
480 }
481};
482
483struct OpMomentTensorsize {
484 template <class T>
485 static Json run(Args& a) {
486 const moment::MomentTensor<T> A = tensor_from_json<T>(a.get("A"), "moment_tensorsize: A");
487 a.done();
488 Json out;
489 out["sz"] = encode_sizes(moment::moment_tensorsize<T>(A));
490 return out;
491 }
492};
493
494struct OpMomentTensortrans {
495 template <class T>
496 static Json run(Args& a) {
497 const moment::MomentTensor<T> A = tensor_from_json<T>(a.get("A"), "moment_tensortrans: A");
498 const Matrix<T> Tm = a.matrix<T>("T");
499 const std::size_t mode = a.count("mode");
500 a.done();
501 // MATLAB's mode is 1-based, the port's 0-based.
502 if (mode < 1) throw InputError("moment_tensortrans: 'mode' is 1-based and must be >= 1");
503 Json out;
504 out["B"] = encode_tensor(moment::moment_tensortrans<T>(A, Tm, mode - 1));
505 return out;
506 }
507};
508
509struct OpMomentJointtrans {
510 template <class T>
511 static Json run(Args& a) {
512 const moment::MomentTensor<T> A = tensor_from_json<T>(a.get("A"), "moment_jointtrans: A");
513 const std::string edge = a.text("edge", "");
514 if (edge.empty()) throw InputError("moment_jointtrans: missing required argument 'edge'");
515 a.done();
516 Json out;
517 out["B"] = encode_tensor(moment::moment_jointtrans<T>(A, edge));
518 return out;
519 }
520};
521
522// ---------------------------------------------------------------------------
523// fj: fork-join response times and maxima
524// ---------------------------------------------------------------------------
525
526/**
527 * fj_amva / fj_qgb: MATLAB defaults Z to 0, but the port has no overload
528 * without Z, so Z is required here rather than restated.
529 */
530struct OpFjAmva {
531 template <class T>
532 static Json run(Args& a) {
533 const std::vector<T> D = a.vector<T>("D");
534 const std::vector<unsigned> P = read_uints(a, "P", "fj_amva");
535 const unsigned M = a.uinteger("M");
536 const T Z = a.number<T>("Z");
537 a.done();
538 const fj::FJAmvaResult<T> r = fj::fj_amva<T>(D, P, M, Z);
539 Json out;
540 out["R"] = encode_vector(r.R);
541 out["Q"] = encode_vector(r.Q);
542 out["X"] = encode_scalar(r.X);
543 out["U"] = encode_vector(r.U);
544 return out;
545 }
546};
547
548struct OpFjQgb {
549 template <class T>
550 static Json run(Args& a) {
551 const std::vector<T> D = a.vector<T>("D");
552 const std::vector<unsigned> P = read_uints(a, "P", "fj_qgb");
553 const unsigned M = a.uinteger("M");
554 const T Z = a.number<T>("Z");
555 a.done();
556 const fj::FJQgbResult<T> r = fj::fj_qgb<T>(D, P, M, Z);
557 Json out;
558 out["Q"] = encode_vector(r.Q);
559 out["y"] = encode_vector(r.y);
560 return out;
561 }
562};
563
564struct OpFjCharMaxBlom {
565 template <class T>
566 static Json run(Args& a) {
567 const unsigned K = a.uinteger("K");
568 a.unsupported("Finv", "it is a quantile function handle; omit it for the standard "
569 "normal the port selects");
570 const std::size_t n = prefix_count(a, {"alpha", "beta"}, "fj_char_max_blom");
571 const T alpha = n >= 1 ? a.number<T>("alpha") : T();
572 const T beta = n >= 2 ? a.number<T>("beta") : T();
573 a.done();
574 const std::function<T(const T&)> none;
575 const fj::FJCharMaxBlomResult<T> r =
576 n == 0 ? fj::fj_char_max_blom<T>(K)
577 : (n == 1 ? fj::fj_char_max_blom<T>(K, none, alpha)
578 : fj::fj_char_max_blom<T>(K, none, alpha, beta));
579 Json out;
580 out["mK"] = encode_scalar(r.mK);
581 // MATLAB returns NaN for the bracket when the port's guard says it does
582 // not exist; the absence is reported by omitting it.
583 if (r.bracket_available) {
584 out["lo"] = encode_scalar(r.lo);
585 out["hi"] = encode_scalar(r.hi);
586 }
587 return out;
588 }
589};
590
591struct OpFjCharMaxDiscrete {
592 template <class T>
593 static Json run(Args& a) {
594 const unsigned K = a.uinteger("K");
595 std::string type = a.text("dist_type", "");
596 const T par = a.number<T>("param");
597 a.done();
598 for (std::size_t i = 0; i < type.size(); ++i)
599 type[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(type[i])));
601 if (type == "geometric" || type == "geom")
603 else if (type == "poisson" || type == "pois")
605 else
606 throw UnsupportedError(ErrorKind{"Distribution"},
607 "fj_char_max_discrete: dist_type must be 'geometric' or 'poisson', got '" + type +
608 "'");
609 const fj::FJCharMaxDiscreteResult<T> r = fj::fj_char_max_discrete<T>(K, dist, par);
610 Json out;
611 out["MK"] = encode_scalar(r.MK);
612 out["mK"] = static_cast<long long>(r.mK);
613 out["exact"] = encode_scalar(r.exact);
614 return out;
615 }
616};
617
618struct OpFjCoxFit {
619 template <class T>
620 static Json run(Args& a) {
621 const T m1 = a.number<T>("m1");
622 const T c2 = a.number<T>("c2");
623 a.done();
624 const fj::FJCoxFitResult<T> r = fj::fj_cox_fit<T>(m1, c2);
625 Json out;
626 out["mu1"] = encode_scalar(r.mu1);
627 out["mu2"] = encode_scalar(r.mu2);
628 out["q"] = encode_scalar(r.q);
629 out["kmin"] = static_cast<long long>(r.kmin);
630 out["kmax"] = static_cast<long long>(r.kmax);
631 return out;
632 }
633};
634
635struct OpFjDagMakespan {
636 template <class T>
637 static Json run(Args& a) {
638 const Matrix<T> pred = a.matrix<T>("pred");
639 Matrix<T> rate = a.matrix<T>("rate");
640 a.done();
641 // MATLAB also takes an n-vector of constant rates and repmats it to the
642 // n by n concurrency table; the port takes only the table.
643 const std::size_t n = pred.rows();
644 if ((rate.rows() == 1 || rate.cols() == 1) && rate.rows() * rate.cols() == n && n > 1) {
645 Matrix<T> full(n, n);
646 for (std::size_t i = 0; i < n; ++i)
647 for (std::size_t k = 0; k < n; ++k)
648 full(i, k) = rate.rows() == 1 ? rate(0, i) : rate(i, 0);
649 rate = full;
650 }
651 const fj::FJDagMakespanResult<T> r = fj::fj_dag_makespan<T>(pred, rate);
652 Json out;
653 out["C"] = encode_scalar(r.C);
654 out["I"] = encode_vector(r.I);
655 out["Cend"] = encode_vector(r.Cend);
656 out["E"] = encode_vector(r.E);
657 return out;
658 }
659};
660
661/** MATLAB's options.npoints counts Simpson NODES, the port's npanels panels. */
662unsigned panels_from_npoints(Args& o, const std::string& fn) {
663 const unsigned np = o.uinteger("npoints");
664 if (np < 2) throw InputError(fn + ": options.npoints must be at least 2");
665 return np - 1;
666}
667
668/**
669 * fj_delay_opt(shape, rate, options). The options struct carries maxsweeps,
670 * dtol and npoints, taken positionally by the port in that order. options.tol
671 * is refused: the port fixes the dispersion tolerance and takes no argument
672 * for it.
673 */
674struct OpFjDelayOpt {
675 template <class T>
676 static Json run(Args& a) {
677 const std::vector<unsigned> shape = read_uints(a, "shape", "fj_delay_opt");
678 const std::vector<T> rate = a.vector<T>("rate");
679 std::size_t n = 0;
680 unsigned maxsweeps = 0, npanels = 0;
681 T dtol = T();
682 if (const Json* oj = a.opt("options")) {
683 Args o(*oj, "fj_delay_opt: options");
684 o.unsupported("tol", "the port fixes the dispersion tolerance of the inner "
685 "fj_dispersion calls and takes no argument for it");
686 n = prefix_count(o, {"maxsweeps", "dtol", "npoints"}, "fj_delay_opt: options");
687 if (n >= 1) maxsweeps = o.uinteger("maxsweeps");
688 if (n >= 2) dtol = o.number<T>("dtol");
689 if (n >= 3) npanels = panels_from_npoints(o, "fj_delay_opt");
690 o.done();
691 }
692 a.done();
693 fj::FJDelayOptResult<T> r;
694 if (n == 0) r = fj::fj_delay_opt<T>(shape, rate);
695 else if (n == 1) r = fj::fj_delay_opt<T>(shape, rate, maxsweeps);
696 else if (n == 2) r = fj::fj_delay_opt<T>(shape, rate, maxsweeps, dtol);
697 else r = fj::fj_delay_opt<T>(shape, rate, maxsweeps, dtol, npanels);
698 Json out;
699 out["d"] = encode_vector(r.d);
700 out["Edisp"] = encode_scalar(r.Edisp);
701 out["Emax"] = encode_scalar(r.Emax);
702 return out;
703 }
704};
705
706/**
707 * fj_dispersion(shape, rate, d, options). Without d the port's two-argument
708 * overload runs; options {tol, npoints} are positional after d, so they
709 * require d.
710 */
711struct OpFjDispersion {
712 template <class T>
713 static Json run(Args& a) {
714 const std::vector<unsigned> shape = read_uints(a, "shape", "fj_dispersion");
715 const std::vector<T> rate = a.vector<T>("rate");
716 const bool hasD = a.has("d");
717 const std::vector<T> d = a.vector_or_empty<T>("d");
718 std::size_t n = 0;
719 unsigned npanels = 0;
720 T tol = T();
721 if (const Json* oj = a.opt("options")) {
722 if (!hasD)
723 throw InputError("fj_dispersion: 'options' follows 'd' positionally in the port; "
724 "give 'd' as well");
725 Args o(*oj, "fj_dispersion: options");
726 n = prefix_count(o, {"tol", "npoints"}, "fj_dispersion: options");
727 if (n >= 1) tol = o.number<T>("tol");
728 if (n >= 2) npanels = panels_from_npoints(o, "fj_dispersion");
729 o.done();
730 }
731 a.done();
732 fj::FJDispersionResult<T> r;
733 if (!hasD) r = fj::fj_dispersion<T>(shape, rate);
734 else if (n == 0) r = fj::fj_dispersion<T>(shape, rate, d);
735 else if (n == 1) r = fj::fj_dispersion<T>(shape, rate, d, tol);
736 else r = fj::fj_dispersion<T>(shape, rate, d, tol, npanels);
737 Json out;
738 out["Edisp"] = encode_scalar(r.Edisp);
739 out["Emax"] = encode_scalar(r.Emax);
740 out["Emin"] = encode_scalar(r.Emin);
741 return out;
742 }
743};
744
745struct OpFjIsmGreen {
746 template <class T>
747 static Json run(Args& a) {
748 const T lambda = a.number<T>("lambda");
749 const T mu = a.number<T>("mu");
750 const unsigned s = a.uinteger("s");
751 const std::vector<T> c = a.vector<T>("c");
752 a.done();
753 const fj::FJIsmGreenResult<T> r = fj::fj_ism_green<T>(lambda, mu, s, c);
754 Json o;
755 o["EB"] = encode_scalar(r.EB);
756 o["EB2"] = encode_scalar(r.EB2);
757 o["ED"] = encode_scalar(r.ED);
758 o["ED2"] = encode_scalar(r.ED2);
759 o["EQ"] = encode_scalar(r.EQ);
760 o["EQbar"] = encode_scalar(r.EQbar);
761 o["pq"] = encode_scalar(r.pq);
762 o["pd"] = encode_scalar(r.pd);
763 o["pi0"] = encode_scalar(r.pi0);
764 o["rho"] = encode_scalar(r.rho);
765 o["q"] = encode_vector(r.q);
766 o["ES"] = encode_scalar(r.ES);
767 Json out;
768 out["W"] = encode_scalar(r.W);
769 out["R"] = encode_scalar(r.R);
770 out["out"] = o;
771 return out;
772 }
773};
774
775struct OpFjLstMaxHet {
776 template <class T>
777 static Json run(Args& a) {
778 const std::vector<T> lambda = a.vector<T>("lambda");
779 const T s = a.number<T>("s");
780 a.done();
781 Json out;
782 out["L"] = encode_scalar(fj::fj_lst_max_het<T>(lambda, s));
783 return out;
784 }
785};
786
787struct OpFjResptBulk {
788 template <class T>
789 static Json run(Args& a) {
790 const unsigned K = a.uinteger("K");
791 const T lambda = a.number<T>("lambda");
792 const T mu = a.number<T>("mu");
793 const unsigned c = a.uinteger("c");
794 const bool hasN = a.has("nmax");
795 const std::size_t nmax = a.count("nmax", 0);
796 a.done();
797 const fj::FJResptBulkResult<T> r = hasN ? fj::fj_respt_bulk<T>(K, lambda, mu, c, nmax)
798 : fj::fj_respt_bulk<T>(K, lambda, mu, c);
799 Json out;
800 out["Rreq"] = encode_scalar(r.Rreq);
801 out["Rtask"] = encode_scalar(r.Rtask);
802 out["Q"] = encode_scalar(r.Q);
803 out["p"] = encode_vector(r.p);
804 return out;
805 }
806};
807
808struct OpFjResptClosed {
809 template <class T>
810 static Json run(Args& a) {
811 const unsigned K = a.uinteger("K");
812 const T x = a.number<T>("x");
813 const unsigned M = a.uinteger("M");
814 const bool hasA = a.has("A");
815 const T A = hasA ? a.number<T>("A") : T();
816 a.done();
817 const fj::FJResptClosedResult<T> r =
818 hasA ? fj::fj_respt_closed<T>(K, x, M, A) : fj::fj_respt_closed<T>(K, x, M);
819 Json out;
820 out["R"] = encode_scalar(r.R);
821 out["exact"] = r.exact;
822 return out;
823 }
824};
825
826struct OpFjResptNosplit {
827 template <class T>
828 static Json run(Args& a) {
829 const unsigned K = a.uinteger("K");
830 const T lambda = a.number<T>("lambda");
831 const T mu = a.number<T>("mu");
832 a.done();
833 const fj::FJResptNosplitResult<T> r = fj::fj_respt_nosplit<T>(K, lambda, mu);
834 Json out;
835 out["R"] = encode_scalar(r.R);
836 out["rho"] = encode_scalar(r.rho);
837 return out;
838 }
839};
840
841struct OpFjSerialization {
842 template <class T>
843 static Json run(Args& a) {
844 const std::vector<T> Rs = a.vector<T>("Rs");
845 const T R0 = a.number<T>("R0");
846 const unsigned M = a.uinteger("M");
847 const bool hasAlpha = a.has("alpha");
848 const T alpha = hasAlpha ? a.number<T>("alpha") : T();
849 a.done();
850 const fj::FJSerializationResult<T> r = hasAlpha
851 ? fj::fj_serialization<T>(Rs, R0, M, alpha)
852 : fj::fj_serialization<T>(Rs, R0, M);
853 Json out;
854 out["P"] = encode_vector(r.P);
855 out["delay"] = encode_vector(r.delay);
856 out["Rtot"] = encode_scalar(r.Rtot);
857 return out;
858 }
859};
860
861struct OpFjTsmCapacity {
862 template <class T>
863 static Json run(Args& a) {
864 const unsigned s = a.uinteger("s");
865 const std::vector<T> f = a.vector<T>("f");
866 const std::vector<unsigned> rr = read_uints(a, "r", "fj_tsm_capacity");
867 const std::vector<T> x = a.vector<T>("x");
868 a.done();
869 const fj::FJTsmCapacityResult<T> r = fj::fj_tsm_capacity<T>(s, f, rr, x);
870 Json out;
871 out["Lmax"] = encode_scalar(r.Lmax);
872 out["Llp"] = encode_scalar(r.Llp);
873 // MATLAB's Lfcfs is NaN outside the two-class closed form; omitted then.
874 if (r.fcfs_available) out["Lfcfs"] = encode_scalar(r.Lfcfs);
875 Json states = Json::array();
876 for (std::size_t i = 0; i < r.states.size(); ++i) {
877 Json row = Json::array();
878 for (std::size_t k = 0; k < r.states[i].size(); ++k)
879 row.push_back(static_cast<long long>(r.states[i][k]));
880 states.push_back(row);
881 }
882 Json p;
883 p["states"] = states;
884 p["prob"] = encode_vector(r.prob);
885 out["p"] = p;
886 return out;
887 }
888};
889
890struct OpFjXmaxCoxian {
891 template <class T>
892 static Json run(Args& a) {
893 const unsigned K = a.uinteger("K");
894 const T mu1 = a.number<T>("mu1");
895 const T mu2 = a.number<T>("mu2");
896 const T q = a.number<T>("q");
897 a.done();
898 const fj::FJXmaxCoxianResult<T> r = fj::fj_xmax_coxian<T>(K, mu1, mu2, q);
899 Json out;
900 out["Xmax"] = encode_scalar(r.Xmax);
901 out["m1"] = encode_scalar(r.m1);
902 out["c2"] = encode_scalar(r.c2);
903 return out;
904 }
905};
906
907struct OpFjXmaxHet {
908 template <class T>
909 static Json run(Args& a) {
910 const std::vector<T> lambda = a.vector<T>("lambda");
911 const bool hasN = a.has("n");
912 const unsigned n = a.uinteger("n", 0);
913 a.done();
914 Json out;
915 out["Mn"] = encode_scalar(hasN ? fj::fj_xmax_het<T>(lambda, n) : fj::fj_xmax_het<T>(lambda));
916 return out;
917 }
918};
919
920struct OpFjXmaxMomentsHet {
921 template <class T>
922 static Json run(Args& a) {
923 const std::vector<T> lambda = a.vector<T>("lambda");
924 const bool hasN = a.has("n");
925 const unsigned n = a.uinteger("n", 0);
926 a.done();
927 Json out;
928 out["M"] = encode_vector(hasN ? fj::fj_xmax_moments_het<T>(lambda, n)
930 return out;
931 }
932};
933
934struct OpFjXmaxHz {
935 template <class T>
936 static Json run(Args& a) {
937 const T m1 = a.number<T>("m1");
938 const T m2 = a.number<T>("m2");
939 const unsigned K = a.uinteger("K");
940 a.done();
941 const fj::FJXmaxHzResult<T> r = fj::fj_xmax_hz<T>(m1, m2, K);
942 Json out;
943 out["Xmax"] = encode_scalar(r.Xmax);
944 out["resid"] = encode_scalar(r.resid);
945 return out;
946 }
947};
948
949struct OpFjMg1ResptMoments {
950 template <class T>
951 static Json run(Args& a) {
952 const T lambda = a.number<T>("lambda");
953 const T ES = a.number<T>("ES");
954 const T ES2 = a.number<T>("ES2");
955 const T ES3 = a.number<T>("ES3");
956 a.done();
957 const fj::Mg1ResptMoments<T> r = fj::fj_mg1_respt_moments<T>(lambda, ES, ES2, ES3);
958 Json out;
959 out["ET"] = encode_scalar(r.ET);
960 out["VT"] = encode_scalar(r.VT);
961 return out;
962 }
963};
964
965template <class T>
966Json forktail_results(const fj::ForkTailResult<T>& r) {
967 Json out;
968 out["xp"] = encode_scalar(r.xp);
969 out["alpha"] = encode_vector(r.alpha);
970 out["beta"] = encode_vector(r.beta);
971 return out;
972}
973
974/** fj_tail_forktail(ET, VT, K, p, P): K, p and P are positional. */
975struct OpFjTailForktail {
976 template <class T>
977 static Json run(Args& a) {
978 const std::vector<T> ET = a.vector<T>("ET");
979 const std::vector<T> VT = a.vector<T>("VT");
980 const std::size_t n = prefix_count(a, {"K", "p", "P"}, "fj_tail_forktail");
981 const std::vector<T> K = n >= 1 ? a.vector<T>("K") : std::vector<T>();
982 const T p = n >= 2 ? a.number<T>("p") : T();
983 const std::vector<T> P = n >= 3 ? a.vector<T>("P") : std::vector<T>();
984 a.done();
985 if (n == 0) return forktail_results(fj::fj_tail_forktail<T>(ET, VT));
986 if (n == 1) return forktail_results(fj::fj_tail_forktail<T>(ET, VT, K));
987 if (n == 2) return forktail_results(fj::fj_tail_forktail<T>(ET, VT, K, p));
988 return forktail_results(fj::fj_tail_forktail<T>(ET, VT, K, p, P));
989 }
990};
991
992/** fj_tail_ordstat(ET, VT, K, p, kreq): K, p and kreq are positional. */
993struct OpFjTailOrdstat {
994 template <class T>
995 static Json run(Args& a) {
996 const std::vector<T> ET = a.vector<T>("ET");
997 const std::vector<T> VT = a.vector<T>("VT");
998 const std::size_t n = prefix_count(a, {"K", "p", "kreq"}, "fj_tail_ordstat");
999 const std::size_t K = n >= 1 ? a.count("K") : 0;
1000 const T p = n >= 2 ? a.number<T>("p") : T();
1001 const std::size_t kreq = n >= 3 ? a.count("kreq") : 0;
1002 a.done();
1003 if (n == 0) return forktail_results(fj::fj_tail_ordstat<T>(ET, VT));
1004 if (n == 1) return forktail_results(fj::fj_tail_ordstat<T>(ET, VT, K));
1005 if (n == 2) return forktail_results(fj::fj_tail_ordstat<T>(ET, VT, K, p));
1006 return forktail_results(fj::fj_tail_ordstat<T>(ET, VT, K, p, kreq));
1007 }
1008};
1009
1010/**
1011 * fj_branch_members(lqn, joinaidx). Of the LayeredNetworkStruct only the four
1012 * fields the algorithm reads cross the boundary, as the object lqn; indices
1013 * stay 1-based, as in MATLAB.
1014 */
1015struct OpFjBranchMembers {
1016 template <class T>
1017 static Json run(Args& a) {
1018 Args l(a.get("lqn"), "fj_branch_members: lqn");
1019 fj::LqnBranchView<T> v;
1020 v.graph = l.matrix<T>("graph");
1021 v.ashift = l.count("ashift");
1022 v.nacts = l.count("nacts");
1023 v.actposttype = l.ints("actposttype");
1024 l.done();
1025 const std::size_t joinaidx = a.count("joinaidx");
1026 a.done();
1027 const std::vector<std::vector<std::size_t> > m = fj::fj_branch_members<T>(v, joinaidx);
1028 Json members = Json::array();
1029 for (std::size_t i = 0; i < m.size(); ++i) members.push_back(encode_sizes(m[i]));
1030 Json out;
1031 out["members"] = members;
1032 return out;
1033 }
1034};
1035
1036/**
1037 * fj_dist2fj(lineMAP, distType, sn, ist, r). The reference reads ONE field of
1038 * sn, procid(ist, r), so sn crosses as {"procid": [[..]]} and nothing else;
1039 * ist and r are 1-based. The codes are MATLAB's ProcessType values.
1040 */
1041struct OpFjDist2fj {
1042 template <class T>
1043 static Json run(Args& a) {
1044 const mam::Map<T> m = map_from_pair<T>(a.get("lineMAP"), "fj_dist2fj: lineMAP");
1045 const std::string distType = a.text("distType", "");
1046 Args sn(a.get("sn"), "fj_dist2fj: sn");
1047 const Matrix<double> procid = sn.matrix<double>("procid");
1048 sn.done();
1049 const std::size_t ist = a.count("ist");
1050 const std::size_t r = a.count("r");
1051 a.done();
1052 fj::FjDistKind kind;
1053 if (distType == "arrival")
1055 else if (distType == "service")
1057 else
1058 throw InputError("fj_dist2fj: distType must be 'arrival' or 'service', got '" +
1059 distType + "'");
1060 if (ist < 1 || r < 1 || ist > procid.rows() || r > procid.cols())
1061 throw InputError("fj_dist2fj: (ist, r) are 1-based and must index sn.procid");
1062 const double code = procid(ist - 1, r - 1);
1063 fj::FjProcType pt;
1064 if (code == 0.0) pt = fj::FjProcType::Exp;
1065 else if (code == 1.0) pt = fj::FjProcType::Erlang;
1066 else if (code == 2.0) pt = fj::FjProcType::HyperExp;
1067 else if (code == 5.0) pt = fj::FjProcType::Map;
1068 else
1069 throw UnsupportedError(ErrorKind{"Distribution"},
1070 "fj_dist2fj: sn.procid(ist, r) = " + std::to_string(code) +
1071 " is not a process FJ_codes takes (EXP=0, ERLANG=1, HYPEREXP=2, MAP=5)");
1072 const fj::FjDist<T> d = fj::fj_dist2fj<T>(m, kind, pt);
1073 Json out;
1074 if (kind == fj::FjDistKind::Arrival) {
1075 out["lambda"] = encode_scalar(d.lambda);
1076 out["lambda0"] = encode_matrix(d.lambda0);
1077 out["lambda1"] = encode_matrix(d.lambda1);
1078 out["ma"] = encode_count(d.ma);
1079 out["Ia"] = encode_matrix(d.Ia);
1080 out["ArrChoice"] = d.choice;
1081 } else {
1082 out["mu"] = encode_scalar(d.mu);
1083 out["ST"] = encode_matrix(d.ST);
1084 out["St"] = encode_vector(d.St);
1085 out["tau_st"] = encode_vector(d.tau_st);
1086 out["SerChoice"] = d.choice;
1087 }
1088 return out;
1089 }
1090};
1091
1092// ---------------------------------------------------------------------------
1093// aoi: the service-law transforms, evaluated where the caller asks
1094// ---------------------------------------------------------------------------
1095//
1096// aoi_lst_* return a function handle in MATLAB. A handle cannot cross JSON, so
1097// the boundary takes the points "s" at which to evaluate it and returns the
1098// values as "lst". The law is the port's own constructor, unchanged.
1099
1100template <class T>
1101Json lst_values(Args& a, const aoi::Lst<T>& L) {
1102 const std::vector<T> s = a.vector<T>("s");
1103 a.done();
1104 std::vector<T> v(s.size());
1105 for (std::size_t i = 0; i < s.size(); ++i) v[i] = L(s[i]);
1106 Json out;
1107 out["lst"] = encode_vector(v);
1108 return out;
1109}
1110
1111struct OpAoiLstDet {
1112 template <class T>
1113 static Json run(Args& a) {
1114 const T d = a.number<T>("d");
1115 return lst_values<T>(a, aoi::aoi_lst_det<T>(d));
1116 }
1117};
1118
1119struct OpAoiLstErlang {
1120 template <class T>
1121 static Json run(Args& a) {
1122 const unsigned k = a.uinteger("k");
1123 const T mu = a.number<T>("mu");
1124 return lst_values<T>(a, aoi::aoi_lst_erlang<T>(k, mu));
1125 }
1126};
1127
1128struct OpAoiLstExp {
1129 template <class T>
1130 static Json run(Args& a) {
1131 const T mu = a.number<T>("mu");
1132 return lst_values<T>(a, aoi::aoi_lst_exp<T>(mu));
1133 }
1134};
1135
1136struct OpAoiLstPh {
1137 template <class T>
1138 static Json run(Args& a) {
1139 const std::vector<T> alpha = a.vector<T>("alpha");
1140 const Matrix<T> Tm = a.matrix<T>("T");
1141 return lst_values<T>(a, aoi::aoi_lst_ph<T>(alpha, Tm));
1142 }
1143};
1144
1145/** aoi_dist2ph(proc): proc is MATLAB's cell {D0, D1}. */
1146struct OpAoiDist2ph {
1147 template <class T>
1148 static Json run(Args& a) {
1149 const mam::Map<T> m = map_from_pair<T>(a.get("proc"), "aoi_dist2ph: proc");
1150 a.done();
1151 const aoi::AoiPh<T> r = aoi::aoi_dist2ph<T>(m);
1152 Json out;
1153 out["alpha"] = encode_vector(r.alpha);
1154 out["T"] = encode_matrix(r.Tmat);
1155 return out;
1156 }
1157};
1158
1159// ---------------------------------------------------------------------------
1160// qsys: MAP/G/1/K and its marked and per-flow variants
1161// ---------------------------------------------------------------------------
1162
1163/** The 'tol' and 'nmax' pair of the MAP/G/1/K family, which tune one truncation. */
1164template <class T>
1165struct Truncation {
1166 bool given = false;
1167 T tol = T();
1168 std::size_t nmax = 0;
1169};
1170
1171template <class T>
1172Truncation<T> read_truncation(Args& a, const std::string& fn) {
1173 Truncation<T> tr;
1174 tr.given = all_or_none(a, {"tol", "nmax"}, fn);
1175 if (tr.given) {
1176 tr.tol = a.number<T>("tol");
1177 tr.nmax = a.count("nmax");
1178 } else {
1179 a.opt("tol");
1180 a.opt("nmax");
1181 }
1182 return tr;
1183}
1184
1185struct OpQsysMapg1k {
1186 template <class T>
1187 static Json run(Args& a) {
1188 const mam::Map<T> arrival = read_map<T>(a, "D0", "D1");
1189 const qsys::ServiceLaw<T> svc = read_svc<T>(a, "qsys_mapg1k");
1190 const std::size_t K = a.count("K");
1191 const Truncation<T> tr = read_truncation<T>(a, "qsys_mapg1k");
1192 a.done();
1193 const qsys::MapG1kResult<T> r = tr.given
1194 ? qsys::qsys_mapg1k<T>(arrival, svc, K, tr.tol, tr.nmax)
1195 : qsys::qsys_mapg1k<T>(arrival, svc, K);
1196 Json out;
1197 out["p0"] = encode_scalar(r.p0);
1198 out["pK"] = encode_scalar(r.pK);
1199 out["lossProbability"] = encode_scalar(r.lossProbability);
1200 out["throughput"] = encode_scalar(r.throughput);
1201 out["lambda"] = encode_scalar(r.lambda);
1202 out["meanServiceTime"] = encode_scalar(r.meanServiceTime);
1203 out["utilization"] = encode_scalar(r.utilization);
1204 out["rho"] = encode_scalar(r.rho);
1205 out["nmax"] = encode_count(r.nmax);
1206 out["sigma"] = encode_vector(r.sigma);
1207 out["pKvec"] = encode_vector(r.pKvec);
1208 out["p0vec"] = encode_vector(r.p0vec);
1209 out["plevel"] = encode_vector(r.plevel);
1210 out["meanQueueLength"] = encode_scalar(r.meanQueueLength);
1211 out["countingResidual"] = encode_scalar(r.countingResidual);
1212 return out;
1213 }
1214};
1215
1216struct OpQsysMmapg1k {
1217 template <class T>
1218 static Json run(Args& a) {
1219 const Matrix<T> D0 = a.matrix<T>("D0");
1220 const std::vector<Matrix<T> > D1c = a.matrices<T>("D1c");
1221 const qsys::ServiceLaw<T> svc = read_svc<T>(a, "qsys_mmapg1k");
1222 const std::size_t K = a.count("K");
1223 const Truncation<T> tr = read_truncation<T>(a, "qsys_mmapg1k");
1224 a.done();
1225 const qsys::MmapG1kResult<T> r =
1226 tr.given ? qsys::qsys_mmapg1k<T>(D0, D1c, svc, K, tr.tol, tr.nmax)
1227 : qsys::qsys_mmapg1k<T>(D0, D1c, svc, K);
1228 Json out;
1229 out["throughput"] = encode_vector(r.throughput);
1230 out["lossRatio"] = encode_vector(r.lossRatio);
1231 out["lambda"] = encode_vector(r.lambda);
1232 out["lambdaAggregate"] = encode_scalar(r.lambdaAggregate);
1233 out["throughputAggregate"] = encode_scalar(r.throughputAggregate);
1234 out["lossAggregate"] = encode_scalar(r.lossAggregate);
1235 out["p0"] = encode_scalar(r.p0);
1236 out["pK"] = encode_scalar(r.pK);
1237 out["pKvec"] = encode_vector(r.pKvec);
1238 out["plevel"] = encode_vector(r.plevel);
1239 out["meanQueueLength"] = encode_scalar(r.meanQueueLength);
1240 out["meanServiceTime"] = encode_scalar(r.meanServiceTime);
1241 out["utilization"] = encode_scalar(r.utilization);
1242 out["rho"] = encode_scalar(r.rho);
1243 return out;
1244 }
1245};
1246
1247/** qsys_mapg1k_perflow(MAPS, svc, K): MAPS is a list of [D0, D1] pairs. */
1248struct OpQsysMapg1kPerflow {
1249 template <class T>
1250 static Json run(Args& a) {
1251 const std::vector<mam::Map<T> > flows =
1252 maps_from_json<T>(a.get("MAPS"), "qsys_mapg1k_perflow: MAPS");
1253 const qsys::ServiceLaw<T> svc = read_svc<T>(a, "qsys_mapg1k_perflow");
1254 const std::size_t K = a.count("K");
1255 const Truncation<T> tr = read_truncation<T>(a, "qsys_mapg1k_perflow");
1256 a.done();
1257 const qsys::MapG1kPerflowResult<T> r =
1258 tr.given ? qsys::qsys_mapg1k_perflow<T>(flows, svc, K, tr.tol, tr.nmax)
1259 : qsys::qsys_mapg1k_perflow<T>(flows, svc, K);
1260 Json out;
1261 out["throughput"] = encode_vector(r.throughput);
1262 out["lossRatio"] = encode_vector(r.lossRatio);
1263 out["lambda"] = encode_vector(r.lambda);
1264 out["lambdaAggregate"] = encode_scalar(r.lambdaAggregate);
1265 out["throughputAggregate"] = encode_scalar(r.throughputAggregate);
1266 out["lossAggregate"] = encode_scalar(r.lossAggregate);
1267 out["p0"] = encode_vector(r.p0);
1268 out["pK"] = encode_vector(r.pK);
1269 out["meanServiceTime"] = encode_scalar(r.meanServiceTime);
1270 out["rho"] = encode_scalar(r.rho);
1271 return out;
1272 }
1273};
1274
1275struct OpQsysSvclawCoeffs {
1276 template <class T>
1277 static Json run(Args& a) {
1278 const qsys::ServiceLaw<T> svc = read_svc<T>(a, "qsys_svclaw_coeffs");
1279 const T theta = a.number<T>("theta");
1280 const T tol = a.number<T>("tol");
1281 const std::size_t nmaxCap = a.count("nmaxCap");
1282 a.done();
1283 const qsys::SvclawCoeffsResult<T> r = qsys::qsys_svclaw_coeffs<T>(svc, theta, tol, nmaxCap);
1284 Json out;
1285 out["cn"] = encode_vector(r.cn);
1286 out["Smean"] = encode_scalar(r.Smean);
1287 out["residual"] = encode_scalar(r.residual);
1288 return out;
1289 }
1290};
1291
1292// ---------------------------------------------------------------------------
1293// qsys: laws given as distribution records
1294// ---------------------------------------------------------------------------
1295
1296/**
1297 * qsys_ldps_workload(lambda, B, alpha, N, t, ngrid). B is MATLAB's
1298 * Distribution object; it crosses as its model.json record, and the port is
1299 * handed exactly the three things the reference reads from it: getMean,
1300 * getSCV and evalCDF. An empty or absent t selects the automatic grid, as in
1301 * MATLAB; the port takes t and ngrid together, so ngrid is required whenever
1302 * either is named.
1303 */
1304struct OpQsysLdpsWorkload {
1305 template <class T>
1306 static Json run(Args& a) {
1307 const T lambda = a.number<T>("lambda");
1308 const lang::Distrib<T> law = dist_record<T>(a.get("B"), "qsys_ldps_workload: B");
1309 const std::vector<T> alpha = a.vector<T>("alpha");
1310 const std::size_t N = a.count("N");
1311 const bool tuned = a.has("t") || a.has("ngrid");
1312 if (tuned && !a.has("ngrid"))
1313 throw InputError("qsys_ldps_workload: the port takes 't' and 'ngrid' together; give "
1314 "'ngrid' with 't'");
1315 const std::vector<T> t = a.vector_or_empty<T>("t");
1316 const std::size_t ngrid = a.count("ngrid", 0);
1317 a.done();
1318 qsys::WorkloadServiceLaw<T> B;
1319 B.mean = lang::dist_moment(law, 1u);
1320 const T m2 = lang::dist_moment(law, 2u);
1321 B.scv = T(m2 / (B.mean * B.mean) - num_traits<T>::from_int(1));
1322 B.cdf = [law](const T& x) { return lang::dist_cdf(law, x); };
1323 const qsys::LdpsWorkloadResult<T> r =
1324 tuned ? qsys::qsys_ldps_workload<T>(lambda, B, alpha, N, t, ngrid)
1325 : qsys::qsys_ldps_workload<T>(lambda, B, alpha, N);
1326 Json out;
1327 out["F"] = encode_vector(r.F);
1328 out["t"] = encode_vector(r.t);
1329 out["p"] = encode_vector(r.p);
1330 return out;
1331 }
1332};
1333
1334/**
1335 * qsys_mmapgk1(MMAP, svc, ...). MMAP is the LINE list [D0, D1, D^(1), ...].
1336 * Each svc entry is a [D0s, D1s] pair (a phase-type MAP) or a distribution
1337 * record; MATLAB's third form, a struct holding an lst handle, cannot cross.
1338 * The port takes wPoints, numWMoms, tol and the iteration cap together (MATLAB
1339 * fixes the cap internally, the port exposes it as iterMax), and quadNodes
1340 * only after them.
1341 */
1342struct OpQsysMmapgk1 {
1343 template <class T>
1344 static Json run(Args& a) {
1345 const std::vector<Matrix<T> > MMAP = a.matrices<T>("MMAP");
1346 const Json& sj = a.get("svc");
1347 if (!sj.is_array()) throw InputError("qsys_mmapgk1: svc must be a list, one law per type");
1348 std::vector<qsys::MmapGk1Service<T> > svc;
1349 for (std::size_t k = 0; k < sj.size(); ++k) {
1350 const std::string where = "qsys_mmapgk1: svc[" + std::to_string(k) + "]";
1351 if (sj[k].is_array()) {
1352 svc.push_back(qsys::MmapGk1Service<T>(map_from_pair<T>(sj[k], where)));
1353 } else if (sj[k].is_object() && sj[k].contains("lst")) {
1354 throw UnsupportedError(ErrorKind{"JsonApi"},
1355 where + ": a transform struct carries an lst function handle, which cannot "
1356 "cross the JSON API boundary; give the law as a distribution record "
1357 "or a [D0, D1] pair");
1358 } else {
1359 svc.push_back(qsys::MmapGk1Service<T>(dist_record<T>(sj[k], where)));
1360 }
1361 }
1362 const bool tuned = all_or_none(a, {"wPoints", "numWMoms", "tol", "iterMax"}, "qsys_mmapgk1");
1363 if (a.has("quadNodes") && !tuned)
1364 throw InputError("qsys_mmapgk1: 'quadNodes' follows 'wPoints', 'numWMoms', 'tol' and "
1365 "'iterMax' positionally in the port; give them as well");
1366 const std::vector<T> w = a.vector_or_empty<T>("wPoints");
1367 const std::size_t nm = a.count("numWMoms", 0);
1368 const double tol = a.scalar<double>("tol", 0.0);
1369 const std::size_t iterMax = a.count("iterMax", 0);
1370 const bool hasQ = a.has("quadNodes");
1371 const std::size_t q = a.count("quadNodes", 0);
1372 a.done();
1373 qsys::MmapGk1Result<T> r;
1374 if (!tuned) r = qsys::qsys_mmapgk1<T>(MMAP, svc);
1375 else if (!hasQ) r = qsys::qsys_mmapgk1<T>(MMAP, svc, w, nm, tol, iterMax);
1376 else r = qsys::qsys_mmapgk1<T>(MMAP, svc, w, nm, tol, iterMax, q);
1377 Json out;
1378 out["lambda"] = encode_vector(r.lambdas);
1379 out["arrivalRate"] = encode_scalar(r.arrivalRate);
1380 out["utilization"] = encode_scalar(r.utilization);
1381 out["idleVector"] = encode_vector(r.idleVector);
1382 out["waitMoments"] = encode_vectors(r.waitMoments);
1383 out["meanWaitingTime"] = encode_vector(r.meanWaitingTime);
1384 out["meanSojournTime"] = encode_vector(r.meanSojournTime);
1385 out["meanQueueLength"] = encode_scalar(r.meanQueueLength);
1386 out["waitCDF"] = encode_vectors(r.waitCDF);
1387 out["waitPoints"] = encode_vector(r.waitPoints);
1388 return out;
1389 }
1390};
1391
1392/**
1393 * qsys_mmapgk1_lcfspr(MMAP, svc, ...). MMAP and svc as for qsys_mmapgk1; the
1394 * MATLAB name-value options nMax, wPoints, quadNodes and tol are each optional.
1395 * respLST is a function handle and cannot cross JSON; as for aoi_lst_*, the
1396 * optional points "s" (real) say where to evaluate it, and "respLST" returns
1397 * its values there, [type][point]. respCDF carries the CDF values.
1398 */
1399struct OpQsysMmapgk1Lcfspr {
1400 template <class T>
1401 static Json run(Args& a) {
1402 const std::vector<Matrix<T> > MMAP = a.matrices<T>("MMAP");
1403 const Json& sj = a.get("svc");
1404 if (!sj.is_array())
1405 throw InputError("qsys_mmapgk1_lcfspr: svc must be a list, one law per type");
1406 std::vector<qsys::MmapGk1Service<T> > svc;
1407 for (std::size_t k = 0; k < sj.size(); ++k) {
1408 const std::string where = "qsys_mmapgk1_lcfspr: svc[" + std::to_string(k) + "]";
1409 if (sj[k].is_array()) {
1410 svc.push_back(qsys::MmapGk1Service<T>(map_from_pair<T>(sj[k], where)));
1411 } else if (sj[k].is_object() && sj[k].contains("lst")) {
1412 throw UnsupportedError(ErrorKind{"JsonApi"},
1413 where + ": a transform struct carries an lst function handle, which cannot "
1414 "cross the JSON API boundary; give the law as a distribution record "
1415 "or a [D0, D1] pair");
1416 } else {
1417 svc.push_back(qsys::MmapGk1Service<T>(dist_record<T>(sj[k], where)));
1418 }
1419 }
1420 qsys::MmapGk1LcfsprOptions<T> opt;
1421 if (a.has("nMax")) opt.nMax = static_cast<long>(a.count("nMax"));
1422 opt.wPoints = a.vector_or_empty<T>("wPoints");
1423 opt.quadNodes = a.count("quadNodes", 120);
1424 opt.tol = a.scalar<double>("tol", 1e-12);
1425 const bool wantLst = a.has("s");
1426 const std::vector<T> sPts = wantLst ? a.vector<T>("s") : std::vector<T>();
1427 a.done();
1428 const qsys::MmapGk1LcfsprResult<T> r = qsys::qsys_mmapgk1_lcfspr<T>(MMAP, svc, opt);
1429 Json out;
1430 out["lambda"] = encode_vector(r.lambdas);
1431 out["arrivalRate"] = encode_scalar(r.arrivalRate);
1432 out["utilization"] = encode_scalar(r.utilization);
1433 out["Q"] = encode_matrix(r.Q);
1434 out["kappa"] = encode_vector(r.kappa);
1435 out["Rk"] = encode_matrices(r.Rk);
1436 out["R"] = encode_matrix(r.R);
1437 out["classMeanQueueLength"] = encode_vector(r.classMeanQueueLength);
1438 out["meanQueueLength"] = encode_scalar(r.meanQueueLength);
1439 out["meanSojournTime"] = encode_vector(r.meanSojournTime);
1440 out["queueLengthDistr"] = encode_vector(r.queueLengthDistr);
1441 out["classQueueLengthDistr"] = encode_vectors(r.classQueueLengthDistr);
1442 out["respCDF"] = encode_vectors(r.respCDF);
1443 out["respPoints"] = encode_vector(r.respPoints);
1444 if (wantLst) {
1445 const std::size_t K = r.lambdas.size();
1446 std::vector<std::vector<T> > lv(K, std::vector<T>(sPts.size()));
1447 for (std::size_t i = 0; i < sPts.size(); ++i) {
1448 const std::vector<std::complex<double> > w =
1449 r.respLST(std::complex<double>(num_traits<T>::to_double(sPts[i]), 0.0));
1450 for (std::size_t k = 0; k < K; ++k) lv[k][i] = num_traits<T>::from_double(w[k].real());
1451 }
1452 out["respLST"] = encode_vectors(lv);
1453 }
1454 out["analyzer"] = r.analyzer;
1455 return out;
1456 }
1457};
1458
1459/**
1460 * qsys_tandem_ub_ciucu(x, lst, p, mu, dlst). lst is the interarrival
1461 * Laplace-Stieltjes transform; it crosses as the distribution record of the
1462 * interarrival law, whose transform the port evaluates. dlst is refused: the
1463 * port differentiates lst itself when it is absent.
1464 */
1465struct OpQsysTandemUbCiucu {
1466 template <class T>
1467 static Json run(Args& a) {
1468 const std::vector<T> x = a.vector<T>("x");
1469 const lang::Distrib<T> law = dist_record<T>(a.get("lst"), "qsys_tandem_ub_ciucu: lst");
1470 const std::vector<T> p = a.vector<T>("p");
1471 const std::vector<T> mu = a.vector<T>("mu");
1472 a.unsupported("dlst", "it is a function handle; omit it and the port differentiates the "
1473 "transform of lst numerically");
1474 a.done();
1475 const std::function<T(const T&)> lst = [law](const T& s) { return lang::dist_lst(law, s); };
1476 const qsys::TandemUbResult<T> r = qsys::qsys_tandem_ub_ciucu<T>(x, lst, p, mu);
1477 Json out;
1478 out["S"] = encode_vector(r.S);
1479 out["W"] = encode_vector(r.W);
1480 out["theta"] = encode_scalar(r.theta);
1481 out["alpha"] = encode_scalar(r.alpha);
1482 out["A"] = encode_scalar(r.A);
1483 out["B"] = encode_scalar(r.B);
1484 out["C"] = encode_scalar(r.C);
1485 out["D"] = encode_scalar(r.D);
1486 out["analyzer"] = r.analyzer;
1487 return out;
1488 }
1489};
1490
1491// ---------------------------------------------------------------------------
1492// qsys: Lindley recursions
1493// ---------------------------------------------------------------------------
1494
1495template <class T>
1496Json lindley_results(const qsys::LindleyResult<T>& r, bool scalar) {
1497 Json out;
1498 out["mean"] = encode_shaped(r.mean, scalar);
1499 out["var"] = encode_shaped(r.var, scalar);
1500 out["moments"] = encode_matrix(r.moments);
1501 out["mmax"] = static_cast<long long>(r.mmax);
1502 out["analyzer"] = r.analyzer;
1503 return out;
1504}
1505
1506struct OpQsysHh1Lindley {
1507 template <class T>
1508 static Json run(Args& a) {
1509 const std::vector<T> lambda = a.vector<T>("lambda");
1510 const std::vector<T> pa = a.vector<T>("pa");
1511 const std::vector<T> mu = a.vector<T>("mu");
1512 const std::vector<T> ps = a.vector<T>("ps");
1513 const bool scalar = !a.get("Wn").is_array();
1514 const std::vector<T> Wn = a.vector<T>("Wn");
1515 const bool hasM = a.has("mmax");
1516 const unsigned mmax = a.uinteger("mmax", 0);
1517 a.done();
1518 if (scalar)
1519 return lindley_results(
1520 hasM ? qsys::qsys_hh1_lindley<T>(lambda, pa, mu, ps, Wn[0], mmax)
1521 : qsys::qsys_hh1_lindley<T>(lambda, pa, mu, ps, Wn[0]),
1522 true);
1523 return lindley_results(hasM ? qsys::qsys_hh1_lindley<T>(lambda, pa, mu, ps, Wn, mmax)
1524 : qsys::qsys_hh1_lindley<T>(lambda, pa, mu, ps, Wn),
1525 false);
1526 }
1527};
1528
1529struct OpQsysMm1Lindley {
1530 template <class T>
1531 static Json run(Args& a) {
1532 const T lambda = a.number<T>("lambda");
1533 const T mu = a.number<T>("mu");
1534 const bool scalar = !a.get("Wn").is_array();
1535 const std::vector<T> Wn = a.vector<T>("Wn");
1536 const bool hasM = a.has("mmax");
1537 const unsigned mmax = a.uinteger("mmax", 0);
1538 a.done();
1539 if (scalar)
1540 return lindley_results(hasM ? qsys::qsys_mm1_lindley<T>(lambda, mu, Wn[0], mmax)
1541 : qsys::qsys_mm1_lindley<T>(lambda, mu, Wn[0]),
1542 true);
1543 return lindley_results(hasM ? qsys::qsys_mm1_lindley<T>(lambda, mu, Wn, mmax)
1544 : qsys::qsys_mm1_lindley<T>(lambda, mu, Wn),
1545 false);
1546 }
1547};
1548
1549struct OpQsysLindleyMoment {
1550 template <class T>
1551 static Json run(Args& a) {
1552 const T lambda = a.number<T>("lambda");
1553 const T mu = a.number<T>("mu");
1554 const bool scalar = !a.get("Wn").is_array();
1555 const std::vector<T> Wn = a.vector<T>("Wn");
1556 const unsigned m = a.uinteger("m");
1557 a.done();
1558 Json out;
1559 if (scalar)
1560 out["value"] = encode_scalar(qsys::qsys_lindley_moment<T>(lambda, mu, Wn[0], m));
1561 else
1562 out["value"] = encode_vector(qsys::qsys_lindley_moment<T>(lambda, mu, Wn, m));
1563 return out;
1564 }
1565};
1566
1567struct OpQsysMm1TandemLindley {
1568 template <class T>
1569 static Json run(Args& a) {
1570 const T lambda = a.number<T>("lambda");
1571 const T mu1 = a.number<T>("mu1");
1572 const T mu2 = a.number<T>("mu2");
1573 const bool s1 = !a.get("Wk").is_array();
1574 const bool s2 = !a.get("Wk1").is_array();
1575 const std::vector<T> Wk = a.vector<T>("Wk");
1576 const std::vector<T> Wk1 = a.vector<T>("Wk1");
1577 a.done();
1578 const bool scalar = s1 && s2;
1579 const qsys::Mm1TandemLindleyResult<T> r =
1580 scalar ? qsys::qsys_mm1_tandem_lindley<T>(lambda, mu1, mu2, Wk[0], Wk1[0])
1581 : qsys::qsys_mm1_tandem_lindley<T>(lambda, mu1, mu2, Wk, Wk1);
1582 Json out;
1583 out["mean"] = encode_shaped(r.mean, scalar);
1584 out["interdepMean"] = encode_shaped(r.interdepMean, scalar);
1585 out["idleProb"] = encode_shaped(r.idleProb, scalar);
1586 out["analyzer"] = r.analyzer;
1587 return out;
1588 }
1589};
1590
1591struct OpQsysTandemLindley {
1592 template <class T>
1593 static Json run(Args& a) {
1594 const std::vector<T> A = a.vector<T>("A");
1595 const Matrix<T> S = a.matrix<T>("S");
1596 const bool hasW0 = a.has("W0");
1597 const std::vector<T> W0 = a.vector_or_empty<T>("W0");
1598 a.done();
1599 const qsys::TandemLindleyResult<T> r =
1600 hasW0 ? qsys::qsys_tandem_lindley<T>(A, S, W0) : qsys::qsys_tandem_lindley<T>(A, S);
1601 Json out;
1602 out["W"] = encode_matrix(r.W);
1603 out["G"] = encode_matrix(r.G);
1604 out["T"] = encode_matrix(r.T_);
1605 out["departure"] = encode_matrix(r.departure);
1606 out["analyzer"] = r.analyzer;
1607 return out;
1608 }
1609};
1610
1611struct OpQsysMm1Ps {
1612 template <class T>
1613 static Json run(Args& a) {
1614 const std::vector<T> lambda = a.vector<T>("lambda");
1615 const std::vector<T> mu = a.vector<T>("mu");
1616 a.done();
1617 const qsys::Mm1PsResult<T> r = qsys::qsys_mm1_ps<T>(lambda, mu);
1618 Json out;
1619 out["W"] = encode_vector(r.W);
1620 out["W2"] = encode_vector(r.W2);
1621 out["alpha"] = encode_scalar(r.alpha);
1622 return out;
1623 }
1624};
1625
1626// ---------------------------------------------------------------------------
1627// qsys: the remaining stand-alone systems
1628// ---------------------------------------------------------------------------
1629
1630/**
1631 * qsys_mg1_ps(lambda, svc, svcparam, ...), the phase-type path: svc is the
1632 * initial vector alpha and svcparam the subgenerator T. The transform-handle
1633 * path (svc a handle, svcparam the mean) and the 'pdf' option are handles and
1634 * cannot cross. The handle-valued outputs are not reported; their tabulations
1635 * on x, s and t are.
1636 */
1637struct OpQsysMg1Ps {
1638 static Json run(Args& a) {
1639 const double lambda = a.number<double>("lambda");
1640 if (a.get("svc").is_string())
1641 throw UnsupportedError(ErrorKind{"JsonApi"},
1642 "qsys_mg1_ps: argument 'svc' cannot cross the JSON API boundary as a transform "
1643 "handle; give the phase-type initial vector alpha, with svcparam its "
1644 "subgenerator T");
1645 const std::vector<double> alpha = a.vector<double>("svc");
1646 const Matrix<double> Tm = a.matrix<double>("svcparam");
1647 qsys::Mg1PsOptions opt;
1648 if (a.has("x")) opt.x = a.vector<double>("x");
1649 if (a.has("s")) opt.s = a.vector<double>("s");
1650 if (a.has("t")) opt.t = a.vector<double>("t");
1651 opt.nterms = a.count("nterms", opt.nterms);
1652 a.unsupported("pdf", "it is a density function handle; on the phase-type path the port "
1653 "derives the density itself");
1654 a.done();
1655 const qsys::Mg1PsResult r = qsys::qsys_mg1_ps(lambda, alpha, Tm, opt);
1656 Json out;
1657 out["rho"] = r.rho;
1658 out["m1"] = r.m1;
1659 out["m2"] = r.m2;
1660 out["x"] = encode_vector(r.x);
1661 out["s"] = encode_vector(r.s);
1662 out["t"] = encode_vector(r.t);
1663 out["lstCondVal"] = encode_matrix(r.lstCondVal);
1664 out["lstUncondVal"] = encode_vector(r.lstUncondVal);
1665 out["atomCond"] = encode_vector(r.atomCond);
1666 out["atomUncond"] = r.atomUncond;
1667 out["meanCond"] = encode_vector(r.meanCond);
1668 out["m2Cond"] = encode_vector(r.m2Cond);
1669 out["varCond"] = encode_vector(r.varCond);
1670 out["meanUncond"] = r.meanUncond;
1671 out["m2Uncond"] = r.m2Uncond;
1672 out["varUncond"] = r.varUncond;
1673 out["pdfCond"] = encode_matrix(r.pdfCond);
1674 out["cdfCond"] = encode_matrix(r.cdfCond);
1675 out["pdfUncond"] = encode_vector(r.pdfUncond);
1676 out["cdfUncond"] = encode_vector(r.cdfUncond);
1677 return out;
1678 }
1679};
1680
1681/**
1682 * qsys_bmapm1(D, mu, ...). The port's tuned overload takes all five options;
1683 * Uniformization and MaxLevel have the port's own "select automatically"
1684 * values (<= 0 and 0), which are what MATLAB's empty defaults mean, while
1685 * MaxIter, Tolerance and TailTolerance have numeric defaults that are not
1686 * restated: naming any option therefore requires those three.
1687 */
1688struct OpQsysBmapm1 {
1689 template <class T>
1690 static Json run(Args& a) {
1691 const std::vector<Matrix<T> > D = a.matrices<T>("D");
1692 const T mu = a.number<T>("mu");
1693 const bool tuned = a.has("Uniformization") || a.has("MaxLevel") || a.has("MaxIter") ||
1694 a.has("Tolerance") || a.has("TailTolerance");
1695 if (tuned && !(a.has("MaxIter") && a.has("Tolerance") && a.has("TailTolerance")))
1696 throw InputError("qsys_bmapm1: the port takes its options together; with any of them "
1697 "give 'MaxIter', 'Tolerance' and 'TailTolerance'");
1698 const T q = a.scalar<T>("Uniformization", num_traits<T>::from_int(0));
1699 const std::size_t maxLevel = a.count("MaxLevel", 0);
1700 const unsigned maxIter = a.uinteger("MaxIter", 0);
1701 const T tol = a.scalar<T>("Tolerance", num_traits<T>::from_int(0));
1702 const double tailTol = a.scalar<double>("TailTolerance", 0.0);
1703 a.done();
1704 const qsys::BmapM1Result<T> r =
1705 tuned ? qsys::qsys_bmapm1<T>(D, mu, q, maxLevel, maxIter, tol, tailTol)
1706 : qsys::qsys_bmapm1<T>(D, mu);
1707 Json out;
1708 out["theta"] = encode_vector(r.theta);
1709 out["lambda"] = encode_scalar(r.lambda);
1710 out["rho"] = encode_scalar(r.rho);
1711 out["q"] = encode_scalar(r.q);
1712 out["A0"] = encode_matrix(r.A0);
1713 out["A1"] = encode_matrix(r.A1);
1714 out["B0"] = encode_matrix(r.B0);
1715 out["Bk"] = encode_matrices(r.Bk);
1716 out["A"] = encode_matrix(r.A);
1717 out["alpha"] = encode_vector(r.alpha);
1718 out["G"] = encode_matrix(r.G);
1719 out["drift"] = encode_scalar(r.drift);
1720 out["decayRate"] = r.decayRate;
1721 out["levelProb"] = encode_matrix(r.levelProb);
1722 out["pi0"] = encode_scalar(r.pi0);
1723 out["meanQueueLength"] = encode_scalar(r.meanQueueLength);
1724 out["utilization"] = encode_scalar(r.utilization);
1725 out["throughput"] = encode_scalar(r.throughput);
1726 out["truncLevel"] = encode_count(r.truncLevel);
1727 out["truncError"] = r.truncError;
1728 out["gConverged"] = r.gConverged;
1729 return out;
1730 }
1731};
1732
1733/** qsys_maxima_twomoment(n, mean_, cs2, 'q', 'exactFitted'), positional in the port. */
1734struct OpQsysMaximaTwomoment {
1735 template <class T>
1736 static Json run(Args& a) {
1737 const std::size_t n = a.count("n");
1738 const T mean = a.number<T>("mean_");
1739 const T cs2 = a.number<T>("cs2");
1740 const std::size_t k = prefix_count(a, {"q", "exactFitted"}, "qsys_maxima_twomoment");
1741 const T q = k >= 1 ? a.number<T>("q") : T();
1742 const bool ef = k >= 2 ? a.boolean("exactFitted", false) : false;
1743 a.done();
1744 const qsys::MaximaResult<T> r =
1745 k == 0 ? qsys::qsys_maxima_twomoment<T>(n, mean, cs2)
1746 : (k == 1 ? qsys::qsys_maxima_twomoment<T>(n, mean, cs2, q)
1747 : qsys::qsys_maxima_twomoment<T>(n, mean, cs2, q, ef));
1748 Json out;
1749 out["value"] = encode_scalar(r.value);
1750 out["slope"] = encode_scalar(r.slope);
1751 out["eta"] = encode_scalar(r.eta);
1752 out["threshold"] = encode_scalar(r.threshold);
1753 out["reliable"] = r.reliable;
1754 out["family"] = r.family;
1755 if (r.hasFitted) out["exactFittedValue"] = encode_scalar(r.exactFittedValue);
1756 return out;
1757 }
1758};
1759
1760/**
1761 * qsys_mgisrgi_whitt(lambda, mu, s, r, patience, ...). r may be the string
1762 * "Inf". A numeric patience is the exponential rate theta, as in MATLAB; the
1763 * hazard and ccdf forms are function handles and cannot cross. The options
1764 * keep the port's MgisrgiOptions defaults unless named.
1765 */
1766struct OpQsysMgisrgiWhitt {
1767 template <class T>
1768 static Json run(Args& a) {
1769 const T lambda = a.number<T>("lambda");
1770 const T mu = a.number<T>("mu");
1771 const unsigned s = a.uinteger("s");
1772 const double r = a.number<double>("r");
1773 const Json& pj = a.get("patience");
1774 if (pj.is_object() || pj.is_string())
1775 throw UnsupportedError(ErrorKind{"JsonApi"},
1776 "qsys_mgisrgi_whitt: argument 'patience' cannot cross the JSON API boundary as a "
1777 "hazard or ccdf function handle; give the exponential rate theta as a number");
1778 const qsys::Patience<T> patience =
1779 qsys::Patience<T>::exponential(a.number<T>("patience"));
1780 qsys::MgisrgiOptions opt;
1781 if (a.has("wPoints")) opt.wPoints = a.vector<double>("wPoints");
1782 opt.maxQueue = a.count("maxQueue", opt.maxQueue);
1783 opt.tol = a.scalar<double>("tol", opt.tol);
1784 opt.invMethod = a.text("invMethod", opt.invMethod);
1785 opt.invN = a.count("invN", opt.invN);
1786 a.done();
1787 const qsys::QsysAbandonResult<T> res =
1788 qsys::qsys_mgisrgi_whitt<T>(lambda, mu, s, r, patience, opt);
1789 Json out;
1790 out["queueLengthDist"] = encode_vector(res.queueLengthDist);
1791 out["probLoss"] = encode_scalar(res.probLoss);
1792 out["probNoWait"] = encode_scalar(res.probNoWait);
1793 out["probServed"] = encode_scalar(res.probServed);
1794 out["probAbandon"] = encode_scalar(res.probAbandon);
1795 out["meanNumber"] = encode_scalar(res.meanNumber);
1796 out["varNumber"] = encode_scalar(res.varNumber);
1797 out["meanQueueLength"] = encode_scalar(res.meanQueueLength);
1798 out["varQueueLength"] = encode_scalar(res.varQueueLength);
1799 out["utilization"] = encode_scalar(res.utilization);
1800 out["throughput"] = encode_scalar(res.throughput);
1801 out["abandonRate"] = encode_scalar(res.abandonRate);
1802 out["meanWaitServed"] = encode_scalar(res.meanWaitServed);
1803 out["varWaitServed"] = encode_scalar(res.varWaitServed);
1804 out["meanWaitAbandon"] = encode_scalar(res.meanWaitAbandon);
1805 out["varWaitAbandon"] = encode_scalar(res.varWaitAbandon);
1806 out["meanWait"] = encode_scalar(res.meanWait);
1807 out["secondMomentWait"] = encode_scalar(res.secondMomentWait);
1808 out["abandonRates"] = encode_vector(res.abandonRates);
1809 out["totalAbandonRates"] = encode_vector(res.totalAbandonRates);
1810 out["numWaitingSpaces"] = encode_count(res.numWaitingSpaces);
1811 out["exponentialPatience"] = res.exponentialPatience;
1812 out["patienceRate"] = encode_scalar(res.patienceRate);
1813 out["waitPoints"] = encode_vector(res.waitPoints);
1814 out["cdfWaitServed"] = encode_vector(res.cdfWaitServed);
1815 out["cdfWaitAbandon"] = encode_vector(res.cdfWaitAbandon);
1816 out["cdfWait"] = encode_vector(res.cdfWait);
1817 return out;
1818 }
1819};
1820
1821/** qsys_mapmapc(D0, D1, S0, S1, c, 'maxNumComp', 'numWMoms'), positional in the port. */
1822struct OpQsysMapmapc {
1823 template <class T>
1824 static Json run(Args& a) {
1825 const mam::Map<T> arrival = read_map<T>(a, "D0", "D1");
1826 const mam::Map<T> service = read_map<T>(a, "S0", "S1");
1827 const unsigned c = a.uinteger("c");
1828 const std::size_t k = prefix_count(a, {"maxNumComp", "numWMoms"}, "qsys_mapmapc");
1829 const std::size_t dsz = k >= 1 ? a.count("maxNumComp") : 0;
1830 const std::size_t nw = k >= 2 ? a.count("numWMoms") : 0;
1831 a.done();
1832 const qsys::MapPhcResult<T> r =
1833 k == 0 ? qsys::qsys_mapmapc<T>(arrival, service, c)
1834 : (k == 1 ? qsys::qsys_mapmapc<T>(arrival, service, c, dsz)
1835 : qsys::qsys_mapmapc<T>(arrival, service, c, dsz, nw));
1836 Json out;
1837 out["meanQueueLength"] = encode_scalar(r.meanQueueLength);
1838 out["meanWaitingTime"] = encode_scalar(r.meanWaitingTime);
1839 out["meanSojournTime"] = encode_scalar(r.meanSojournTime);
1840 out["utilization"] = encode_scalar(r.utilization);
1841 out["queueLengthDist"] = encode_vector(r.queueLengthDist);
1842 out["waitingTimeMoments"] = encode_vector(r.waitingTimeMoments);
1843 out["waitingTimeCCDF"] = encode_vector(r.waitingTimeCCDF);
1844 out["waitingTimePoints"] = encode_vector(r.waitingTimePoints);
1845 out["probWait"] = encode_scalar(r.probWait);
1846 out["phaseCount"] = encode_count(r.phaseCount);
1847 return out;
1848 }
1849};
1850
1851/**
1852 * qsys_mmapmap1(D, svc, 'carry'). D is MATLAB's BUTools-layout cell
1853 * {D0, D1_1, ..., D1_K}: the per-mark blocks, whose sum is the aggregate D1.
1854 * svc is a list of [S0, S1] pairs, one per mark.
1855 */
1856struct OpQsysMmapmap1 {
1857 template <class T>
1858 static Json run(Args& a) {
1859 const std::vector<Matrix<T> > D = a.matrices<T>("D");
1860 const std::vector<mam::Map<T> > svc = maps_from_json<T>(a.get("svc"), "qsys_mmapmap1: svc");
1861 const bool hasCarry = a.has("carry");
1862 const std::vector<int> ci = a.ints_or_empty("carry");
1863 a.done();
1864 if (D.size() < 2)
1865 throw InputError("qsys_mmapmap1: D must be {D0, D1_1, ..., D1_K} with K >= 1");
1866 mam::Mmap<T> mm;
1867 mm.D0 = D[0];
1868 mm.D1 = D[1];
1869 mm.Dc.push_back(D[1]);
1870 for (std::size_t k = 2; k < D.size(); ++k) {
1871 if (D[k].rows() != mm.D1.rows() || D[k].cols() != mm.D1.cols())
1872 throw InputError("qsys_mmapmap1: the blocks of D must share one size");
1873 for (std::size_t i = 0; i < mm.D1.rows(); ++i)
1874 for (std::size_t j = 0; j < mm.D1.cols(); ++j)
1875 mm.D1(i, j) = T(mm.D1(i, j) + D[k](i, j));
1876 mm.Dc.push_back(D[k]);
1877 }
1878 std::vector<bool> carry;
1879 for (std::size_t i = 0; i < ci.size(); ++i) {
1880 if (ci[i] != 0 && ci[i] != 1)
1881 throw InputError("qsys_mmapmap1: 'carry' holds flags, 0 or 1");
1882 carry.push_back(ci[i] != 0);
1883 }
1884 const qsys::MmapMap1Result<T> r = hasCarry ? qsys::qsys_mmapmap1<T>(mm, svc, carry)
1885 : qsys::qsys_mmapmap1<T>(mm, svc);
1886 Json out;
1887 out["meanQueueLength"] = encode_vector(r.meanQueueLength);
1888 out["meanSojournTime"] = encode_vector(r.meanSojournTime);
1889 out["arrivalRate"] = encode_vector(r.arrivalRate);
1890 Json cj = Json::array();
1891 for (std::size_t i = 0; i < r.carry.size(); ++i) cj.push_back(static_cast<bool>(r.carry[i]));
1892 out["carry"] = cj;
1893 out["markCount"] = encode_count(r.markCount);
1894 return out;
1895 }
1896};
1897
1898// ---------------------------------------------------------------------------
1899// sim: planning and option helpers
1900// ---------------------------------------------------------------------------
1901
1902/** sim_quest_options(options): the MATLAB options struct, validated and completed. */
1903struct OpSimQuestOptions {
1904 static Json run(Args& a) {
1905 sim::QuestOptions in;
1906 const Json* oj = a.opt("options");
1907 if (oj) {
1908 Args o(*oj, "sim_quest_options: options");
1909 in.b0 = static_cast<long>(o.integer("b0", static_cast<int>(in.b0)));
1910 in.m0 = static_cast<long>(o.integer("m0", static_cast<int>(in.m0)));
1911 if (o.has("s")) {
1912 const std::vector<int> s = o.ints("s");
1913 in.s.assign(s.begin(), s.end());
1914 }
1915 in.beta = o.scalar<double>("beta", in.beta);
1916 in.eta = o.scalar<double>("eta", in.eta);
1917 in.theta = o.scalar<double>("theta", in.theta);
1918 in.weight = o.scalar<double>("weight", in.weight);
1919 in.force = o.boolean("force", in.force);
1920 o.done();
1921 }
1922 a.done();
1923 const sim::QuestOptions r = oj ? sim::sim_quest_options(in) : sim::sim_quest_options();
1924 Json opt;
1925 opt["b0"] = static_cast<long long>(r.b0);
1926 opt["m0"] = static_cast<long long>(r.m0);
1927 Json s = Json::array();
1928 for (std::size_t i = 0; i < r.s.size(); ++i) s.push_back(static_cast<long long>(r.s[i]));
1929 opt["s"] = s;
1930 opt["beta"] = r.beta;
1931 opt["eta"] = r.eta;
1932 opt["theta"] = r.theta;
1933 opt["weight"] = r.weight;
1934 opt["force"] = r.force;
1935 Json out;
1936 out["opt"] = opt;
1937 return out;
1938 }
1939};
1940
1941struct OpSimQuestHeuristicCi {
1942 template <class T>
1943 static Json run(Args& a) {
1944 if constexpr (!num_traits<T>::has_transcendental) {
1945 throw UnsupportedError(ErrorKind{"Arithmetic"}, sim_exact_refusal(
1946 "sim_quest_heuristic_ci", "the interval is a quantile times a square root"));
1947 } else {
1948 const std::vector<T> bqe = a.vector<T>("bqe");
1949 const T centre = a.number<T>("centre");
1950 const T Ap = a.number<T>("Ap");
1951 const T Np = a.number<T>("Np");
1952 const std::size_t nstar = a.count("nstar");
1953 const double alpha = a.number<double>("alpha");
1954 const bool ac = required_flag(a, "useAutocorr", "sim_quest_heuristic_ci");
1955 a.done();
1956 const sim::QuestInterval<T> r =
1957 sim::sim_quest_heuristic_ci<T>(bqe, centre, Ap, Np, nstar, alpha, ac);
1958 Json out;
1959 out["lower"] = encode_scalar(r.lower);
1960 out["upper"] = encode_scalar(r.upper);
1961 return out;
1962 }
1963 }
1964};
1965
1966/** sim_runlength(mean_, asymVar, 'relPrecision', 'confidence', 'runLength'), positional. */
1967struct OpSimRunlength {
1968 template <class T>
1969 static Json run(Args& a) {
1970 if constexpr (!num_traits<T>::has_transcendental) {
1971 throw UnsupportedError(ErrorKind{"Arithmetic"}, sim_exact_refusal(
1972 "sim_runlength", "the run length is set by a normal quantile"));
1973 } else {
1974 const T mean = a.number<T>("mean_");
1975 const T av = a.number<T>("asymVar");
1976 const std::size_t k =
1977 prefix_count(a, {"relPrecision", "confidence", "runLength"}, "sim_runlength");
1978 const T rp = k >= 1 ? a.number<T>("relPrecision") : T();
1979 const T cf = k >= 2 ? a.number<T>("confidence") : T();
1980 const T rl = k >= 3 ? a.number<T>("runLength") : T();
1981 a.done();
1982 sim::RunLengthResult<T> r;
1983 if (k == 0) r = sim::sim_runlength<T>(mean, av);
1984 else if (k == 1) r = sim::sim_runlength<T>(mean, av, rp);
1985 else if (k == 2) r = sim::sim_runlength<T>(mean, av, rp, cf);
1986 else r = sim::sim_runlength<T>(mean, av, rp, cf, rl);
1987 Json out;
1988 out["requiredRunLength"] = encode_scalar(r.requiredRunLength);
1989 out["z"] = encode_scalar(r.z);
1990 if (r.hasRun) {
1991 out["halfWidth"] = encode_scalar(r.halfWidth);
1992 out["achievedRelPrecision"] = encode_scalar(r.achievedRelPrecision);
1993 }
1994 return out;
1995 }
1996 }
1997};
1998
1999// ---------------------------------------------------------------------------
2000// Exact closed forms: finite-source, loss, priority, busy-period, setup and
2001// renewal-arrival queues. A law argument (svc, LA, EU, lst) is the model.json
2002// distribution record; MATLAB's LST-handle form has no JSON spelling.
2003// ---------------------------------------------------------------------------
2004
2005/** The refusal of an LST function handle where the reference also takes one. */
2006[[noreturn]] void refuse_lst_handle(const std::string& fn, const char* key) {
2007 throw UnsupportedError(ErrorKind{"JsonApi"},
2008 fn + ": argument '" + key + "' cannot cross the JSON API boundary as an LST function "
2009 "handle; give it as a distribution record {\"type\": ..., ...}");
2010}
2011
2012/** qsys_mmckk(alpha, mu, c, K, 'tPoints', 'percentiles'): M/M/c/K/K. */
2013struct OpQsysMmckk {
2014 static Json run(Args& a) {
2015 const double alpha = a.number<double>("alpha");
2016 const double mu = a.number<double>("mu");
2017 const unsigned c = a.uinteger("c");
2018 const unsigned K = a.uinteger("K");
2019 const qsys::QsysResOptions o = read_qsys_res_options(a, false);
2020 a.done();
2021 return encode_qsys_res(qsys::qsys_mmckk(alpha, mu, c, K, o));
2022 }
2023};
2024
2025/** qsys_engset(alpha, mu, c, K): the Engset loss system. */
2026struct OpQsysEngset {
2027 static Json run(Args& a) {
2028 const double alpha = a.number<double>("alpha");
2029 const double mu = a.number<double>("mu");
2030 const unsigned c = a.uinteger("c");
2031 const unsigned K = a.uinteger("K");
2032 a.done();
2033 return encode_qsys_res(qsys::qsys_engset(alpha, mu, c, K));
2034 }
2035};
2036
2037/** qsys_mg1kk(alpha, K, svc[, ES]): the finite-source M/G/1//K queue (Takacs). */
2038struct OpQsysMg1kk {
2039 static Json run(Args& a) {
2040 const double alpha = a.number<double>("alpha");
2041 const unsigned K = a.uinteger("K");
2042 if (a.get("svc").is_string()) refuse_lst_handle("qsys_mg1kk", "svc");
2043 const lang::Distrib<double> svc = dist_record<double>(a.get("svc"), "qsys_mg1kk: svc");
2044 // With a Distribution the reference reads the mean from the law and ignores ES.
2045 if (a.has("ES")) (void)a.number<double>("ES");
2046 a.done();
2047 return encode_qsys_res(qsys::qsys_mg1kk(alpha, K, svc));
2048 }
2049};
2050
2051/** qsys_ddckk(EO, ES, c, K): the deterministic finite-source D/D/c/K/K queue. */
2052struct OpQsysDdckk {
2053 template <class T>
2054 static Json run(Args& a) {
2055 const T EO = a.number<T>("EO");
2056 const T ES = a.number<T>("ES");
2057 const unsigned c = a.uinteger("c");
2058 const unsigned K = a.uinteger("K");
2059 a.done();
2060 return encode_qsys_res(qsys::qsys_ddckk(EO, ES, c, K));
2061 }
2062};
2063
2064/** qsys_mg1_setup(lambda, mu, cs, EU[, EU2]): M/G/1 with setup times; EU a number or a law. */
2065struct OpQsysMg1Setup {
2066 template <class T>
2067 static Json run(Args& a) {
2068 const T lambda = a.number<T>("lambda");
2069 const T mu = a.number<T>("mu");
2070 const T cs = a.number<T>("cs");
2071 qsys::Mg1SetupResult<T> r;
2072 if (a.get("EU").is_object()) {
2073 const lang::Distrib<T> U = dist_record<T>(a.get("EU"), "qsys_mg1_setup: EU");
2074 a.done();
2075 r = qsys::qsys_mg1_setup(lambda, mu, cs, U);
2076 } else {
2077 const T EU = a.number<T>("EU");
2078 if (!a.has("EU2"))
2079 throw InputError(ErrorKind("Argument"),
2080 "qsys_mg1_setup: qsys_mg1_setup needs the second moment EU2 of the setup time "
2081 "after EU, or a setup Distribution in place of EU");
2082 const T EU2 = a.number<T>("EU2");
2083 a.done();
2084 r = qsys::qsys_mg1_setup(lambda, mu, cs, EU, EU2);
2085 }
2086 Json out;
2087 out["W"] = encode_scalar(r.W);
2088 out["Wq"] = encode_scalar(r.Wq);
2089 out["U"] = encode_scalar(r.U);
2090 out["Q"] = encode_scalar(r.Q);
2091 out["res"] = encode_qsys_res(r.res);
2092 return out;
2093 }
2094};
2095
2096/** qsys_mg1_exceptional(lambda, mu, cs, muf, csf): M/G/1 with exceptional first service. */
2097struct OpQsysMg1Exceptional {
2098 template <class T>
2099 static Json run(Args& a) {
2100 const T lambda = a.number<T>("lambda");
2101 const T mu = a.number<T>("mu");
2102 const T cs = a.number<T>("cs");
2103 const T muf = a.number<T>("muf");
2104 const T csf = a.number<T>("csf");
2105 a.done();
2106 const qsys::Mg1ExceptionalResult<T> r = qsys::qsys_mg1_exceptional(lambda, mu, cs, muf, csf);
2107 Json out;
2108 out["W"] = encode_scalar(r.W);
2109 out["Wq"] = encode_scalar(r.Wq);
2110 out["U"] = encode_scalar(r.U);
2111 out["Q"] = encode_scalar(r.Q);
2112 out["res"] = encode_qsys_res(r.res);
2113 return out;
2114 }
2115};
2116
2117/** qsys_mmcc(lambda, mu, c): the Erlang-B loss system. */
2118struct OpQsysMmcc {
2119 static Json run(Args& a) {
2120 const double lambda = a.number<double>("lambda");
2121 const double mu = a.number<double>("mu");
2122 const unsigned c = a.uinteger("c");
2123 a.done();
2124 return encode_qsys_res(qsys::qsys_mmcc(lambda, mu, c));
2125 }
2126};
2127
2128/** qsys_mgc_ps(lambda, mu, c): the insensitive M/G/c processor-sharing queue. */
2129struct OpQsysMgcPs {
2130 static Json run(Args& a) {
2131 const double lambda = a.number<double>("lambda");
2132 const double mu = a.number<double>("mu");
2133 const unsigned c = a.uinteger("c");
2134 a.done();
2135 return encode_qsys_res(qsys::qsys_mgc_ps(lambda, mu, c));
2136 }
2137};
2138
2139/** qsys_mg1_prio_pr(lambda, mu, cs, 'ES3'): preemptive-resume priority M/G/1. */
2140struct OpQsysMg1PrioPr {
2141 template <class T>
2142 static Json run(Args& a) {
2143 const std::vector<T> lambda = a.vector<T>("lambda");
2144 const std::vector<T> mu = a.vector<T>("mu");
2145 const std::vector<T> cs = a.vector<T>("cs");
2146 const std::vector<T> ES3 = a.vector_or_empty<T>("ES3");
2147 a.done();
2148 const qsys::Mg1PrioPrResult<T> r = qsys::qsys_mg1_prio_pr(lambda, mu, cs, ES3);
2149 Json out;
2150 out["W"] = encode_vector(r.W);
2151 out["rhohat"] = encode_scalar(r.rho);
2152 out["res"] = encode_qsys_res(r.res);
2153 return out;
2154 }
2155};
2156
2157/** qsys_mmc_prio(lambda, mu, c): non-preemptive priority M/M/c, common service rate. */
2158struct OpQsysMmcPrio {
2159 template <class T>
2160 static Json run(Args& a) {
2161 const std::vector<T> lambda = a.vector<T>("lambda");
2162 const T mu = a.number<T>("mu");
2163 const unsigned c = a.uinteger("c");
2164 a.done();
2165 const qsys::MmcPrioResult<T> r = qsys::qsys_mmc_prio(lambda, mu, c);
2166 Json out;
2167 out["W"] = encode_vector(r.W);
2168 out["rhohat"] = encode_scalar(r.rho);
2169 out["res"] = encode_qsys_res(r.res);
2170 return out;
2171 }
2172};
2173
2174/** qsys_mmc_prio_tardiness(lambda, mu, c, d[, type, method]): exact priority M/M/c tardiness (HGK 2013). */
2175struct OpQsysMmcPrioTardiness {
2176 static Json run(Args& a) {
2177 const std::vector<double> lambda = a.vector<double>("lambda");
2178 const double mu = a.number<double>("mu");
2179 const unsigned c = a.uinteger("c");
2180 const std::vector<double> d = a.vector<double>("d");
2181 const std::string type = a.text("type", "np");
2182 const std::string method = a.text("method", "euler");
2183 a.done();
2184 const qsys::MmcPrioTardinessResult r = qsys::qsys_mmc_prio_tardiness(lambda, mu, c, d, type, method);
2185 Json out;
2186 out["Tard"] = encode_vector(r.Tard);
2187 out["Ptard"] = encode_vector(r.Ptard);
2188 out["res"] = encode_qsys_res(r.res);
2189 return out;
2190 }
2191};
2192
2193/** qsys_mmc_prio_eta(lambda, mu, c, r, v, q, d[, type, method]): expected tardiness at arrival (HGK 2013). */
2194struct OpQsysMmcPrioEta {
2195 static Json run(Args& a) {
2196 const std::vector<double> lambda = a.vector<double>("lambda");
2197 const double mu = a.number<double>("mu");
2198 const unsigned c = a.uinteger("c");
2199 const unsigned r = a.uinteger("r");
2200 const unsigned v = a.uinteger("v");
2201 const unsigned q = a.uinteger("q");
2202 const std::vector<double> d = a.vector<double>("d");
2203 const std::string type = a.text("type", "np");
2204 const std::string method = a.text("method", "euler");
2205 a.done();
2206 const qsys::MmcPrioEtaResult e = qsys::qsys_mmc_prio_eta(lambda, mu, c, r, v, q, d, type, method);
2207 Json out;
2208 out["Tard"] = encode_vector(e.Tard);
2209 out["Ptard"] = encode_vector(e.Ptard);
2210 out["S"] = encode_scalar(e.S);
2211 return out;
2212 }
2213};
2214
2215/** qsys_mg1_conservation(lambda, mu, cs[, W]): Kleinrock's conservation law. */
2216struct OpQsysMg1Conservation {
2217 template <class T>
2218 static Json run(Args& a) {
2219 const std::vector<T> lambda = a.vector<T>("lambda");
2220 const std::vector<T> mu = a.vector<T>("mu");
2221 const std::vector<T> cs = a.vector<T>("cs");
2222 const std::vector<T> W = a.vector_or_empty<T>("W");
2223 a.done();
2224 const qsys::Mg1ConservationResult<T> r = qsys::qsys_mg1_conservation(lambda, mu, cs, W);
2225 Json res = Json::object();
2226 res["invariant"] = encode_scalar(r.V);
2227 res["fcfsWaitingTime"] = encode_scalar(r.fcfsWaitingTime);
2228 // MATLAB's NaN when W is not given; JSON has no NaN, so null.
2229 res["residual"] = r.hasResidual ? encode_scalar(r.residual) : Json();
2230 Json out;
2231 out["V"] = encode_scalar(r.V);
2232 out["res"] = res;
2233 return out;
2234 }
2235};
2236
2237/** qsys_mg1_sjf(lambda, mu, cs): non-preemptive shortest-job-first M/G/1. */
2238struct OpQsysMg1Sjf {
2239 static Json run(Args& a) {
2240 const std::vector<double> lambda = a.vector<double>("lambda");
2241 const std::vector<double> mu = a.vector<double>("mu");
2242 const std::vector<double> cs = a.vector<double>("cs");
2243 a.done();
2244 const qsys::Mg1SjfResult r = qsys::qsys_mg1_sjf(lambda, mu, cs);
2245 Json out;
2246 out["W"] = encode_vector(r.W);
2247 out["rhohat"] = r.rhohat;
2248 out["res"] = encode_qsys_res(r.res);
2249 return out;
2250 }
2251};
2252
2253/** qsys_mg1_busy(lambda, mu, cs, 'ES3', 'lst', 'sPoints'): the M/G/1 busy period. */
2254struct OpQsysMg1Busy {
2255 static Json run(Args& a) {
2256 const double lambda = a.number<double>("lambda");
2257 const double mu = a.number<double>("mu");
2258 const double cs = a.number<double>("cs");
2259 qsys::Mg1BusyOptions o;
2260 if (a.has("ES3")) o.ES3 = a.number<double>("ES3");
2261 if (a.has("lst")) {
2262 if (a.get("lst").is_string()) refuse_lst_handle("qsys_mg1_busy", "lst");
2263 const lang::Distrib<double> d = dist_record<double>(a.get("lst"), "qsys_mg1_busy: lst");
2264 o.lst = [d](double s) { return lang::dist_lst(d, s); };
2265 }
2266 if (a.has("sPoints")) o.sPoints = a.vector<double>("sPoints");
2267 a.done();
2268 return encode_qsys_res(qsys::qsys_mg1_busy(lambda, mu, cs, o));
2269 }
2270};
2271
2272/** qsys_gimc(LA, mu, c, 'lambda', 'tPoints', 'percentiles', 'nmax'): the GI/M/c queue. */
2273struct OpQsysGimc {
2274 static Json run(Args& a) {
2275 if (a.get("LA").is_string()) refuse_lst_handle("qsys_gimc", "LA");
2276 const lang::Distrib<double> LA = dist_record<double>(a.get("LA"), "qsys_gimc: LA");
2277 const double mu = a.number<double>("mu");
2278 const unsigned c = a.uinteger("c");
2279 const double lambda = a.has("lambda") ? a.number<double>("lambda") : -1.0;
2280 const qsys::QsysResOptions o = read_qsys_res_options(a);
2281 a.done();
2282 return encode_qsys_res(qsys::qsys_gimc(LA, mu, c, o, lambda));
2283 }
2284};
2285
2286/** qsys_md1(lambda, mu, 'nmax'): the M/D/1 queue. */
2287struct OpQsysMd1 {
2288 static Json run(Args& a) {
2289 const double lambda = a.number<double>("lambda");
2290 const double mu = a.number<double>("mu");
2291 const long nmax = a.has("nmax") ? static_cast<long>(a.count("nmax")) : -1L;
2292 a.done();
2293 return encode_qsys_res(qsys::qsys_md1(lambda, mu, nmax));
2294 }
2295};
2296
2297/** The complex roots and coefficients, as [re, im] pairs. */
2298Json encode_cplx_pairs(const std::vector<std::complex<double> >& v) {
2299 Json out = Json::array();
2300 for (const std::complex<double>& z : v) out.push_back(Json::array({z.real(), z.imag()}));
2301 return out;
2302}
2303
2304/** qsys_mer1(lambda, mu, r, 'tPoints', 'percentiles', 'nmax'): the M/Er/1 queue. */
2305struct OpQsysMer1 {
2306 static Json run(Args& a) {
2307 const double lambda = a.number<double>("lambda");
2308 const double mu = a.number<double>("mu");
2309 const unsigned r = a.uinteger("r");
2310 const qsys::QsysResOptions o = read_qsys_res_options(a);
2311 a.done();
2312 const qsys::Mer1Result m = qsys::qsys_mer1(lambda, mu, r, o);
2313 Json out = encode_qsys_res(m.res);
2314 // MATLAB's res.roots and res.coeffs are complex; a complex number is a pair [re, im].
2315 out["roots"] = encode_cplx_pairs(m.roots);
2316 out["coeffs"] = encode_cplx_pairs(m.coeffs);
2317 return out;
2318 }
2319};
2320
2321/** qsys_mh21(lambda, p1, mu1, mu2, 'tPoints', 'percentiles', 'nmax'): the M/H2/1 queue. */
2322struct OpQsysMh21 {
2323 static Json run(Args& a) {
2324 const double lambda = a.number<double>("lambda");
2325 const double p1 = a.number<double>("p1");
2326 const double mu1 = a.number<double>("mu1");
2327 const double mu2 = a.number<double>("mu2");
2328 const qsys::QsysResOptions o = read_qsys_res_options(a);
2329 a.done();
2330 return encode_qsys_res(qsys::qsys_mh21(lambda, p1, mu1, mu2, o));
2331 }
2332};
2333
2334} // namespace
2335
2337 // moment: all three arithmetics
2338 t["moment_cumulant_from_raw"] = make<OpMomentCumulantFromRaw>();
2339 t["moment_raw_from_cumulant"] = make<OpMomentRawFromCumulant>();
2340 t["moment_factcumulant_from_factorial"] = make<OpMomentFactcumulantFromFactorial>();
2341 t["moment_factorial_from_factcumulant"] = make<OpMomentFactorialFromFactcumulant>();
2342 t["moment_binomial_from_tail"] = make<OpMomentBinomialFromTail>();
2343 t["moment_tail_from_binomial"] = make<OpMomentTailFromBinomial>();
2344 t["moment_housematrix"] = make<OpMomentHousematrix>();
2345 t["moment_tensorsize"] = make<OpMomentTensorsize>();
2346 t["moment_tensortrans"] = make<OpMomentTensortrans>();
2347 t["moment_jointtrans"] = make<OpMomentJointtrans>();
2348 t["moment_joint_aggregate"] = make<OpMjAggregate>();
2349 t["moment_joint_marking"] = make<OpMjMarking>();
2350 t["moment_joint_central_from_raw_mean"] = make<OpMjCentralFromRawMean>();
2351 t["moment_joint_raw_from_central"] = make<OpMjRawFromCentral>();
2352 t["moment_joint_binomial_from_factorial"] = make<OpMjBinomialFromFactorial>();
2353 t["moment_joint_binomial_from_negbinomial"] = make<OpMjBinomialFromNegbinomial>();
2354 t["moment_joint_binomial_from_tail"] = make<OpMjBinomialFromTail>();
2355 t["moment_joint_central_from_raw"] = make<OpMjCentralFromRaw>();
2356 t["moment_joint_central_from_tail"] = make<OpMjCentralFromTail>();
2357 t["moment_joint_cumulant_from_raw"] = make<OpMjCumulantFromRaw>();
2358 t["moment_joint_factcumulant_from_factorial"] = make<OpMjFactcumulantFromFactorial>();
2359 t["moment_joint_factorial_from_binomial"] = make<OpMjFactorialFromBinomial>();
2360 t["moment_joint_factorial_from_factcumulant"] = make<OpMjFactorialFromFactcumulant>();
2361 t["moment_joint_factorial_from_raw"] = make<OpMjFactorialFromRaw>();
2362 t["moment_joint_factorial_from_upfactorial"] = make<OpMjFactorialFromUpfactorial>();
2363 t["moment_joint_negbinomial_from_binomial"] = make<OpMjNegbinomialFromBinomial>();
2364 t["moment_joint_negbinomial_from_upfactorial"] = make<OpMjNegbinomialFromUpfactorial>();
2365 t["moment_joint_raw_from_cumulant"] = make<OpMjRawFromCumulant>();
2366 t["moment_joint_raw_from_factorial"] = make<OpMjRawFromFactorial>();
2367 t["moment_joint_raw_from_upfactorial"] = make<OpMjRawFromUpfactorial>();
2368 t["moment_joint_tail_from_binomial"] = make<OpMjTailFromBinomial>();
2369 t["moment_joint_upfactorial_from_factorial"] = make<OpMjUpfactorialFromFactorial>();
2370 t["moment_joint_upfactorial_from_negbinomial"] = make<OpMjUpfactorialFromNegbinomial>();
2371 t["moment_joint_upfactorial_from_raw"] = make<OpMjUpfactorialFromRaw>();
2372
2373 // fj: all three arithmetics
2374 t["fj_amva"] = make<OpFjAmva>();
2375 t["fj_char_max_blom"] = make<OpFjCharMaxBlom>();
2376 t["fj_cox_fit"] = make<OpFjCoxFit>();
2377 t["fj_dag_makespan"] = make<OpFjDagMakespan>();
2378 t["fj_ism_green"] = make<OpFjIsmGreen>();
2379 t["fj_lst_max_het"] = make<OpFjLstMaxHet>();
2380 t["fj_qgb"] = make<OpFjQgb>();
2381 t["fj_respt_bulk"] = make<OpFjResptBulk>();
2382 t["fj_respt_closed"] = make<OpFjResptClosed>();
2383 t["fj_respt_nosplit"] = make<OpFjResptNosplit>();
2384 t["fj_serialization"] = make<OpFjSerialization>();
2385 t["fj_tsm_capacity"] = make<OpFjTsmCapacity>();
2386 t["fj_xmax_coxian"] = make<OpFjXmaxCoxian>();
2387 t["fj_xmax_het"] = make<OpFjXmaxHet>();
2388 t["fj_xmax_hz"] = make<OpFjXmaxHz>();
2389 t["fj_xmax_moments_het"] = make<OpFjXmaxMomentsHet>();
2390 t["fj_mg1_respt_moments"] = make<OpFjMg1ResptMoments>();
2391 t["fj_branch_members"] = make<OpFjBranchMembers>();
2392 t["fj_dist2fj"] = make<OpFjDist2fj>();
2393 // fj: double and real
2394 t["fj_char_max_discrete"] = make<NeedsTranscendental<OpFjCharMaxDiscrete> >();
2395 t["fj_delay_opt"] = make<NeedsTranscendental<OpFjDelayOpt> >();
2396 t["fj_dispersion"] = make<NeedsTranscendental<OpFjDispersion> >();
2397 t["fj_tail_forktail"] = make<NeedsTranscendental<OpFjTailForktail> >();
2398 t["fj_tail_ordstat"] = make<NeedsTranscendental<OpFjTailOrdstat> >();
2399
2400 // aoi
2401 t["aoi_lst_det"] = make<NeedsTranscendental<OpAoiLstDet> >();
2402 t["aoi_lst_erlang"] = make<OpAoiLstErlang>();
2403 t["aoi_lst_exp"] = make<OpAoiLstExp>();
2404 t["aoi_lst_ph"] = make<OpAoiLstPh>();
2405 t["aoi_dist2ph"] = make<OpAoiDist2ph>();
2406 // qsys: the registry lists all three arithmetics for these six, but the hh1,
2407 // lindley_moment, mm1 and mm1_tandem ports static_assert a transcendental
2408 // scalar, so an exact call is refused here with the Arithmetic reason.
2409 t["qsys_hh1_lindley"] = make<NeedsTranscendental<OpQsysHh1Lindley>>();
2410 t["qsys_lindley_moment"] = make<NeedsTranscendental<OpQsysLindleyMoment>>();
2411 t["qsys_mm1_lindley"] = make<NeedsTranscendental<OpQsysMm1Lindley>>();
2412 t["qsys_mm1_ps"] = make<OpQsysMm1Ps>();
2413 t["qsys_mm1_tandem_lindley"] = make<NeedsTranscendental<OpQsysMm1TandemLindley>>();
2414 t["qsys_tandem_lindley"] = make<OpQsysTandemLindley>();
2415 // qsys: double and real
2416 t["qsys_mapg1k"] = make<NeedsTranscendental<OpQsysMapg1k> >();
2417 t["qsys_mmapg1k"] = make<NeedsTranscendental<OpQsysMmapg1k> >();
2418 t["qsys_mapg1k_perflow"] = make<NeedsTranscendental<OpQsysMapg1kPerflow> >();
2419 t["qsys_svclaw_coeffs"] = make<NeedsTranscendental<OpQsysSvclawCoeffs> >();
2420 t["qsys_ldps_workload"] = make<NeedsTranscendental<OpQsysLdpsWorkload> >();
2421 t["qsys_mmapgk1"] = make<NeedsTranscendental<OpQsysMmapgk1> >();
2422 t["qsys_mmapgk1_lcfspr"] = make<NeedsTranscendental<OpQsysMmapgk1Lcfspr> >();
2423 t["qsys_tandem_ub_ciucu"] = make<NeedsTranscendental<OpQsysTandemUbCiucu> >();
2424 t["qsys_bmapm1"] = make<NeedsTranscendental<OpQsysBmapm1> >();
2425 t["qsys_maxima_twomoment"] = make<NeedsTranscendental<OpQsysMaximaTwomoment> >();
2426 t["qsys_mgisrgi_whitt"] = make<NeedsTranscendental<OpQsysMgisrgiWhitt> >();
2427 t["qsys_mapmapc"] = make<NeedsTranscendental<OpQsysMapmapc> >();
2428 t["qsys_mmapmap1"] = make<NeedsTranscendental<OpQsysMmapmap1> >();
2429 // qsys: double only
2430 t["qsys_mg1_ps"] = make<DoubleOnly<OpQsysMg1Ps> >();
2431 t["qsys_mmckk"] = make<DoubleOnly<OpQsysMmckk> >();
2432 t["qsys_engset"] = make<DoubleOnly<OpQsysEngset> >();
2433 t["qsys_mg1kk"] = make<DoubleOnly<OpQsysMg1kk> >();
2434 t["qsys_mmcc"] = make<DoubleOnly<OpQsysMmcc> >();
2435 t["qsys_mgc_ps"] = make<DoubleOnly<OpQsysMgcPs> >();
2436 t["qsys_mg1_sjf"] = make<DoubleOnly<OpQsysMg1Sjf> >();
2437 t["qsys_mg1_busy"] = make<DoubleOnly<OpQsysMg1Busy> >();
2438 t["qsys_gimc"] = make<DoubleOnly<OpQsysGimc> >();
2439 t["qsys_md1"] = make<DoubleOnly<OpQsysMd1> >();
2440 t["qsys_mer1"] = make<DoubleOnly<OpQsysMer1> >();
2441 t["qsys_mh21"] = make<DoubleOnly<OpQsysMh21> >();
2442 // qsys exact closed forms: all three arithmetics
2443 t["qsys_ddckk"] = make<OpQsysDdckk>();
2444 t["qsys_mg1_setup"] = make<OpQsysMg1Setup>();
2445 t["qsys_mg1_exceptional"] = make<OpQsysMg1Exceptional>();
2446 t["qsys_mg1_prio_pr"] = make<OpQsysMg1PrioPr>();
2447 t["qsys_mmc_prio"] = make<OpQsysMmcPrio>();
2448 t["qsys_mmc_prio_tardiness"] = make<DoubleOnly<OpQsysMmcPrioTardiness> >();
2449 t["qsys_mmc_prio_eta"] = make<DoubleOnly<OpQsysMmcPrioEta> >();
2450 t["qsys_mg1_conservation"] = make<OpQsysMg1Conservation>();
2451
2452 // sim
2453 t["sim_quest_options"] = make<DoubleOnly<OpSimQuestOptions> >();
2454 t["sim_quest_heuristic_ci"] = make<OpSimQuestHeuristicCi>();
2455 t["sim_runlength"] = make<OpSimRunlength>();
2456}
2457
2459 t["qsys_gig1_rq"] =
2460 "its interarrival law IaFun is a function handle (a CDF or transform evaluated by the "
2461 "port), and the port offers no constructor that builds it from a named law.";
2462 t["qsys_mg1k_loss"] =
2463 "its service law svc_density is a density function handle the port integrates, and the "
2464 "port takes no named-law form for it.";
2465 t["qsys_ggingi_tga"] =
2466 "its patience law (patienceCcdf, patiencePdf) and service law (serviceCcdf) are function "
2467 "handles the port integrates, with no named-law form.";
2468 t["qsys_ggisgi_fluid"] =
2469 "its patience and service laws (patienceCcdf, servingCcdf) are function handles the port "
2470 "integrates, with no named-law form.";
2471 t["qsys_gtmtst_fluid"] =
2472 "its time-varying arrival rate, staffing and service rate (lambdaFun, sFun, muFun) and "
2473 "its patience law (patienceCcdf) are function handles of time.";
2474 t["qsys_mtginf"] =
2475 "its time-varying arrival rate lambdaFun is a function handle of time and its service law "
2476 "serviceCcdf a function handle, with no named-law form.";
2477 t["qsys_mtgs0_mol"] =
2478 "its time-varying arrival rate lambdaFun is a function handle of time and its service law "
2479 "serviceCcdf a function handle, with no named-law form.";
2480 t["fj_order_stat"] =
2481 "its branch law F_X is a CDF function handle the port evaluates, with no named-law form.";
2482 t["fj_xmax_hz_het"] =
2483 "its branch laws are a list of CDF function handles the port integrates, with no "
2484 "named-law form.";
2485}
2486
2487} // namespace reg
2488} // namespace line
MAP to PH conversion for the age-of-information solvers.
Laplace-Stieltjes transform of a deterministic (constant) distribution.
Laplace-Stieltjes transform of an Erlang-k distribution.
Laplace-Stieltjes transform of an exponential distribution.
Laplace-Stieltjes transform of a phase-type distribution PH(alpha, T).
Internal to the –api dispatcher: what a per-domain translation unit needs to add entries to the table...
#define LINE_MOMENT_TENSOR_OP(OPNAME, FN, IN, OUT)
#define LINE_MOMENT_VEC_OP(OPNAME, FN, IN, OUT)
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
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
Mean value analysis of a closed network of fork-join subnetworks.
Activities belonging to each branch of an AND-join.
Blom-corrected plotting position for the characteristic maximum.
Characteristic maximum of a lattice random variable.
Two-stage Coxian fit of a mean and a squared coefficient of variation.
Makespan of a task system with precedence constraints.
Deterministic subtask delays that minimise mean dispersion.
Mean subtask dispersion of a split-merge system with Erlang branches.
Conversion of a LINE MAP into the arrival or service descriptor of the fork-join response-time-tail a...
Green's independent server model of simultaneous server requests.
Laplace-Stieltjes transform of the maximum of heterogeneous exponentials.
Mean and variance of the M/G/1 response time, as ForkTail inputs.
Geometric bound on the queue length of a fork-join subnetwork.
Centralized splitting analysed as an M[K]/M/c bulk arrival system.
Varki bound on the residence time of a closed fork-join subnetwork.
Mean response time of the distributed no-splitting parallel system.
Blocking probability and pseudoserver delay of serialization phases.
ForkTail black-box tail-latency approximation for fork-join requests.
Tail latency of a k-of-n (QUORUM) fork-join request.
Saturation throughput of the team service model.
Expected maximum of K i.i.d.
Exact moments of the maximum of heterogeneous exponentials.
Harrison-Zertal approximation of the maximum of i.i.d.
Moments of the maximum of heterogeneous exponentials by recurrence.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Cumulants from raw moments and the inverse, plus the factorial-cumulant pair.
Conversion matrix of one edge of the house of moments.
Joint moment conversions on the house of moments.
Binomial moments from tail moments and the inverse.
Joint moment arrays and the mode products used by every joint conversion.
std::function< T(const T &)> Lst
A Laplace-Stieltjes transform evaluated at real arguments.
Definition aoi_types.h:41
Lst< T > aoi_lst_exp(const T &mu)
Laplace-Stieltjes transform of an exponential distribution.
Definition aoi_lst_exp.h:36
Lst< T > aoi_lst_erlang(unsigned k, const T &mu)
Laplace-Stieltjes transform of an Erlang-k distribution.
Lst< T > aoi_lst_ph(const std::vector< T > &alpha, const Matrix< T > &Tmat)
Laplace-Stieltjes transform of a phase-type distribution PH(alpha, T).
Definition aoi_lst_ph.h:48
AoiPh< T > aoi_dist2ph(const Matrix< T > &D0, const Matrix< T > &D1)
Convert a MAP (D0, D1) into the PH pair (alpha, T).
Definition aoi_dist2ph.h:69
Lst< T > aoi_lst_det(const T &d)
Laplace-Stieltjes transform of a deterministic (constant) distribution.
Definition aoi_lst_det.h:37
std::vector< T > fj_xmax_moments_het(const std::vector< T > &lambda, unsigned n=1)
Moments of the maximum of heterogeneous exponentials by recurrence.
FjDistKind
Which of the two descriptors to build.
Definition fj_dist2fj.h:68
FJCharMaxBlomResult< T > fj_char_max_blom(unsigned K, const std::function< T(const T &)> &Finv=std::function< T(const T &)>(), const T &alpha=num_traits< T >::from_double(0.4886), const T &beta=num_traits< T >::from_double(0.3140))
Blom-corrected plotting position for the characteristic maximum.
T fj_lst_max_het(const std::vector< T > &lambda, const T &s)
Laplace-Stieltjes transform of the maximum of heterogeneous exponentials.
FJIsmGreenResult< T > fj_ism_green(const T &lambda, const T &mu, unsigned s, const std::vector< T > &c)
Green's independent server model of simultaneous server requests.
FJDelayOptResult< T > fj_delay_opt(const std::vector< unsigned > &shape, const std::vector< T > &rate, unsigned maxsweeps=40, const T &dtol=num_traits< T >::from_double(1e-8), unsigned npanels=2000)
Deterministic subtask delays that minimise mean dispersion.
FJXmaxCoxianResult< T > fj_xmax_coxian(unsigned K, const T &mu1, const T &mu2, const T &q)
Expected maximum of K i.i.d.
std::vector< std::vector< std::size_t > > fj_branch_members(const LqnBranchView< T > &lqn, std::size_t joinaidx)
Branch membership of an AND-join.
FJDagMakespanResult< T > fj_dag_makespan(const Matrix< T > &pred, const Matrix< T > &rate)
Makespan of a task system with precedence constraints.
FJResptNosplitResult< T > fj_respt_nosplit(unsigned K, const T &lambda, const T &mu)
Mean response time of the distributed no-splitting parallel system.
FJTsmCapacityResult< T > fj_tsm_capacity(unsigned s, const std::vector< T > &f, const std::vector< unsigned > &r, const std::vector< T > &x)
Saturation throughput of the team service model.
T fj_xmax_het(const std::vector< T > &lambda, unsigned n=1)
Exact moments of the maximum of heterogeneous exponentials.
Definition fj_xmax_het.h:40
FJCharMaxDiscreteResult< T > fj_char_max_discrete(unsigned K, FJDiscreteDist dist, const T &par)
Characteristic maximum of a lattice random variable.
FJSerializationResult< T > fj_serialization(const std::vector< T > &Rs, const T &R0, unsigned M, const T &alpha=num_traits< T >::from_double(0.5))
Blocking probability and pseudoserver delay of serialization phases.
Mg1ResptMoments< T > fj_mg1_respt_moments(const T &lambda, const T &ES, const T &ES2, const T &ES3)
Mean and variance of the M/G/1 response time, as ForkTail inputs.
FjDist< T > fj_dist2fj(const mam::Map< T > &m, FjDistKind kind, FjProcType procType)
Build the fork-join descriptor of a MAP.
Definition fj_dist2fj.h:99
ForkTailResult< T > fj_tail_forktail(const std::vector< T > &ET, const std::vector< T > &VT, const std::vector< T > &K=std::vector< T >(), const T &p_in=num_traits< T >::from_int(99), const std::vector< T > &P=std::vector< T >())
ForkTail black-box tail-latency approximation for fork-join requests.
FJResptClosedResult< T > fj_respt_closed(unsigned K, const T &x, unsigned M, const T &A)
Varki bound on the residence time of a closed fork-join subnetwork.
FJQgbResult< T > fj_qgb(const std::vector< T > &D, const std::vector< unsigned > &P, unsigned M, const T &Z)
Geometric bound on the queue length of a fork-join subnetwork.
Definition fj_qgb.h:52
FJXmaxHzResult< T > fj_xmax_hz(const T &m1, const T &m2, unsigned K)
Harrison-Zertal approximation of the maximum of i.i.d.
Definition fj_xmax_hz.h:47
ForkTailResult< T > fj_tail_ordstat(const std::vector< T > &ET, const std::vector< T > &VT, std::size_t K=1, const T &p_in=num_traits< T >::from_int(99), std::size_t kreq=0)
Tail latency of a k-of-n (QUORUM) fork-join request.
FJAmvaResult< T > fj_amva(const std::vector< T > &D, const std::vector< unsigned > &P, unsigned M, const T &Z)
Mean value analysis of a closed network of fork-join subnetworks.
Definition fj_amva.h:55
FJCoxFitResult< T > fj_cox_fit(const T &m1, const T &c2)
Two-stage Coxian fit of a mean and a squared coefficient of variation.
Definition fj_cox_fit.h:52
FJDispersionResult< T > fj_dispersion(const std::vector< unsigned > &shape, const std::vector< T > &rate, const std::vector< T > &d, const T &tol=num_traits< T >::from_double(1e-10), unsigned npanels=4000)
Mean subtask dispersion of a split-merge system with Erlang branches.
FjProcType
The subset of ProcessType that the fork-join algorithm accepts.
Definition fj_dist2fj.h:65
FJResptBulkResult< T > fj_respt_bulk(unsigned K, const T &lambda, const T &mu, unsigned c, std::size_t nmax=0)
Centralized splitting analysed as an M[K]/M/c bulk arrival system.
FJDiscreteDist
The lattice laws for which the characteristic maximum is closed.
T dist_lst(const Distrib< T > &d, const T &s)
sn.lst: the Laplace-Stieltjes transform E[exp(-sX)].
T dist_cdf(const Distrib< T > &d, const T &x)
F(x) = P{X <= x}, MATLAB's Distribution.evalCDF.
T dist_moment(const Distrib< T > &d, unsigned k)
The k-th raw moment.
double z(double v)
Definition lqn_focus.h:261
bool in(const std::string &s, std::initializer_list< const char * > set)
std::vector< T > moment_joint_aggregate(const MomentTensor< T > &F)
Factorial moments of a total count from the joint factorial moments of its parts.
MomentTensor< T > moment_joint_marking(const std::vector< T > &f, const std::vector< T > &p, const std::vector< std::size_t > &dims)
Joint factorial moments of a multinomially marked count from the aggregate ones.
std::vector< std::size_t > moment_tensorsize(const MomentTensor< T > &A)
Extents with the trailing singleton dimensions removed.
Matrix< T > moment_housematrix(MomentEdge edge, int n)
(n+1)x(n+1) conversion table of the given edge.
MomentTensor< T > moment_tensortrans(const MomentTensor< T > &A, const Matrix< T > &Tm, std::size_t mode)
Mode product: every fibre of A along dimension mode is replaced by Tm times that fibre.
MomentTensor< T > moment_joint_central_from_raw_mean(const MomentTensor< T > &m, const std::vector< T > &mu)
Joint central moments from joint raw moments about a given mean vector.
MomentTensor< T > moment_jointtrans(const MomentTensor< T > &A, MomentEdge edge)
Applies the conversion matrix of one edge along every dimension of A.
MomentTensor< T > moment_joint_raw_from_central(const MomentTensor< T > &mc, const std::vector< T > &mu)
Joint raw moments from joint central moments about a given mean vector.
Mg1ConservationResult< T > qsys_mg1_conservation(const std::vector< T > &lambda, const std::vector< T > &mu, const std::vector< T > &cs, const std::vector< T > &W=std::vector< T >())
The M/G/1 conservation-law invariant.
MaximaResult< T > qsys_maxima_twomoment(std::size_t n, const T &mean, const T &cs2, const T &q=num_traits< T >::from_int(0), bool exactFitted=true)
Two-moment approximation for the maximum of n iid non-negative variables.
MapG1kResult< T > qsys_mapg1k(const mam::Map< T > &arrival, const ServiceLaw< T > &svc, std::size_t K, const T &tol, std::size_t nmaxCap)
MAP/G/1/K with tail drop.
QsysRes qsys_gimc(const std::function< double(double)> &lst, double lambda, double mu, unsigned c, const QsysResOptions &o=QsysResOptions())
Exact GI/M/c queue, interarrival law given by its LST.
Definition qsys_gimc.h:52
LindleyResult< T > qsys_hh1_lindley(const std::vector< T > &lambda, const std::vector< T > &pa, const std::vector< T > &mu, const std::vector< T > &ps, const std::vector< T > &Wn, unsigned mmax=2)
Conditional waiting-time moments of the Hl/Hn/1 Lindley recursion.
Mer1Result qsys_mer1(double lambda, double mu, unsigned r, const QsysResOptions &o=QsysResOptions())
Exact M/Er/1 queue.
Definition qsys_mer1.h:57
LdpsWorkloadResult< T > qsys_ldps_workload(const T &lambda, const WorkloadServiceLaw< T > &B, const std::vector< T > &alpha, std::size_t N, const std::vector< T > &t, std::size_t ngrid)
Workload distribution of the load-dependent PS station with blocking.
MapPhcResult< T > qsys_mapmapc(const mam::Map< T > &arrival, const mam::Map< T > &service, unsigned c, std::size_t dist_size=500, std::size_t num_w_moms=3)
MAP/MAP/c FCFS with the service phase carried in start order, exactly.
Mg1SjfResult qsys_mg1_sjf(const std::vector< double > &lambda, const std::vector< double > &mu, const std::vector< double > &cs)
M/G/1 non-preemptive SJF.
Mm1TandemLindleyResult< T > qsys_mm1_tandem_lindley(const T &lambda, const T &mu1, const T &mu2, const std::vector< T > &Wk, const std::vector< T > &Wk1)
Conditional waiting time at the SECOND station of an M/M/1 -> /M/1 tandem.
Mm1PsResult< T > qsys_mm1_ps(const std::vector< T > &lambda, const std::vector< T > &mu)
Exact sojourn-time moments of the multiclass M/M/1-PS queue.
Definition qsys_mm1_ps.h:63
Mg1ExceptionalResult< T > qsys_mg1_exceptional(const T &lambda, const T &mu, const T &cs, const T &muf, const T &csf)
M/G/1 with exceptional first service.
MmapGk1Result< T > qsys_mmapgk1(const std::vector< Matrix< T > > &MMAP, const std::vector< MmapGk1Service< T > > &svc, const std::vector< T > &w_points, std::size_t num_w_moms, double tol, std::size_t iter_max, std::size_t quad_nodes)
MMAP[K]/G[K]/1 FCFS, per type.
SvclawCoeffsResult< T > qsys_svclaw_coeffs(const ServiceLaw< T > &svc, const T &theta, const T &tol, std::size_t nmaxCap)
qsys_svclaw_coeffs: uniformization coefficients of a service-time descriptor; see qsys_svclaw_coeffs....
MmcPrioTardinessResult qsys_mmc_prio_tardiness(const std::vector< double > &lambda, double mu, unsigned c, const std::vector< double > &d, const std::string &type="np", const std::string &method="euler")
Stationary per-class expected tardiness of a priority M/M/c queue.
MmcPrioResult< T > qsys_mmc_prio(const std::vector< T > &lambda, const T &mu, unsigned c)
M/M/c with head-of-line priorities.
QsysRes qsys_mmckk(double alpha, double mu, unsigned c, unsigned K, const QsysResOptions &o=QsysResOptions())
Exact M/M/c/K/K finite-source queue.
Definition qsys_mmckk.h:48
QsysAbandonResult< T > qsys_mgisrgi_whitt(const T &lambda, const T &mu, unsigned s, double r, const Patience< T > &patience, const MgisrgiOptions &opts=MgisrgiOptions())
Engineering solution of the call-center model M/GI/s/r+GI.
QsysRes qsys_mh21(double lambda, double p1, double mu1, double mu2, const QsysResOptions &o=QsysResOptions())
Exact M/H2/1 queue.
Definition qsys_mh21.h:44
TandemLindleyResult< T > qsys_tandem_lindley(const std::vector< T > &A, const Matrix< T > &S, const std::vector< T > &W0=std::vector< T >())
Sample-path Lindley recursion along a tandem of single-server FCFS queues.
QsysResT< T > qsys_ddckk(const T &EO, const T &ES, unsigned c, unsigned K)
Exact D/D/c/K/K finite-source queue.
Definition qsys_ddckk.h:48
LindleyResult< T > qsys_mm1_lindley(const T &lambda, const T &mu, const std::vector< T > &Wn, unsigned mmax=2)
Conditional waiting-time moments of the M/M/1 Lindley recursion.
Mg1SetupResult< T > qsys_mg1_setup(const T &lambda, const T &mu, const T &cs, const T &EU, const T &EU2)
M/G/1 with setup times, setup given by its first two moments.
QsysRes qsys_mg1kk(double alpha, unsigned K, const std::function< double(double)> &lst, double ES)
Exact M/G/1/K/K finite-source queue, service given by its LST.
Definition qsys_mg1kk.h:60
BmapM1Result< T > qsys_bmapm1(const std::vector< Matrix< T > > &D, const T &mu, const T &qParam, std::size_t maxLevelParam, unsigned maxIter, const T &tol, double tailTol)
BMAP/M/1 by the matrix-analytic (M/G/1-type) method.
TandemUbResult< T > qsys_tandem_ub_ciucu(const std::vector< T > &x, const std::function< T(const T &)> &lst, const std::vector< T > &p, const std::vector< T > &mu, const std::function< T(const T &)> &dlst=std::function< T(const T &)>())
Tail bounds for a GI/Hn/1 -> .
MmapMap1Result< T > qsys_mmapmap1(const mam::Mmap< T > &arrival, const std::vector< mam::Map< T > > &svc, std::vector< bool > carry=std::vector< bool >())
MMAP[K]/MAP[K]/1 FCFS with per-class carried service phases, exactly.
QsysRes qsys_engset(double alpha, double mu, unsigned c, unsigned K)
Exact Engset M/G/c/c/K loss system.
Definition qsys_engset.h:59
QsysRes qsys_mmcc(double lambda, double mu, unsigned c)
Exact M/M/c/c Erlang loss system.
Definition qsys_mmcc.h:38
Mg1PsResult qsys_mg1_ps(double lambda, const std::vector< double > &alpha, const Matrix< double > &Tmat, const Mg1PsOptions &opt)
Sojourn-time distribution of the M/G/1 processor-sharing queue.
QsysRes qsys_mg1_busy(double lambda, double mu, double cs, const Mg1BusyOptions &o=Mg1BusyOptions())
Busy-period measures of the M/G/1 queue.
QsysRes qsys_mgc_ps(double lambda, double mu, unsigned c)
Exact M/G/c-PS queue.
Definition qsys_mgc_ps.h:44
Mg1PrioPrResult< T > qsys_mg1_prio_pr(const std::vector< T > &lambda, const std::vector< T > &mu, const std::vector< T > &cs, const std::vector< T > &ES3=std::vector< T >())
M/G/1 with preemptive-resume priorities.
MmapG1kResult< T > qsys_mmapg1k(const Matrix< T > &D0, const std::vector< Matrix< T > > &D1c, const ServiceLaw< T > &svc, std::size_t K, const T &tol, std::size_t nmaxCap)
MMAP[K]/G/1/K with tail drop.
MmapGk1LcfsprResult< T > qsys_mmapgk1_lcfspr(const std::vector< Matrix< T > > &MMAP, const std::vector< MmapGk1Service< T > > &svc, const MmapGk1LcfsprOptions< T > &opt)
MMAP[K]/G[K]/1 LCFS-PR, per type, by Takine's matrix product form.
MapG1kPerflowResult< T > qsys_mapg1k_perflow(const std::vector< mam::Map< T > > &flows, const ServiceLaw< T > &svc, std::size_t K, const T &tol, std::size_t nmaxCap)
Per-flow analysis of a MAP-fed tail-drop buffer.
QsysRes qsys_md1(double lambda, double mu, long nmax=-1)
Exact M/D/1 queue.
Definition qsys_md1.h:43
std::vector< T > qsys_lindley_moment(const T &lambda, const T &mu, const std::vector< T > &Wn, unsigned m)
One conditional Lindley moment for exponential primitives.
MmcPrioEtaResult qsys_mmc_prio_eta(const std::vector< double > &lambda, double mu, unsigned c, unsigned r, unsigned v, unsigned q, const std::vector< double > &d, const std::string &type="np", const std::string &method="euler")
Expected tardiness at arrival of a class-r order.
nlohmann::json Json
Definition api_json.h:63
Json encode_vectors(const std::vector< std::vector< T > > &v)
A list of vectors, the shape a per-class or per-segment result returns.
Definition api_json.h:366
Matrix< T > matrix_from_json(const Json &j, const std::string &where)
A matrix from a JSON value: 2-D array as rows, 1-D array as a row vector, scalar as 1x1,...
Definition api_json.h:223
Json encode_matrices(const std::vector< Matrix< T > > &v)
A list of matrices, the shape the MMAP/BMAP families return as {D0,D1,...}.
Definition api_json.h:358
void register_api_qsys(DispatchTable &t)
qsys::QsysResOptions read_qsys_res_options(Args &a, bool with_nmax=true)
The 'tPoints', 'percentiles' and 'nmax' options those functions share.
T number_from_json(const Json &j, const std::string &where)
Definition api_json.h:210
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
void register_library_only_qsys(LibraryOnlyTable &t)
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_qsys_res(const qsys::QsysResT< T > &r)
MATLAB's res struct of the exact queueing-system functions: one key per measure, in the reference's f...
std::map< std::string, Invoker > DispatchTable
QuestOptions sim_quest_options(const QuestOptions &options=QuestOptions())
Validates an option set and returns it.
QuestInterval< T > sim_quest_heuristic_ci(const std::vector< T > &bqe, const T &centre, const T &Ap, const T &Np, std::size_t nstar, double alpha, bool useAutocorr)
Fallback interval used when a QUEST stage test fails.
RunLengthResult< T > sim_runlength(const T &mean, const T &asymVar, const T &relPrecision=num_traits< T >::from_rational(1, 20), const T &confidence=num_traits< T >::from_rational(19, 20), const T &runLength=num_traits< T >::from_int(0))
Run length for a steady-state estimate of a given relative precision.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Reader for the LINE model.json interchange (a Network model) into a qn::Network<T> built through the ...
BMAP/M/1 by the matrix-analytic (M/G/1-type) method.
Exact analysis of the finite-source D/D/c/K/K machine-repair model.
Exact analysis of the Engset loss system M/G/c/c/K: K sources, each idle for an Exp(alpha) time,...
Exact analysis of the GI/M/c queue (Takacs, 1962).
Conditional waiting-time moments of the Hl/Hn/1 Lindley recursion.
Stationary distribution of the unfinished work in a single-stage load-dependent generalized-processor...
One conditional Lindley moment for exponential primitives.
The MAP/G/1/K queue with tail drop: Markovian arrivals, an arbitrary service law F,...
Per-flow throughput and loss ratio of a tail-drop FIFO buffer fed by N flows of mutually different st...
The MAP/MAP/c FCFS queue whose service MAP is CARRIED in service-start order.
Two-moment approximation for the maximum of n iid non-negative variables.
Exact analysis of the M/D/1 queue: the M/G/1 moments with deterministic service plus the queue-length...
Exact analysis of the M/Er/1 queue (Erlang-r service of total rate mu/r per phase-sum,...
Busy-period measures of the M/G/1 queue.
Kleinrock's conservation law for work-conserving non-preemptive M/G/1 disciplines.
M/G/1 with exceptional first service: the job that starts a busy period has service law Bf,...
M/G/1 with preemptive-resume priorities: per-class mean (and optionally variance of the) response tim...
Sojourn-time distribution of the M/G/1 processor-sharing queue.
M/G/1 with setup times: the server switches off when empty and needs a setup time U before serving th...
M/G/1 with non-preemptive shortest-job-first scheduling: per-class mean response times.
Exact analysis of the finite-source M/G/1/K/K (machine-repair) queue.
Exact analysis of the M/G/c processor-sharing queue.
Engineering solution of the call-center model M/GI/s/r+GI.
Exact analysis of the M/H2/1 queue: hyperexponential service with branch probabilities (p1,...
Conditional waiting-time moments of the M/M/1 Lindley recursion.
Exact sojourn-time moments of the multiclass M/M/1-PS queue.
Conditional waiting time at the SECOND station of an M/M/1 -> /M/1 tandem.
Exact per-class throughput and loss ratio of an MMAP[K]/G/1/K tail-drop queue.
The MMAP[K]/G[K]/1 FCFS queue: K customer types with class-dependent GENERAL service,...
The MMAP[K]/G[K]/1 LCFS-PR queue: K customer types with class-dependent GENERAL service under preempt...
The MMAP[K]/MAP[K]/1 FCFS queue in which each class's service phase is CARRIED.
M/M/c with non-preemptive (head-of-line) priorities and a common exponential service rate (Cobham,...
Expected tardiness at arrival (ETA) in a multiclass priority M/M/c queue, conditional on the state th...
Exact stationary expected tardiness in a multiclass priority M/M/c queue (Hafizoglu,...
Exact analysis of the M/M/c/c (Erlang loss) system.
Exact analysis of the finite-source M/M/c/K/K (machine-repair) queue.
Sample-path Lindley recursion along a tandem of single-server FCFS queues.
Tail bounds for a GI/Hn/1 -> .
Fallback interval used when a QUEST stage test fails.
Options of the QUEST procedures, with the published FQUEST defaults.
Run-length planning for steady-state simulation.
ErrorKind(std::string n)
Definition error.h:39
static Patience exponential(const T &theta)
Exponential patience of rate theta, h(t) = theta, the Erlang A case.
static ServiceLaw deterministic(const T &d)
Constant service time d.
static ServiceLaw phase_type(const std::vector< T > &alpha, const Matrix< T > &Tmat)
Phase type (alpha, Tmat).
static ServiceLaw gamma(const T &shape, const T &scale)
Gamma(shape, scale).