LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
api_dispatch_misc.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 'misc' group (see api_dispatch_detail.h):
6 * polling, flow-equivalent servers (fes), caches, cache decomposition (da),
7 * loss networks, retrieval, sum, MAP queueing-network bounds (mapqn),
8 * workflow detectors, decision diagrams (mdd), stochastic Petri nets (spn)
9 * and a handful of mam / pfqn / trace / dqsys helpers.
10 *
11 * CONVENTIONS:
12 * - argument keys are the MATLAB parameter names and result keys the MATLAB
13 * output names; a MATLAB struct argument or output (options, params, ch,
14 * mde, info, stats) is a nested object keyed by its MATLAB field names;
15 * - a MAP is the MATLAB cell {D0, D1}, written as a two-element list
16 * [D0, D1]; a list of MAPs is a list of such pairs, and in a LIST every
17 * matrix is written as an array of rows so the two shapes cannot be
18 * confused (a single MAP may write a 1x1 matrix as a bare number);
19 * - a station, item, list, node or place INDEX passes through in the C++
20 * port's own convention, which the port headers document: 0-based unless
21 * the header says otherwise (ljd_linearize and the mdd node ids are
22 * 1-based because the reference's are). An index is not a measurement and
23 * is never shifted on the way out;
24 * - an argument MATLAB accepts but the port has no parameter for is refused
25 * by name (Args::unsupported) rather than dropped;
26 * - an absent optional argument calls the port's shorter overload, or leaves
27 * the port's options-struct default in place; no default is restated here.
28 */
29
30#include "api_dispatch_detail.h"
31
32#include <cctype>
33#include <cstdint>
34#include <cstdlib>
35#include <string>
36#include <vector>
37
124#include "line/api/mdd/mdd.h"
125#include "line/api/mdd/mdd_rec.h"
129#include "line/api/spn/spn_mdd.h"
132
133namespace line {
134namespace reg {
135namespace {
136
137// ---------------------------------------------------------------------------
138// Shared readers and encoders
139// ---------------------------------------------------------------------------
140
141std::string at(const std::string& where, std::size_t k) {
142 return where + "[" + std::to_string(k) + "]";
143}
144
145/** One non-negative index or count from a JSON number or numeric string. */
146std::size_t size_from_json(const Json& j, const std::string& where) {
147 const std::string text = decimal_text(j, where);
148 char* end = nullptr;
149 const long long v = std::strtoll(text.c_str(), &end, 10);
150 if (end == text.c_str() || *end != '\0')
151 throw InputError(where + ": expected a non-negative integer, got " + text);
152 if (v < 0) throw InputError(where + ": expected a non-negative integer, got " + text);
153 return static_cast<std::size_t>(v);
154}
155
156/** A flat list of non-negative integers; a bare number is a one-element list. */
157std::vector<std::size_t> sizes_from_json(const Json& j, const std::string& where) {
158 std::vector<std::size_t> out;
159 if (!j.is_array()) {
160 out.push_back(size_from_json(j, where));
161 return out;
162 }
163 for (std::size_t k = 0; k < j.size(); ++k) out.push_back(size_from_json(j[k], at(where, k)));
164 return out;
165}
166
167Json encode_sizes(const std::vector<std::size_t>& v) {
168 Json a = Json::array();
169 for (std::size_t x : v) a.push_back(static_cast<std::uint64_t>(x));
170 return a;
171}
172
173/** A flag vector: booleans or 0/1, a bare value being a one-element list. */
174std::vector<bool> flags_from_json(const Json& j, const std::string& where) {
175 std::vector<bool> out;
176 const Json flat = j.is_array() ? j : Json::array({j});
177 for (std::size_t k = 0; k < flat.size(); ++k) {
178 const Json& e = flat[k];
179 if (e.is_boolean()) {
180 out.push_back(e.get<bool>());
181 continue;
182 }
183 const std::string text = decimal_text(e, at(where, k));
184 if (text != "0" && text != "1")
185 throw InputError(at(where, k) + ": a flag must be true, false, 0 or 1, got " + text);
186 out.push_back(text == "1");
187 }
188 return out;
189}
190
191/** A list of numeric vectors, e.g. a MATLAB cell of row vectors or a matrix read by rows. */
192template <class T>
193std::vector<std::vector<T> > vectors_from_json(const Json& j, const std::string& where) {
194 if (!j.is_array()) throw InputError(where + ": expected a list of vectors");
195 std::vector<std::vector<T> > out;
196 for (std::size_t k = 0; k < j.size(); ++k)
197 out.push_back(vector_from_json<T>(j[k], at(where, k)));
198 return out;
199}
200
201/** A list of matrices. */
202template <class T>
203std::vector<Matrix<T> > matrices_from_json(const Json& j, const std::string& where) {
204 if (!j.is_array()) throw InputError(where + ": expected a list of matrices");
205 std::vector<Matrix<T> > out;
206 for (std::size_t k = 0; k < j.size(); ++k)
207 out.push_back(matrix_from_json<T>(j[k], at(where, k)));
208 return out;
209}
210
211/** A list of lists of matrices (a MATLAB cell of cells, e.g. R{v}{i}). */
212template <class T>
213std::vector<std::vector<Matrix<T> > > matrix_lists_from_json(const Json& j,
214 const std::string& where) {
215 if (!j.is_array()) throw InputError(where + ": expected a list of lists of matrices");
216 std::vector<std::vector<Matrix<T> > > out;
217 for (std::size_t k = 0; k < j.size(); ++k)
218 out.push_back(matrices_from_json<T>(j[k], at(where, k)));
219 return out;
220}
221
222template <class T>
223Json encode_matrix_lists(const std::vector<std::vector<Matrix<T> > >& v) {
224 Json a = Json::array();
225 for (const auto& x : v) a.push_back(encode_matrices(x));
226 return a;
227}
228
229/** One MAP written [D0, D1]. */
230template <class T>
231mam::Map<T> map_from_json(const Json& j, const std::string& where) {
232 if (!j.is_array() || j.size() != 2)
233 throw InputError(where + ": a MAP is the two-element list [D0, D1]");
234 mam::Map<T> m;
235 m.D0 = matrix_from_json<T>(j[0], where + ".D0");
236 m.D1 = matrix_from_json<T>(j[1], where + ".D1");
237 if (m.D0.rows() != m.D0.cols() || m.D1.rows() != m.D0.rows() || m.D1.cols() != m.D0.cols())
238 throw InputError(where + ": D0 and D1 must be square matrices of the same order");
239 return m;
240}
241
242/**
243 * True when j is a LIST of MAPs rather than one MAP: its first element is a
244 * pair whose first matrix is written as an array of rows, i.e. j[0][0][0] is
245 * itself an array. A single MAP has a number there.
246 */
247bool is_map_list(const Json& j) {
248 return j.is_array() && !j.empty() && j[0].is_array() && !j[0].empty() &&
249 j[0][0].is_array() && !j[0][0].empty() && j[0][0][0].is_array();
250}
251
252template <class T>
253std::vector<mam::Map<T> > maps_from_json(const Json& j, const std::string& where) {
254 if (!is_map_list(j))
255 throw InputError(where + ": expected a list of MAPs [[D0, D1], ...], each matrix "
256 "written as an array of rows");
257 std::vector<mam::Map<T> > out;
258 for (std::size_t k = 0; k < j.size(); ++k) out.push_back(map_from_json<T>(j[k], at(where, k)));
259 return out;
260}
261
262template <class T>
263Json encode_map(const mam::Map<T>& m) {
264 Json a = Json::array();
265 a.push_back(encode_matrix(m.D0));
266 a.push_back(encode_matrix(m.D1));
267 return a;
268}
269
270template <class T>
271Json encode_maps(const std::vector<mam::Map<T> >& v) {
272 Json a = Json::array();
273 for (const auto& m : v) a.push_back(encode_map(m));
274 return a;
275}
276
277std::string where_of(const char* fn, const char* key) { return std::string(fn) + ": " + key; }
278
279// ---------------------------------------------------------------------------
280// polling
281// ---------------------------------------------------------------------------
282
283/**
284 * The moments the polling formulas consume, reduced exactly as MATLAB's
285 * private/polling_moments.m does: each entry of arvMAPs, svcMAPs and
286 * switchMAPs is either a MAP [D0, D1] or a numeric moment vector, namely
287 * lambda_i, [E[S_i] E[S_i^2]] and [E[R_i] Var[R_i]] respectively.
288 */
289// per_queue_sw = false lets switchMAPs carry a different number of entries
290// (one per table position for polling_qsys_table); r and delta2 then hold the
291// moments of those entries in order.
292template <class T>
293polling::PollingMoments<T> polling_moments_from_json(const Json& arv, const Json& svc, const Json& sw,
294 const char* fn, bool per_queue_sw) {
295 for (const Json* j : {&arv, &svc, &sw})
296 if (!j->is_array()) throw InputError(std::string(fn) + ": arvMAPs, svcMAPs and switchMAPs are lists");
297 const std::size_t n = arv.size();
298 if (svc.size() != n || (per_queue_sw && sw.size() != n))
299 throw InputError(std::string(fn) + ": arvMAPs, svcMAPs and switchMAPs must have one entry per queue");
300 // An entry is a MAP when it is a pair whose elements are arrays; a scalar
301 // or a flat list of numbers is a moment vector.
302 const auto is_map = [](const Json& e) {
303 return e.is_array() && e.size() == 2 && e[0].is_array() && e[1].is_array();
304 };
305 polling::PollingMoments<T> m;
306 for (std::size_t i = 0; i < n; ++i) {
307 const std::string wa = at(std::string(fn) + ": arvMAPs", i);
308 const std::string ws = at(std::string(fn) + ": svcMAPs", i);
309 if (is_map(arv[i])) {
310 m.lambda.push_back(mam::map_lambda(map_from_json<T>(arv[i], wa)));
311 } else {
312 const std::vector<T> v = vector_from_json<T>(arv[i], wa);
313 if (v.empty()) throw InputError(wa + ": empty moment vector");
314 m.lambda.push_back(v[0]);
315 }
316 if (is_map(svc[i])) {
317 const mam::Map<T> s = map_from_json<T>(svc[i], ws);
318 m.b.push_back(mam::map_mean(s));
319 m.b2.push_back(mam::map_moment(s, 2));
320 } else {
321 const std::vector<T> v = vector_from_json<T>(svc[i], ws);
322 if (v.size() < 2) throw InputError(ws + ": expected [E[S] E[S^2]]");
323 m.b.push_back(v[0]);
324 m.b2.push_back(v[1]);
325 }
326 }
327 for (std::size_t i = 0; i < sw.size(); ++i) {
328 const std::string wr = at(std::string(fn) + ": switchMAPs", i);
329 if (is_map(sw[i])) {
330 const mam::Map<T> r = map_from_json<T>(sw[i], wr);
331 m.r.push_back(mam::map_mean(r));
332 m.delta2.push_back(mam::map_var(r));
333 } else {
334 const std::vector<T> v = vector_from_json<T>(sw[i], wr);
335 if (v.size() < 2) throw InputError(wr + ": expected [E[R] Var[R]]");
336 m.r.push_back(v[0]);
337 m.delta2.push_back(v[1]);
338 }
339 }
340 return m;
341}
342
343template <class T>
344polling::PollingMoments<T> polling_moments_from_args(Args& a, const char* fn) {
345 const Json& arv = a.get("arvMAPs");
346 const Json& svc = a.get("svcMAPs");
347 const Json& sw = a.get("switchMAPs");
348 a.done();
349 return polling_moments_from_json<T>(arv, svc, sw, fn, true);
350}
351
352#define LINE_POLLING_OP(Name, fn) \
353 struct Name { \
354 template <class T> \
355 static Json run(Args& a) { \
356 const polling::PollingMoments<T> m = polling_moments_from_args<T>(a, #fn); \
357 Json r; \
358 r["W"] = encode_vector(polling::fn(m)); \
359 return r; \
360 } \
361 };
362LINE_POLLING_OP(OpPolling1Limited, polling_qsys_1limited)
363LINE_POLLING_OP(OpPollingDecrementing, polling_qsys_decrementing)
364LINE_POLLING_OP(OpPollingExhaustive, polling_qsys_exhaustive)
365LINE_POLLING_OP(OpPollingGated, polling_qsys_gated)
366#undef LINE_POLLING_OP
367
368/**
369 * polling_qsys_table(arvMAPs, svcMAPs, switchMAPs, table, discipline):
370 * switchMAPs holds one entry per table position, table is 1-based and
371 * discipline is "exhaustive" or "gated" (or the PollingType id of
372 * EXHAUSTIVE or GATED). Returns W, EX (M x n) and EX2 (one n x n per position).
373 */
374struct OpPollingTable {
375 template <class T>
376 static Json run(Args& a) {
377 const char* fn = "polling_qsys_table";
378 const Json& arv = a.get("arvMAPs");
379 const Json& svc = a.get("svcMAPs");
380 const Json& sw = a.get("switchMAPs");
381 const std::vector<int> tab = a.ints("table");
382 const Json& disc = a.get("discipline");
383 a.done();
384 bool gated = false;
385 if (disc.is_string()) {
386 std::string d = disc.get<std::string>();
387 for (auto& ch : d) ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
388 if (d == "exhaustive") gated = false;
389 else if (d == "gated") gated = true;
390 else throw InputError(ErrorKind{"Argument"}, std::string(fn) + ": The discipline must be 'exhaustive' or 'gated'.");
391 } else {
392 const std::vector<int> id = int_vector_from_json(disc, std::string(fn) + ": discipline");
393 if (id.size() == 1 && id[0] == static_cast<int>(lang::PollingType::EXHAUSTIVE)) gated = false;
394 else if (id.size() == 1 && id[0] == static_cast<int>(lang::PollingType::GATED)) gated = true;
395 else throw UnsupportedError(ErrorKind{"SchedStrategy_POLLING"},
396 std::string(fn) + ": polling_qsys_table is exact for exhaustive or gated service only.");
397 }
398 const polling::PollingMoments<T> m = polling_moments_from_json<T>(arv, svc, sw, fn, false);
399 std::vector<std::size_t> table;
400 for (int t : tab) {
401 if (t < 1) throw InputError(ErrorKind{"Argument"}, std::string(fn) + ": The polling table must list every queue 1.." +
402 std::to_string(m.lambda.size()) + " at least once.");
403 table.push_back(static_cast<std::size_t>(t));
404 }
405 const polling::PollingTableResult<T> res =
406 polling::polling_qsys_table_full(m.lambda, m.b, m.b2, table, m.r, m.delta2, gated);
407 Json r;
408 r["W"] = encode_vector(res.W);
409 Matrix<T> EX(res.EX.size(), m.lambda.size());
410 for (std::size_t p = 0; p < res.EX.size(); ++p)
411 for (std::size_t i = 0; i < m.lambda.size(); ++i) EX(p, i) = res.EX[p][i];
412 r["EX"] = encode_matrix(EX);
413 Json ex2 = Json::array();
414 for (const auto& S : res.EX2) ex2.push_back(encode_matrix(S));
415 r["EX2"] = ex2;
416 return r;
417 }
418};
419
420/** polling_qsys_klimited(arvMAPs, svcMAPs, switchMAPs, K): K scalar or per queue. */
421struct OpPollingKLimited {
422 template <class T>
423 static Json run(Args& a) {
424 const char* fn = "polling_qsys_klimited";
425 const Json& arv = a.get("arvMAPs");
426 const Json& svc = a.get("svcMAPs");
427 const Json& sw = a.get("switchMAPs");
428 const std::vector<int> k = a.ints("K");
429 a.done();
430 const polling::PollingMoments<T> m = polling_moments_from_json<T>(arv, svc, sw, fn, true);
431 Json r;
432 r["W"] = encode_vector(polling::polling_qsys_klimited(m, std::vector<long>(k.begin(), k.end())));
433 return r;
434 }
435};
436
437/**
438 * polling_pcl(arvMAPs, svcMAPs, switchMAPs, disc): disc is a string of
439 * 'E','G','L','D' (one per queue, or one letter for all), or a PollingType id
440 * (KLIMITED and GLIMITED stand for K=1). Returns w, rhs and pcl.
441 */
442struct OpPollingPcl {
443 template <class T>
444 static Json run(Args& a) {
445 const char* fn = "polling_pcl";
446 const Json& arv = a.get("arvMAPs");
447 const Json& svc = a.get("svcMAPs");
448 const Json& sw = a.get("switchMAPs");
449 const Json& disc = a.get("disc");
450 a.done();
451 const polling::PollingMoments<T> m = polling_moments_from_json<T>(arv, svc, sw, fn, true);
452 const std::size_t n = m.lambda.size();
453 std::string code;
454 if (disc.is_string()) {
455 code = disc.get<std::string>();
456 if (code.size() == 1) code = std::string(n, code[0]);
457 } else {
458 for (int id : int_vector_from_json(disc, std::string(fn) + ": disc")) {
459 switch (static_cast<lang::PollingType>(id)) {
460 case lang::PollingType::EXHAUSTIVE: code += 'E'; break;
461 case lang::PollingType::GATED: code += 'G'; break;
463 case lang::PollingType::GLIMITED: code += 'L'; break;
464 case lang::PollingType::DECREMENTING: code += 'D'; break;
465 default:
466 throw UnsupportedError(ErrorKind{"SchedStrategy_POLLING"},
467 std::string(fn) + ": unsupported discipline.");
468 }
469 }
470 if (code.size() == 1) code = std::string(n, code[0]);
471 }
472 const polling::PollingPcl<T> p = polling::polling_pcl(m, code);
473 Json r;
474 r["w"] = encode_vector(p.w);
475 r["rhs"] = encode_scalar(p.rhs);
476 r["pcl"] = encode_scalar(p.pcl);
477 return r;
478 }
479};
480
481// ---------------------------------------------------------------------------
482// fes
483// ---------------------------------------------------------------------------
484
485struct OpLjdLinearize {
486 template <class T>
487 static Json run(Args& a) {
488 const std::vector<int> nvec = a.ints("nvec");
489 const std::vector<int> cutoffs = a.ints("cutoffs");
490 a.done();
491 Json r;
492 r["idx"] = encode_count(fes::ljd_linearize(nvec, cutoffs)); // 1-based, as MATLAB's
493 return r;
494 }
495};
496
497/** The (L, mi, isDelay, cutoffs) quadruple shared by fes_compute_throughputs and _metrics. */
498template <class T>
499struct FesLoad {
500 Matrix<T> L;
501 std::vector<int> mi;
502 std::vector<bool> isDelay;
503 std::vector<int> cutoffs;
504};
505
506template <class T>
507FesLoad<T> read_fes_load(Args& a, const char* fn) {
508 FesLoad<T> f;
509 f.L = a.matrix<T>("L");
510 f.mi = a.ints("mi");
511 f.isDelay = flags_from_json(a.get("isDelay"), where_of(fn, "isDelay"));
512 f.cutoffs = a.ints("cutoffs");
513 a.unsupported("options", "its only field, verbose, prints progress and changes no result; "
514 "the port does not print.");
515 a.done();
516 return f;
517}
518
519struct OpFesComputeThroughputs {
520 template <class T>
521 static Json run(Args& a) {
522 const FesLoad<T> f = read_fes_load<T>(a, "fes_compute_throughputs");
523 Json r;
524 r["scalingTable"] =
525 encode_vectors(fes::fes_compute_throughputs(f.L, f.mi, f.isDelay, f.cutoffs));
526 return r;
527 }
528};
529
530struct OpFesComputeMetrics {
531 template <class T>
532 static Json run(Args& a) {
533 const FesLoad<T> f = read_fes_load<T>(a, "fes_compute_metrics");
534 const fes::FesConditionalMetrics<T> m =
535 fes::fes_compute_metrics(f.L, f.mi, f.isDelay, f.cutoffs);
536 Json r;
537 r["Qtable"] = encode_matrices(m.QN);
538 r["Utable"] = encode_matrices(m.UN);
539 return r;
540 }
541};
542
543/**
544 * fes_build_isolated: MATLAB takes (sn, subsetIndices, stochCompS) and plucks
545 * the subset's service rates out of sn; the port lifts that extraction into
546 * its signature and takes the rates directly (fes_build_isolated.h). The
547 * entry reads them under the port's name, `rates` (M_sub x K, the rows of
548 * sn.rates at the subset), and refuses sn and subsetIndices. mi and isDelay,
549 * which MATLAB returns only as a pass-through of sn, are not outputs here.
550 */
551struct OpFesBuildIsolated {
552 template <class T>
553 static Json run(Args& a) {
554 a.unsupported("sn", "the port takes the subset's service rates as `rates` instead of "
555 "a NetworkStruct.");
556 a.unsupported("subsetIndices", "the port takes the subset's rows of sn.rates as "
557 "`rates`, so the subset is already selected.");
558 const Matrix<T> rates = a.matrix<T>("rates");
559 const Matrix<T> S = a.matrix<T>("stochCompS");
560 a.done();
561 const fes::FesIsolated<T> iso = fes::fes_build_isolated(rates, S);
562 Json r;
563 r["L"] = encode_matrix(iso.L);
564 r["visits"] = encode_matrix(iso.visits);
565 return r;
566 }
567};
568
569/**
570 * A flow-equivalent-server descriptor as MATLAB's fes_map_levels reads it:
571 * a single MAP replicated (and, given mi, scaled by min(k, mi)) over n
572 * levels, or a list of per-level MAPs returned as given.
573 */
574template <class T>
575std::vector<mam::Map<T> > fes_levels_from_json(const Json& j, std::size_t n, const double* mi,
576 const std::string& where) {
577 if (is_map_list(j)) return fes::fes_map_levels(maps_from_json<T>(j, where), n);
578 const mam::Map<T> m = map_from_json<T>(j, where);
579 return mi ? fes::fes_map_levels(m, n, *mi) : fes::fes_map_levels(m, n);
580}
581
582struct OpFesMapLevels {
583 template <class T>
584 static Json run(Args& a) {
585 const Json& F = a.get("FES");
586 const std::size_t n = a.count("n");
587 const bool hasMi = a.has("mi");
588 const double mi = hasMi ? a.number<double>("mi") : 0.0;
589 a.done();
590 Json r;
591 r["FESlev"] = encode_maps(
592 fes_levels_from_json<T>(F, n, hasMi ? &mi : nullptr, "fes_map_levels: FES"));
593 return r;
594 }
595};
596
597struct OpFesMapInterdeparture {
598 template <class T>
599 static Json run(Args& a) {
600 const Json& M = a.get("MAPs");
601 const Json& F = a.get("FES");
602 const std::size_t n = a.count("n");
603 // mi = [mi_station mi_fes] scales a single MAP over the levels exactly
604 // as MATLAB's fes_map_levels call inside fes_map_interdeparture does.
605 const bool hasMi = a.has("mi");
606 std::vector<double> mi;
607 if (hasMi) {
608 mi = a.vector<double>("mi");
609 if (mi.size() != 2)
610 throw InputError("fes_map_interdeparture: mi is [mi_station mi_fes]");
611 }
612 a.done();
613 const std::vector<mam::Map<T> > maps = fes_levels_from_json<T>(
614 M, n, hasMi ? &mi[0] : nullptr, "fes_map_interdeparture: MAPs");
615 const std::vector<mam::Map<T> > fes = fes_levels_from_json<T>(
616 F, n, hasMi ? &mi[1] : nullptr, "fes_map_interdeparture: FES");
617 const mam::Map<T> out = fes::fes_map_interdeparture(maps, fes, n);
618 Json r;
619 r["T0"] = encode_matrix(out.D0);
620 r["T1"] = encode_matrix(out.D1);
621 return r;
622 }
623};
624
625struct OpFesMapInterp {
626 template <class T>
627 static Json run(Args& a) {
628 const std::vector<T> x = a.vector<T>("x");
629 const std::vector<T> y = a.vector<T>("y");
630 const std::vector<T> xq = a.vector<T>("xq");
631 a.done();
632 Json r;
633 r["yq"] = encode_vector(fes::fes_map_interp(x, y, xq));
634 return r;
635 }
636};
637
638/**
639 * fes_map_moments(T0, T1, options). The port's options are POSITIONAL
640 * (method, step_safety, tol, iter_max), so an absent field is honoured by
641 * calling with the leading ones only; a field given after an absent one would
642 * need the absent one's default restated, and is refused instead.
643 */
644struct OpFesMapMoments {
645 template <class T>
646 static Json run(Args& a) {
647 mam::Map<T> m;
648 m.D0 = a.matrix<T>("T0");
649 m.D1 = a.matrix<T>("T1");
650 const Json* oj = a.opt("options");
651 a.done();
652 const Json empty = Json::object();
653 Args o(oj ? *oj : empty, "fes_map_moments options");
654 const char* order[] = {"method", "step_safety", "tol", "iter_max"};
655 std::size_t given = 0;
656 for (std::size_t k = 0; k < 4; ++k)
657 if (o.has(order[k])) given = k + 1;
658 for (std::size_t k = 0; k < given; ++k)
659 if (!o.has(order[k]))
660 throw InputError(std::string("fes_map_moments: options.") + order[given - 1] +
661 " is given without options." + order[k] +
662 "; the port takes these positionally (method, step_safety, "
663 "tol, iter_max), so give every field before the last one used");
664 const std::string method = given > 0 ? o.text("method", "") : std::string();
665 const double step = given > 1 ? o.number<double>("step_safety") : 0.0;
666 const double tol = given > 2 ? o.number<double>("tol") : 0.0;
667 const std::size_t iters = given > 3 ? o.count("iter_max") : 0;
668 o.done();
669 fes::FesMapMoments<T> r0;
670 switch (given) {
671 case 0: r0 = fes::fes_map_moments(m); break;
672 case 1: r0 = fes::fes_map_moments(m, method); break;
673 case 2: r0 = fes::fes_map_moments(m, method, step); break;
674 case 3: r0 = fes::fes_map_moments(m, method, step, tol); break;
675 default: r0 = fes::fes_map_moments(m, method, step, tol, iters); break;
676 }
677 Json r;
678 r["e1"] = encode_scalar(r0.e1);
679 r["e2"] = encode_scalar(r0.e2);
680 r["e3"] = encode_scalar(r0.e3);
681 r["e11"] = encode_scalar(r0.e11);
682 r["idc"] = encode_scalar(r0.idc);
683 return r;
684 }
685};
686
687struct OpFesMapSolve {
688 template <class T>
689 static Json run(Args& a) {
690 const Json& F = a.get("FES");
691 const mam::Map<T> think = map_from_json<T>(a.get("thinkMAP"), "fes_map_solve: thinkMAP");
692 const std::size_t N = a.count("N");
693 a.done();
694 const std::vector<mam::Map<T> > fes = fes_levels_from_json<T>(F, N, nullptr, "fes_map_solve: FES");
695 const fes::FesMapSolveResult<T> s = fes::fes_map_solve(fes, think, N);
696 Json r;
697 r["XN"] = encode_scalar(s.X);
698 r["RN"] = encode_scalar(s.R);
699 r["QN"] = encode_scalar(s.Q);
700 r["pk"] = encode_vector(s.pk);
701 return r;
702 }
703};
704
705struct OpFesMapAggregate {
706 template <class T>
707 static Json run(Args& a) {
708 const std::vector<mam::Map<T> > maps = maps_from_json<T>(a.get("maps"), "fes_map_aggregate: maps");
709 const std::vector<double> servers = a.vector<double>("servers");
710 const std::size_t n = a.count("n");
711 const Json* oj = a.opt("options");
712 a.done();
713 const Json empty = Json::object();
714 Args o(oj ? *oj : empty, "fes_map_aggregate options");
715 o.unsupported("verbose", "it prints progress and changes no result; the port does not print.");
716 const bool hasGrid = o.has("grid");
717 const std::vector<std::size_t> grid =
718 hasGrid ? sizes_from_json(o.get("grid"), "fes_map_aggregate: options.grid")
719 : std::vector<std::size_t>();
720 o.done();
721 const fes::FesMapAggregateResult<T> s =
722 hasGrid ? fes::fes_map_aggregate(maps, servers, n, grid)
723 : fes::fes_map_aggregate(maps, servers, n);
724 Json info;
725 info["throughput"] = encode_vector(s.throughput);
726 info["moments"] = encode_vectors(s.moments);
727 info["status"] = encode_ints(s.status);
728 info["grid"] = encode_sizes(s.grid);
729 Json r;
730 r["FES"] = encode_maps(s.fes);
731 r["info"] = info;
732 return r;
733 }
734};
735
736// ---------------------------------------------------------------------------
737// cache
738//
739// gamma is the (n x h) access-factor matrix and m the (h) list capacities. A
740// MATLAB request-rate array lambda(u, n, h+1) is read by the ports at its first
741// page only, so `lambda` is that (u x n) page, except where a port takes the
742// per-list rates (cache_gamma, cache_gamma_lp, cache_ttl_lrua), in which case it
743// is the list of the u per-user (n x h+1) matrices and R the per-user list of
744// per-item (h+1 x h+1) access graphs.
745// ---------------------------------------------------------------------------
746
747template <class T>
748Json cache_miss_results(const T& M, const std::vector<T>& MU, const std::vector<T>& MI,
749 const std::vector<T>& pi0) {
750 Json r;
751 r["M"] = encode_scalar(M);
752 r["MU"] = encode_vector(MU);
753 r["MI"] = encode_vector(MI);
754 r["pi0"] = encode_vector(pi0);
755 return r;
756}
757
758/** The optional size-constrained pair (sigma, k): both or neither. */
759struct SizeCaps {
760 bool given = false;
761 std::vector<int> sigma, k;
762};
763
764SizeCaps read_size_caps(Args& a, const char* fn, const char* capKey) {
765 SizeCaps s;
766 const bool hs = a.has("sigma"), hk = a.has(capKey);
767 if (hs != hk)
768 throw InputError(std::string(fn) + ": sigma and " + capKey +
769 " constrain the cache together; give both or neither");
770 s.given = hs;
771 s.sigma = a.ints_or_empty("sigma");
772 s.k = a.ints_or_empty(capKey);
773 return s;
774}
775
776struct OpCacheErec {
777 template <class T>
778 static Json run(Args& a) {
779 const Matrix<T> gamma = a.matrix<T>("gamma");
780 const std::vector<int> m = a.ints("m");
781 const SizeCaps s = read_size_caps(a, "cache_erec", "k");
782 a.done();
783 Json r;
784 r["E"] = encode_scalar(s.given ? cache::cache_erec(gamma, m, s.sigma, s.k)
785 : cache::cache_erec(gamma, m));
786 return r;
787 }
788};
789
790struct OpCacheProbErec {
791 template <class T>
792 static Json run(Args& a) {
793 const Matrix<T> gamma = a.matrix<T>("gamma");
794 const std::vector<int> m = a.ints("m");
795 const SizeCaps s = read_size_caps(a, "cache_prob_erec", "k");
796 a.done();
797 Json r;
798 r["prob"] = encode_matrix(s.given ? cache::cache_prob_erec(gamma, m, s.sigma, s.k)
799 : cache::cache_prob_erec(gamma, m));
800 return r;
801 }
802};
803
804/**
805 * cache_cost(gamma, m, sigma[, k[, pij]]) -> K, pij. As in the reference, an
806 * absent pij is the cache_prob_erec occupancy, and it is returned either way.
807 */
808struct OpCacheCost {
809 template <class T>
810 static Json run(Args& a) {
811 const Matrix<T> gamma = a.matrix<T>("gamma");
812 const std::vector<int> m = a.ints("m");
813 const std::vector<int> sigma = a.ints("sigma");
814 const std::vector<int> k = a.ints_or_empty("k");
815 const bool hasPij = a.has("pij");
816 Matrix<T> pij = a.matrix_or_empty<T>("pij");
817 a.done();
818 if (!hasPij) pij = cache::cache_prob_erec(gamma, m, sigma, k);
819 Json r;
820 r["K"] = encode_vector(cache::cache_cost(gamma, m, sigma, k, pij));
821 r["pij"] = encode_matrix(pij);
822 return r;
823 }
824};
825
826/** viol: one row [item, list, blockinglist] per blocked pair, 0-based like parent. */
827struct OpCacheCostPathcheck {
828 template <class T>
829 static Json run(Args& a) {
830 const Matrix<T> gamma = a.matrix<T>("gamma");
831 const std::vector<int> sigma = a.ints("sigma");
832 const std::vector<int> k = a.ints("k");
833 const std::vector<int> parent = a.ints("parent"); // 0-based, -1 = rooted in the miss list
834 a.done();
835 Json viol = Json::array();
836 for (const cache::CacheBlockedPair& p : cache::cache_cost_pathcheck(gamma, sigma, k, parent))
837 viol.push_back(encode_sizes({p.item, p.list, p.blocking_list}));
838 Json r;
839 r["viol"] = viol;
840 return r;
841 }
842};
843
844struct OpCacheSpmSize {
845 template <class T>
846 static Json run(Args& a) {
847 const Matrix<T> gamma = a.matrix<T>("gamma");
848 const std::vector<int> m = a.ints("m");
849 const std::vector<int> sigma = a.ints("sigma");
850 const std::vector<int> k = a.ints("k");
851 const bool hasMode = a.has("costmode");
852 const std::string mode = a.text("costmode", "");
853 a.done();
854 cache::CacheSpmSizeResult<T> s;
855 if (hasMode) {
857 if (mode == "atmost" || mode == "ATMOST" || mode == "AtMost")
859 else if (mode == "exact" || mode == "EXACT" || mode == "Exact")
861 else
862 throw InputError("cache_spm_size: costmode must be 'atmost' or 'exact', got '" +
863 mode + "'");
864 s = cache::cache_spm_size(gamma, m, sigma, k, cm);
865 } else {
866 s = cache::cache_spm_size(gamma, m, sigma, k);
867 }
868 Json out;
869 out["xi"] = encode_vector(s.xi);
870 out["zeta"] = encode_vector(s.zeta);
871 Json b = Json::array();
872 for (bool x : s.binding) b.push_back(x);
873 out["binding"] = b;
874 out["pij"] = encode_matrix(s.pij);
875 out["K"] = encode_vector(s.k_mean);
876 out["phi"] = encode_scalar(s.phi);
877 out["logdetSigma"] = encode_scalar(s.logdet_sigma);
878 out["iter"] = encode_count(s.iterations);
879 out["relerrEst"] = s.relerr_est;
880 out["method"] = s.method;
881 out["span"] = s.span;
882 Json r;
883 r["E"] = encode_scalar(s.e);
884 r["logE"] = encode_scalar(s.log_e);
885 r["out"] = out;
886 return r;
887 }
888};
889
890struct OpCacheGamma {
891 template <class T>
892 static Json run(Args& a) {
893 const std::vector<Matrix<T> > lambda = matrices_from_json<T>(a.get("lambda"), "cache_gamma: lambda");
894 const auto R = matrix_lists_from_json<T>(a.get("R"), "cache_gamma: R");
895 a.done();
896 const cache::CacheGammaGraphResult<T> g = cache::cache_gamma(lambda, R);
897 Json r;
898 r["gamma"] = encode_matrix(g.gamma);
899 r["u"] = encode_count(g.u);
900 r["n"] = encode_count(g.n);
901 r["h"] = encode_count(g.h);
902 return r;
903 }
904};
905
906struct OpCacheGammaLp {
907 template <class T>
908 static Json run(Args& a) {
909 const std::vector<Matrix<T> > lambda = matrices_from_json<T>(a.get("lambda"), "cache_gamma_lp: lambda");
910 const auto R = matrix_lists_from_json<T>(a.get("R"), "cache_gamma_lp: R");
911 a.done();
912 const cache::CacheGammaResult<T> g = cache::cache_gamma_lp(lambda, R);
913 Json r;
914 r["gamma"] = encode_matrix(g.gamma);
915 r["u"] = encode_count(g.u);
916 r["n"] = encode_count(g.n);
917 r["h"] = encode_count(g.h);
918 r["parent"] = encode_ints(g.parent); // 0-based, -1 = rooted in the miss list
919 return r;
920 }
921};
922
923/** cache_miss_asy(gamma, m[, maxiter, tol]): the port takes the two together. */
924struct OpCacheMissAsy {
925 template <class T>
926 static Json run(Args& a) {
927 const Matrix<T> gamma = a.matrix<T>("gamma");
928 const std::vector<int> m = a.ints("m");
929 const bool hi = a.has("maxiter"), ht = a.has("tol");
930 if (hi != ht)
931 throw InputError("cache_miss_asy: the port takes maxiter and tol together; give "
932 "both or neither");
933 const int maxiter = a.integer("maxiter", 0);
934 const T tol = a.scalar<T>("tol", T(0));
935 a.done();
936 Json r;
937 r["missratio"] = encode_scalar(hi ? cache::cache_miss_asy(gamma, m, maxiter, tol)
938 : cache::cache_miss_asy(gamma, m));
939 return r;
940 }
941};
942
943struct OpCacheMiss {
944 template <class T>
945 static Json run(Args& a) {
946 const Matrix<T> gamma = a.matrix<T>("gamma");
947 const std::vector<int> m = a.ints("m");
948 const bool hasLambda = a.has("lambda");
949 const Matrix<T> lambda = a.matrix_or_empty<T>("lambda");
950 const SizeCaps s = read_size_caps(a, "cache_miss", "cap");
951 a.done();
952 const cache::CacheMissResult<T> x =
953 hasLambda ? (s.given ? cache::cache_miss(gamma, m, lambda, s.sigma, s.k)
954 : cache::cache_miss(gamma, m, lambda))
955 : (s.given ? cache::cache_miss(gamma, m, s.sigma, s.k)
956 : cache::cache_miss(gamma, m));
957 return cache_miss_results(x.M, x.MU, x.MI, x.pi0);
958 }
959};
960
961struct OpCacheMissFpi {
962 template <class T>
963 static Json run(Args& a) {
964 const Matrix<T> gamma = a.matrix<T>("gamma");
965 const std::vector<int> m = a.ints("m");
966 const Matrix<T> lambda = a.matrix<T>("lambda");
967 a.done();
968 const cache::CacheMissResult<T> x = cache::cache_miss_fpi(gamma, m, lambda);
969 return cache_miss_results(x.M, x.MU, x.MI, x.pi0);
970 }
971};
972
973struct OpCacheMissSpm {
974 template <class T>
975 static Json run(Args& a) {
976 const Matrix<T> gamma = a.matrix<T>("gamma");
977 const std::vector<int> m = a.ints("m");
978 const Matrix<T> lambda = a.matrix<T>("lambda");
979 a.done();
980 const cache::CacheMissSpmResult<T> x = cache::cache_miss_spm(gamma, m, lambda);
981 Json r = cache_miss_results(x.M, x.MU, x.MI, x.pi0);
982 r["lE"] = encode_scalar(x.lE);
983 return r;
984 }
985};
986
987/**
988 * cache_miss_rmf(gamma, m, lambda[, tspan, x0init, accost, wgen]).
989 *
990 * The steady state is the port's cache_miss_rmf; a tspan adds the transient
991 * of cache_miss_rmf_transient over [tspan(1), tspan(end)], from x0init, with
992 * the sojourn clock wgen = {A, c, phi0} when given. Two port gaps surface as
993 * refusals: the transient port has no default initial occupancy, so tspan
994 * needs x0init; and the accost overload takes the fixed-point horizon tmax
995 * explicitly (the reference fixes it), so accost needs tmax. tmax is a
996 * port-only key, optional otherwise.
997 */
998struct OpCacheMissRmf {
999 template <class T>
1000 static Json run(Args& a) {
1001 const Matrix<T> gm = a.matrix<T>("gamma");
1002 const std::vector<T> gamma(gm.data(), gm.data() + gm.size());
1003 const std::vector<int> m = a.ints("m");
1004 const Matrix<T> lambda = a.matrix<T>("lambda");
1005 const bool hasTmax = a.has("tmax");
1006 const T tmax = a.scalar<T>("tmax", T(0));
1007 const bool hasAccost = a.has("accost");
1008 const auto accost = hasAccost
1009 ? matrix_lists_from_json<T>(a.get("accost"), "cache_miss_rmf: accost")
1010 : std::vector<std::vector<Matrix<T> > >();
1011 const bool hasTspan = a.has("tspan");
1012 const std::vector<T> tspan = a.vector_or_empty<T>("tspan");
1013 const bool hasX0 = a.has("x0init");
1014 const std::vector<T> x0init = a.vector_or_empty<T>("x0init");
1015 const Json* wj = a.opt("wgen");
1016 a.done();
1017 if (hasAccost && !hasTmax)
1018 throw InputError("cache_miss_rmf: the port's access-graph overload takes the "
1019 "fixed-point horizon explicitly; give tmax with accost");
1020 if (hasX0 && !hasTspan)
1021 throw InputError("cache_miss_rmf: x0init is the initial occupancy of the transient "
1022 "and needs tspan");
1023 if (wj && !hasTspan)
1024 throw InputError("cache_miss_rmf: wgen weights the transient and needs tspan");
1025 if (hasTspan && !hasX0)
1026 throw InputError("cache_miss_rmf: the port's transient (cache_miss_rmf_transient) "
1027 "has no default initial occupancy; give x0init with tspan");
1028 if (hasTspan && tspan.size() != 2)
1029 throw InputError("cache_miss_rmf: tspan is [t0 t1]; the port integrates between "
1030 "the two end points only");
1031 cache::CacheSojournClock<T> clk;
1032 if (wj) {
1033 Args w(*wj, "cache_miss_rmf wgen");
1034 clk.A = w.matrix<T>("A");
1035 clk.c = w.vector<T>("c");
1036 clk.phi0 = w.vector<T>("phi0");
1037 w.done();
1038 }
1039 const cache::CacheMissRmfResult<T> s =
1040 hasAccost ? cache::cache_miss_rmf(gamma, m, lambda, tmax, accost)
1041 : (hasTmax ? cache::cache_miss_rmf(gamma, m, lambda, tmax)
1042 : cache::cache_miss_rmf(gamma, m, lambda));
1043 Json r = cache_miss_results(s.M, s.MU, s.MI, s.pi0);
1044 r["xss"] = encode_vector(s.xss);
1045 r["Wcov"] = encode_matrix(s.Wcov);
1046 if (hasTspan) {
1047 const cache::CacheMissRmfResult<T> tr =
1048 hasAccost ? cache::cache_miss_rmf_transient(m, lambda, tspan[0], tspan[1], x0init,
1049 accost, clk)
1050 : cache::cache_miss_rmf_transient(m, lambda, tspan[0], tspan[1], x0init,
1051 clk);
1052 r["tout"] = encode_vector(tr.tout);
1053 r["pi0_t"] = encode_matrix(tr.pi0_t);
1054 r["MU_t"] = encode_matrix(tr.MU_t);
1055 r["xtraj"] = encode_matrix(tr.xtraj);
1056 if (wj) {
1057 Json sj;
1058 sj["xbar"] = encode_vector(tr.sojourn.xbar);
1059 sj["wtot"] = encode_scalar(tr.sojourn.wtot);
1060 sj["MU"] = encode_vector(tr.sojourn.MU);
1061 r["sj"] = sj;
1062 }
1063 }
1064 return r;
1065 }
1066};
1067
1068/** (n*(h+1)) flat occupancy x[k + s*n] as the (n x h+1) matrix MATLAB reshapes it to. */
1069template <class T>
1070Matrix<T> occupancy_matrix(const std::vector<T>& x, std::size_t n) {
1071 const std::size_t c = n ? x.size() / n : 0;
1072 Matrix<T> X(n, c);
1073 for (std::size_t s = 0; s < c; ++s)
1074 for (std::size_t k = 0; k < n; ++k) X(k, s) = x[k + s * n];
1075 return X;
1076}
1077
1078/**
1079 * cache_rrm_meanfield is a SCRIPT in MATLAB with its data fixed inline; the
1080 * port takes that data as (lambda, m[, tmax]), and the entry reads it under
1081 * those names. x is returned as the (n x h+1) matrix the script reshapes.
1082 */
1083struct OpCacheRrmMeanfield {
1084 template <class T>
1085 static Json run(Args& a) {
1086 const std::vector<T> lambda = a.vector<T>("lambda");
1087 const std::vector<int> m = a.ints("m");
1088 const bool hasTmax = a.has("tmax");
1089 const T tmax = a.scalar<T>("tmax", T(0));
1090 a.done();
1091 const cache::CacheRrmMeanfieldResult<T> s =
1092 hasTmax ? cache::cache_rrm_meanfield(lambda, m, tmax) : cache::cache_rrm_meanfield(lambda, m);
1093 Json r;
1094 r["x"] = encode_matrix(occupancy_matrix(s.x, lambda.size()));
1095 r["missrate"] = encode_scalar(s.missrate);
1096 r["missratio"] = encode_scalar(s.missratio);
1097 return r;
1098 }
1099};
1100
1101/**
1102 * cache_rrm_meanfield_ode(t, x, lambda, m, n, h). The drift is autonomous, so
1103 * the time argument (unnamed in MATLAB) is not taken. The port derives n and h
1104 * from lambda and m; when given they are checked against those sizes.
1105 */
1106struct OpCacheRrmMeanfieldOde {
1107 template <class T>
1108 static Json run(Args& a) {
1109 const Matrix<T> X = a.matrix<T>("x");
1110 const std::vector<T> lambda = a.vector<T>("lambda");
1111 const std::vector<int> m = a.ints("m");
1112 const bool hn = a.has("n"), hh = a.has("h");
1113 const std::size_t n = a.count("n", 0), h = a.count("h", 0);
1114 a.done();
1115 if (hn && n != lambda.size())
1116 throw InputError("cache_rrm_meanfield_ode: n = " + std::to_string(n) +
1117 " but lambda has " + std::to_string(lambda.size()) + " items");
1118 if (hh && h != m.size())
1119 throw InputError("cache_rrm_meanfield_ode: h = " + std::to_string(h) + " but m has " +
1120 std::to_string(m.size()) + " lists");
1121 // x is the flat column x(:) of the (n x h+1) occupancy, or that matrix itself.
1122 std::vector<T> x;
1123 if (X.rows() == lambda.size() && X.cols() == m.size() + 1) {
1124 for (std::size_t s = 0; s < X.cols(); ++s)
1125 for (std::size_t k = 0; k < X.rows(); ++k) x.push_back(X(k, s));
1126 } else {
1127 x.assign(X.data(), X.data() + X.size());
1128 }
1129 Json r;
1130 r["dxdt"] = encode_vector(cache::cache_rrm_meanfield_ode(x, lambda, m));
1131 return r;
1132 }
1133};
1134
1135struct OpCacheMva {
1136 template <class T>
1137 static Json run(Args& a) {
1138 const Matrix<T> gamma = a.matrix<T>("gamma");
1139 const std::vector<int> m = a.ints("m");
1140 a.done();
1141 const cache::CacheMvaResult<T> s = cache::cache_mva(gamma, m);
1142 Json r;
1143 r["pi"] = encode_vector(s.pi);
1144 r["pi0"] = encode_vector(s.pi0);
1145 r["pij"] = encode_matrix(s.pij);
1146 r["x"] = encode_vector(s.x);
1147 r["u"] = encode_matrix(s.u);
1148 r["E"] = encode_scalar(s.E);
1149 return r;
1150 }
1151};
1152
1153struct OpCacheMvaMiss {
1154 template <class T>
1155 static Json run(Args& a) {
1156 const std::vector<T> p = a.vector<T>("p");
1157 const std::vector<int> m = a.ints("m");
1158 const Matrix<T> R = a.matrix<T>("R");
1159 a.done();
1160 const cache::CacheMvaMissResult<T> s = cache::cache_mva_miss(p, m, R);
1161 Json r;
1162 r["M"] = encode_scalar(s.M);
1163 r["Mk"] = encode_vector(s.Mk);
1164 return r;
1165 }
1166};
1167
1168/** The (gamma, m) -> prob family. */
1169#define LINE_CACHE_GM_MATRIX_OP(Name, fn, out, ...) \
1170 struct Name { \
1171 template <class T> \
1172 static Json run(Args& a) { \
1173 const Matrix<T> gamma = a.matrix<T>("gamma"); \
1174 const std::vector<int> m = a.ints("m"); \
1175 __VA_ARGS__; \
1176 a.done(); \
1177 Json r; \
1178 r[out] = encode_matrix(cache::fn(gamma, m)); \
1179 return r; \
1180 } \
1181 };
1182LINE_CACHE_GM_MATRIX_OP(OpCacheProbFpi, cache_prob_fpi, "prob", (void)0)
1183LINE_CACHE_GM_MATRIX_OP(OpCacheProbSpm, cache_prob_spm, "prob",
1184 a.unsupported("lE", "the port computes the log normalizing constant "
1185 "itself and takes no precomputed value."))
1186#undef LINE_CACHE_GM_MATRIX_OP
1187
1188struct OpCacheTtlHlru {
1189 template <class T>
1190 static Json run(Args& a) {
1191 const Matrix<T> lambda = a.matrix<T>("lambda");
1192 const std::vector<int> m = a.ints("m");
1193 a.done();
1194 Json r;
1195 r["pij"] = encode_matrix(cache::cache_ttl_hlru(lambda, m));
1196 return r;
1197 }
1198};
1199
1200struct OpCacheTHlru {
1201 template <class T>
1202 static Json run(Args& a) {
1203 const Matrix<T> gamma = a.matrix<T>("gamma");
1204 const std::vector<int> m = a.ints("m");
1205 a.done();
1206 Json r;
1207 r["t"] = encode_vector(cache::cache_t_hlru(gamma, m));
1208 return r;
1209 }
1210};
1211
1212struct OpCacheSpm {
1213 template <class T>
1214 static Json run(Args& a) {
1215 const Matrix<T> gamma = a.matrix<T>("gamma");
1216 const std::vector<int> m = a.ints("m");
1217 a.unsupported("xi0", "the port starts the saddle-point iteration from its own initial "
1218 "point and takes none.");
1219 a.done();
1220 const cache::CacheSpmResult<T> s = cache::cache_spm(gamma, m);
1221 Json r;
1222 r["Z"] = encode_scalar(s.Z);
1223 r["lZ"] = encode_scalar(s.lZ);
1224 r["xi"] = encode_vector(s.xi);
1225 return r;
1226 }
1227};
1228
1229struct OpCacheXiFp {
1230 template <class T>
1231 static Json run(Args& a) {
1232 const Matrix<T> gamma = a.matrix<T>("gamma");
1233 const std::vector<int> m = a.ints("m");
1234 a.unsupported("xi", "the port starts the fixed point from its own initial point and "
1235 "takes none.");
1236 a.done();
1237 const cache::CacheXiFpResult<T> s = cache::cache_xi_fp(gamma, m);
1238 Json r;
1239 r["xi"] = encode_vector(s.xi);
1240 r["pi0"] = encode_vector(s.pi0);
1241 r["pij"] = encode_matrix(s.pij);
1242 r["it"] = s.it;
1243 return r;
1244 }
1245};
1246
1247struct OpCacheXiIter {
1248 template <class T>
1249 static Json run(Args& a) {
1250 const Matrix<T> gamma = a.matrix<T>("gamma");
1251 const std::vector<int> m = a.ints("m");
1252 a.unsupported("tmax", "the port has no iteration-count argument.");
1253 a.done();
1254 Json r;
1255 r["z"] = encode_vector(cache::cache_xi_iter(gamma, m));
1256 return r;
1257 }
1258};
1259
1260/**
1261 * Sampling controls of cache_is / cache_miss_is / cache_prob_is: samples and
1262 * the port-only seed come together (the port's overload takes both), and the
1263 * size constraint (sigma, k) needs them, since the port's constrained
1264 * overload is the one taking all four.
1265 */
1266struct IsControls {
1267 bool sampled = false;
1268 std::size_t samples = 0;
1269 std::uint64_t seed = 0;
1270 SizeCaps caps;
1271};
1272
1273IsControls read_is_controls(Args& a, const char* fn) {
1274 IsControls c;
1275 const bool hs = a.has("samples"), hd = a.has("seed");
1276 if (hs != hd)
1277 throw InputError(std::string(fn) + ": the port takes samples and seed together; give "
1278 "both or neither");
1279 c.sampled = hs;
1280 c.samples = a.count("samples", 0);
1281 c.seed = static_cast<std::uint64_t>(a.count("seed", 0));
1282 c.caps = read_size_caps(a, fn, "k");
1283 if (c.caps.given && !c.sampled)
1284 throw InputError(std::string(fn) + ": the port's size-constrained overload also takes "
1285 "samples and seed; give them with sigma and k");
1286 return c;
1287}
1288
1289struct OpCacheIs {
1290 template <class T>
1291 static Json run(Args& a) {
1292 const Matrix<T> gamma = a.matrix<T>("gamma");
1293 const std::vector<int> m = a.ints("m");
1294 const IsControls c = read_is_controls(a, "cache_is");
1295 a.done();
1296 const cache::CacheIsResult<T> s =
1297 c.caps.given ? cache::cache_is(gamma, m, c.samples, c.seed, c.caps.sigma, c.caps.k)
1298 : (c.sampled ? cache::cache_is(gamma, m, c.samples, c.seed)
1299 : cache::cache_is(gamma, m));
1300 Json r;
1301 r["E"] = encode_scalar(s.E);
1302 r["lE"] = encode_scalar(s.lE);
1303 return r;
1304 }
1305};
1306
1307struct OpCacheProbIs {
1308 template <class T>
1309 static Json run(Args& a) {
1310 const Matrix<T> gamma = a.matrix<T>("gamma");
1311 const std::vector<int> m = a.ints("m");
1312 const IsControls c = read_is_controls(a, "cache_prob_is");
1313 a.done();
1314 Json r;
1315 r["prob"] = encode_matrix(
1316 c.caps.given ? cache::cache_prob_is(gamma, m, c.samples, c.seed, c.caps.sigma, c.caps.k)
1317 : (c.sampled ? cache::cache_prob_is(gamma, m, c.samples, c.seed)
1318 : cache::cache_prob_is(gamma, m)));
1319 return r;
1320 }
1321};
1322
1323struct OpCacheMissIs {
1324 template <class T>
1325 static Json run(Args& a) {
1326 const Matrix<T> gamma = a.matrix<T>("gamma");
1327 const std::vector<int> m = a.ints("m");
1328 const Matrix<T> lambda = a.matrix<T>("lambda");
1329 const IsControls c = read_is_controls(a, "cache_miss_is");
1330 a.done();
1331 const cache::CacheMissIsResult<T> s =
1332 c.caps.given
1333 ? cache::cache_miss_is(gamma, m, lambda, c.samples, c.seed, c.caps.sigma, c.caps.k)
1334 : (c.sampled ? cache::cache_miss_is(gamma, m, lambda, c.samples, c.seed)
1335 : cache::cache_miss_is(gamma, m, lambda));
1336 Json r = cache_miss_results(s.M, s.MU, s.MI, s.pi0);
1337 r["lE"] = encode_scalar(s.lE);
1338 return r;
1339 }
1340};
1341
1342struct OpCacheLrumMapLevelstats {
1343 template <class T>
1344 static Json run(Args& a) {
1345 const Matrix<T> D0 = a.matrix<T>("D0");
1346 const Matrix<T> D1 = a.matrix<T>("D1");
1347 const std::vector<T> Tv = a.vector<T>("T");
1348 a.done();
1349 const cache::CacheLrumMapLevelStats<T> s = cache::cache_lrum_map_levelstats(D0, D1, Tv);
1350 Json r;
1351 r["prob"] = encode_vector(s.prob);
1352 r["occ"] = encode_vector(s.occ);
1353 r["hitfrac"] = encode_vector(s.hitfrac);
1354 return r;
1355 }
1356};
1357
1358/** Per-item MAPs from MATLAB's two cells D0c{k}, D1c{k}. */
1359template <class T>
1360std::vector<mam::Map<T> > item_maps(Args& a, const char* fn) {
1361 const std::vector<Matrix<T> > D0 = matrices_from_json<T>(a.get("D0c"), where_of(fn, "D0c"));
1362 const std::vector<Matrix<T> > D1 = matrices_from_json<T>(a.get("D1c"), where_of(fn, "D1c"));
1363 if (D0.size() != D1.size())
1364 throw InputError(std::string(fn) + ": D0c and D1c must have one matrix per item");
1365 std::vector<mam::Map<T> > items(D0.size());
1366 for (std::size_t k = 0; k < D0.size(); ++k) {
1367 items[k].D0 = D0[k];
1368 items[k].D1 = D1[k];
1369 }
1370 return items;
1371}
1372
1373/**
1374 * The LRU(m) MAP characteristic-time solvers. MATLAB stops on fsolve's
1375 * defaults; the port iterates to a relative tolerance it takes as a required
1376 * argument (internal callers pass GlobalConstants::FineTol), so `tol` is a
1377 * required port-only key here, and `maxswp` an optional one.
1378 */
1379struct OpCacheTLrumMap {
1380 template <class T>
1381 static Json run(Args& a) {
1382 const std::vector<mam::Map<T> > items = item_maps<T>(a, "cache_t_lrum_map");
1383 const std::vector<T> m = a.vector<T>("m");
1384 const T tol = a.number<T>("tol");
1385 const bool hasSw = a.has("maxswp");
1386 const unsigned maxswp = a.uinteger("maxswp", 0);
1387 a.done();
1388 Json r;
1389 r["t"] = encode_vector(hasSw ? cache::cache_t_lrum_map(items, m, tol, maxswp)
1390 : cache::cache_t_lrum_map(items, m, tol));
1391 return r;
1392 }
1393};
1394
1395struct OpCacheTtlLrumMap {
1396 template <class T>
1397 static Json run(Args& a) {
1398 const std::vector<mam::Map<T> > items = item_maps<T>(a, "cache_ttl_lrum_map");
1399 const std::vector<T> m = a.vector<T>("m");
1400 const T tol = a.number<T>("tol");
1401 a.done();
1402 const cache::CacheTtlLrumMapResult<T> s = cache::cache_ttl_lrum_map(items, m, tol);
1403 Json r;
1404 r["pij"] = encode_matrix(s.pij);
1405 r["pijtime"] = encode_matrix(s.pijtime);
1406 return r;
1407 }
1408};
1409
1410struct OpCacheTtlLrua {
1411 template <class T>
1412 static Json run(Args& a) {
1413 const std::vector<Matrix<T> > lambda = matrices_from_json<T>(a.get("lambda"), "cache_ttl_lrua: lambda");
1414 const auto R = matrix_lists_from_json<T>(a.get("R"), "cache_ttl_lrua: R");
1415 const std::vector<T> m = a.vector<T>("m");
1416 a.unsupported("seed", "the port solves for the characteristic times by deterministic "
1417 "bisection and draws nothing.");
1418 const T tol = a.number<T>("tol");
1419 const bool hasSw = a.has("maxswp");
1420 const unsigned maxswp = a.uinteger("maxswp", 0);
1421 a.done();
1422 Json r;
1423 r["prob"] = encode_matrix(hasSw ? cache::cache_ttl_lrua(lambda, R, m, tol, maxswp)
1424 : cache::cache_ttl_lrua(lambda, R, m, tol));
1425 return r;
1426 }
1427};
1428
1429// ---------------------------------------------------------------------------
1430// da: decomposition-aggregation helpers
1431// ---------------------------------------------------------------------------
1432
1433/**
1434 * da_cache_isolate(ch, lambda). ch carries MATLAB's sn.nodeparam fields:
1435 * itemcap (h), nitems, pread (one row per class; null or [] for MATLAB's NaN)
1436 * and accost (u x n of (h+1) x (h+1), or [] for the default linear routing).
1437 */
1438struct OpDaCacheIsolate {
1439 template <class T>
1440 static Json run(Args& a) {
1441 const Json& chj = a.get("ch");
1442 const std::vector<T> lambda = a.vector<T>("lambda");
1443 a.done();
1444 if (!chj.is_object()) throw InputError("da_cache_isolate: ch must be an object");
1445 Args ch(chj, "da_cache_isolate ch");
1446 da::CacheParam<T> p;
1447 p.itemcap = ch.ints("itemcap");
1448 p.nitems = ch.count("nitems");
1449 const Json& pr = ch.get("pread");
1450 if (!pr.is_array()) throw InputError("da_cache_isolate: ch.pread must be a list, one row per class");
1451 for (std::size_t v = 0; v < pr.size(); ++v) {
1452 if (pr[v].is_null() || (pr[v].is_array() && pr[v].empty())) {
1453 p.pread.push_back(std::vector<T>());
1454 continue;
1455 }
1456 p.pread.push_back(vector_from_json<T>(pr[v], at("da_cache_isolate: ch.pread", v)));
1457 }
1458 if (ch.has("accost")) {
1459 const Json& ac = ch.get("accost");
1460 if (!(ac.is_array() && ac.empty()))
1461 p.accost = matrix_lists_from_json<T>(ac, "da_cache_isolate: ch.accost");
1462 }
1463 ch.done();
1464 const da::CacheIsolateResult<T> s = da::da_cache_isolate(p, lambda);
1465 Json r;
1466 r["gamma"] = encode_matrix(s.gamma);
1467 r["lambda_cache"] = encode_matrices(s.lambda_cache); // per class, n x (h+1)
1468 r["Rcost"] = encode_matrix_lists(s.Rcost);
1469 r["parent"] = encode_ints(s.parent);
1470 return r;
1471 }
1472};
1473
1474struct OpDaTrafficSuperpos {
1475 template <class T>
1476 static Json run(Args& a) {
1477 const std::vector<T> lambda = a.vector<T>("lambda");
1478 const std::vector<T> a2 = a.vector<T>("a2");
1479 a.done();
1480 Json r;
1481 r["d2"] = encode_scalar(da::da_traffic_superpos(lambda, a2));
1482 return r;
1483 }
1484};
1485
1486// ---------------------------------------------------------------------------
1487// lossn: loss networks
1488// ---------------------------------------------------------------------------
1489
1490struct OpLossnErlangfp {
1491 template <class T>
1492 static Json run(Args& a) {
1493 const std::vector<T> nu = a.vector<T>("nu");
1494 const Matrix<T> A = a.matrix<T>("A");
1495 const std::vector<T> C = a.vector<T>("C");
1496 a.done();
1497 const lossn::ErlangFpResult<T> s = lossn::lossn_erlangfp(nu, A, C);
1498 Json r;
1499 r["QLen"] = encode_vector(s.QLen);
1500 r["Loss"] = encode_vector(s.Loss);
1501 r["E"] = encode_matrix(s.E);
1502 r["niter"] = encode_count(s.iterations);
1503 return r;
1504 }
1505};
1506
1507/** lossn_manjunath(nu, A, C); options.max_live_states is the port's state cap. */
1508struct OpLossnManjunath {
1509 template <class T>
1510 static Json run(Args& a) {
1511 const std::vector<T> nu = a.vector<T>("nu");
1512 const Matrix<T> A = a.matrix<T>("A");
1513 const std::vector<T> C = a.vector<T>("C");
1514 lossn::LossnManjunathOptions o;
1515 if (a.has("options")) {
1516 Args op(*a.opt("options"), "lossn_manjunath options");
1517 if (op.has("max_live_states")) o.max_live_states = op.count("max_live_states");
1518 op.done();
1519 }
1520 a.done();
1521 const lossn::LossnManjunathResult<T> s = lossn::lossn_manjunath(nu, A, C, o);
1522 Json r;
1523 r["QLen"] = encode_vector(s.QLen);
1524 r["Loss"] = encode_vector(s.Loss);
1525 r["lG"] = s.lG;
1526 r["niter"] = encode_count(s.iterations);
1527 return r;
1528 }
1529};
1530
1531/**
1532 * lossn_mci(nu, A, C, options): options {samples, alpha, gamma, seed}. The
1533 * port draws from a seeded generator only, so options.seed is required: MATLAB
1534 * falls back to the session RNG state, which has no JSON form.
1535 */
1536struct OpLossnMci {
1537 template <class T>
1538 static Json run(Args& a) {
1539 const std::vector<T> nu = a.vector<T>("nu");
1540 const Matrix<T> A = a.matrix<T>("A");
1541 const std::vector<T> C = a.vector<T>("C");
1542 const Json* oj = a.opt("options");
1543 a.done();
1544 if (!oj)
1545 throw InputError("lossn_mci: options.seed is required; the port draws from a seeded "
1546 "generator and has no session RNG to fall back on");
1547 Args op(*oj, "lossn_mci options");
1548 lossn::LossnMciOptions<T> o;
1549 if (op.has("samples")) o.samples = op.count("samples");
1550 if (op.has("alpha")) o.alpha = op.scalar<double>("alpha", 0.0);
1551 if (op.has("gamma")) o.gamma = op.vector<T>("gamma");
1552 if (op.has("revenue")) o.revenue = op.vector<T>("revenue");
1553 if (!op.has("seed"))
1554 throw InputError("lossn_mci: options.seed is required; the port draws from a seeded "
1555 "generator and has no session RNG to fall back on");
1556 const std::uint64_t seed = static_cast<std::uint64_t>(op.uinteger("seed"));
1557 op.done();
1558 const lossn::LossnMciResult<T> s = lossn::lossn_mci(nu, A, C, o, seed);
1559 Json ci;
1560 ci["accept"] = encode_matrix(s.acceptCI);
1561 ci["loss"] = encode_matrix(s.lossCI);
1562 ci["acceptPoint"] = encode_vector(s.acceptPoint);
1563 ci["lossPoint"] = encode_vector(s.lossPoint);
1564 ci["level"] = encode_scalar(s.level);
1565 ci["linkLossPoint"] = encode_matrix(s.linkLossPoint);
1566 if (s.hasRevenue) {
1567 ci["revenue"] = encode_scalar(s.revenue);
1568 ci["revsens"] = encode_vector(s.revsens);
1569 ci["revsensCI"] = encode_matrix(s.revsensCI);
1570 ci["impliedCost"] = encode_vector(s.impliedCost);
1571 ci["linkImpliedCost"] = encode_matrix(s.linkImpliedCost);
1572 }
1573 Json r;
1574 r["QLen"] = encode_vector(s.QLen);
1575 r["Loss"] = encode_vector(s.Loss);
1576 r["lG"] = s.lG;
1577 r["ci"] = ci;
1578 r["nsamples"] = encode_count(s.nsamples);
1579 return r;
1580 }
1581};
1582
1583struct OpLossnRec {
1584 template <class T>
1585 static Json run(Args& a) {
1586 const std::vector<T> nu = a.vector<T>("nu");
1587 const Matrix<T> A = a.matrix<T>("A");
1588 const std::vector<T> C = a.vector<T>("C");
1589 a.done();
1590 const lossn::LossnRecResult<T> s = lossn::lossn_rec(nu, A, C);
1591 Json r;
1592 r["QLen"] = encode_vector(s.QLen);
1593 r["Loss"] = encode_vector(s.Loss);
1594 r["lG"] = s.lG;
1595 r["niter"] = s.iterations;
1596 return r;
1597 }
1598};
1599
1600/** lossn_kr(rho, b, C): occupancy q, blocking B, carried load QLen. */
1601struct OpLossnKr {
1602 template <class T>
1603 static Json run(Args& a) {
1604 const std::vector<T> rho = a.vector<T>("rho");
1605 const std::vector<T> b = a.vector<T>("b");
1606 const T C = a.number<T>("C");
1607 a.done();
1608 const lossn::KrResult<T> s = lossn::lossn_kr(rho, b, C);
1609 Json r;
1610 r["q"] = encode_vector(s.q);
1611 r["B"] = encode_vector(s.B);
1612 r["QLen"] = encode_vector(s.QLen);
1613 return r;
1614 }
1615};
1616
1617/** lossn_kr_dim(rho, b, Btarget); a scalar Btarget applies to every class. */
1618struct OpLossnKrDim {
1619 template <class T>
1620 static Json run(Args& a) {
1621 const std::vector<T> rho = a.vector<T>("rho");
1622 const std::vector<T> b = a.vector<T>("b");
1623 std::vector<T> Bt = a.vector<T>("Btarget");
1624 a.done();
1625 if (Bt.size() == 1) Bt.assign(rho.size(), Bt[0]);
1626 const lossn::KrDimResult<T> s = lossn::lossn_kr_dim(rho, b, Bt);
1627 Json r;
1628 r["C"] = s.C;
1629 r["B"] = encode_vector(s.B);
1630 return r;
1631 }
1632};
1633
1634/** lossn_kr_asy(rho, b, C): critical-loading blocking, alpha, sigma, delta. */
1635struct OpLossnKrAsy {
1636 template <class T>
1637 static Json run(Args& a) {
1638 const std::vector<T> rho = a.vector<T>("rho");
1639 const std::vector<T> b = a.vector<T>("b");
1640 const T C = a.number<T>("C");
1641 a.done();
1642 const lossn::KrAsyResult<T> s = lossn::lossn_kr_asy(rho, b, C);
1643 Json r;
1644 r["B"] = encode_vector(s.B);
1645 r["alpha"] = encode_scalar(s.alpha);
1646 r["sigma"] = encode_scalar(s.sigma);
1647 r["delta"] = encode_scalar(s.delta);
1648 return r;
1649 }
1650};
1651
1652/** lossn_asy(nu, A, C): the Kelly limit. */
1653struct OpLossnAsy {
1654 template <class T>
1655 static Json run(Args& a) {
1656 const std::vector<T> nu = a.vector<T>("nu");
1657 const Matrix<T> A = a.matrix<T>("A");
1658 const std::vector<T> C = a.vector<T>("C");
1659 a.done();
1660 const lossn::LossnAsyResult<T> s = lossn::lossn_asy(nu, A, C);
1661 Json r;
1662 r["QLen"] = encode_vector(s.QLen);
1663 r["Loss"] = encode_vector(s.Loss);
1664 r["a"] = encode_vector(s.a);
1665 r["niter"] = s.niter;
1666 return r;
1667 }
1668};
1669
1670/**
1671 * The options struct lossn_links and lossn_revsens forward to lossn_mci:
1672 * {samples, alpha, gamma, seed}. The seed is required only when the method is
1673 * 'mci', for the reason OpLossnMci gives.
1674 */
1675template <class T>
1676void lossn_parse_mci_options(const Json* oj, const std::string& fn, bool isMci,
1677 lossn::LossnMciOptions<T>& o, std::uint64_t& seed) {
1678 bool haveSeed = false;
1679 if (oj) {
1680 Args op(*oj, fn + " options");
1681 if (op.has("samples")) o.samples = op.count("samples");
1682 if (op.has("alpha")) o.alpha = op.scalar<double>("alpha", 0.0);
1683 if (op.has("gamma")) o.gamma = op.vector<T>("gamma");
1684 if (op.has("seed")) {
1685 seed = static_cast<std::uint64_t>(op.uinteger("seed"));
1686 haveSeed = true;
1687 }
1688 op.done();
1689 }
1690 if (isMci && !haveSeed)
1691 throw InputError(fn + ": options.seed is required for method 'mci'; the port draws from a "
1692 "seeded generator and has no session RNG to fall back on");
1693}
1694
1695/** lossn_links(nu, A, C, method, options): per-link blocking E and occupancy Occ. */
1696struct OpLossnLinks {
1697 template <class T>
1698 static Json run(Args& a) {
1699 const std::vector<T> nu = a.vector<T>("nu");
1700 const Matrix<T> A = a.matrix<T>("A");
1701 const std::vector<T> C = a.vector<T>("C");
1702 const std::string method = a.text("method", "exact");
1703 const Json* oj = a.opt("options");
1704 a.done();
1705 lossn::LossnMciOptions<T> o;
1706 std::uint64_t seed = 0;
1707 lossn_parse_mci_options<T>(oj, "lossn_links", lossn::detail::lossn_lower(method) == "mci", o,
1708 seed);
1709 const lossn::LossnLinksResult<T> s = lossn::lossn_links(nu, A, C, method, o, seed);
1710 Json r;
1711 r["E"] = encode_matrix(s.E);
1712 r["Occ"] = encode_vector(s.Occ);
1713 r["Loss"] = encode_vector(s.Loss);
1714 r["QLen"] = encode_vector(s.QLen);
1715 return r;
1716 }
1717};
1718
1719/** lossn_revsens(nu, A, C, w, method, options): revenue sensitivities and implied costs. */
1720struct OpLossnRevsens {
1721 template <class T>
1722 static Json run(Args& a) {
1723 const std::vector<T> nu = a.vector<T>("nu");
1724 const Matrix<T> A = a.matrix<T>("A");
1725 const std::vector<T> C = a.vector<T>("C");
1726 const std::vector<T> w = a.vector<T>("w");
1727 const std::string method = a.text("method", "exact");
1728 const Json* oj = a.opt("options");
1729 a.done();
1730 lossn::LossnMciOptions<T> o;
1731 std::uint64_t seed = 0;
1732 lossn_parse_mci_options<T>(oj, "lossn_revsens", lossn::detail::lossn_lower(method) == "mci",
1733 o, seed);
1734 const lossn::LossnRevsensResult<T> s = lossn::lossn_revsens(nu, A, C, w, method, o, seed);
1735 Json r;
1736 r["dW"] = encode_vector(s.dW);
1737 r["c"] = encode_vector(s.c);
1738 r["W"] = encode_scalar(s.W);
1739 r["clink"] = encode_matrix(s.clink);
1740 if (s.hasCI) {
1741 Json ci;
1742 ci["revsens"] = encode_vector(s.revsens);
1743 ci["revsensCI"] = encode_matrix(s.revsensCI);
1744 ci["level"] = encode_scalar(s.level);
1745 r["ci"] = ci;
1746 } else {
1747 r["ci"] = Json::array();
1748 }
1749 return r;
1750 }
1751};
1752
1753// ---------------------------------------------------------------------------
1754// dqsys_bernoulli1
1755// ---------------------------------------------------------------------------
1756
1757/**
1758 * dqsys_bernoulli1(b, p[, L]). Without L the buffer is unbounded and b, p are
1759 * scalars; MATLAB then returns pmf and arrivalPmf as function handles, which
1760 * have no JSON form and are left out (the closed-form law is Geo/Geo/1, see
1761 * dqsys_geogeo1). With L every field is a plain value.
1762 */
1763struct OpDqsysBernoulli1 {
1764 template <class T>
1765 static Json run(Args& a) {
1766 Json r;
1767 if (!a.has("L")) {
1768 const T b = a.number<T>("b");
1769 const T p = a.number<T>("p");
1770 a.done();
1771 const dqsys::Bernoulli1InfiniteResult<T> s = dqsys::dqsys_bernoulli1(b, p);
1772 r["capacity"] = "Inf";
1773 r["arrivalProb"] = encode_scalar(s.arrivalProb);
1774 r["serviceProb"] = encode_scalar(s.serviceProb);
1775 r["emptyProb"] = encode_scalar(s.emptyProb);
1776 r["utilization"] = encode_scalar(s.utilization);
1777 r["throughput"] = encode_scalar(s.throughput);
1778 r["lossProb"] = encode_scalar(s.lossProb);
1779 r["meanQueueLength"] = encode_scalar(s.meanQueueLength);
1780 r["meanWaitingQueue"] = encode_scalar(s.meanWaitingQueue);
1781 r["meanSojournTime"] = encode_scalar(s.meanSojournTime);
1782 r["meanWaitingTime"] = encode_scalar(s.meanWaitingTime);
1783 r["normConst"] = encode_scalar(s.normConst);
1784 r["analyzer"] = s.analyzer;
1785 return r;
1786 }
1787 const std::vector<T> b = a.vector<T>("b");
1788 const std::vector<T> p = a.vector<T>("p");
1789 const std::size_t L = a.count("L");
1790 a.done();
1791 const dqsys::Bernoulli1Result<T> s = dqsys::dqsys_bernoulli1(b, p, L);
1792 r["capacity"] = encode_count(s.capacity);
1793 r["arrivalProb"] = encode_vector(s.arrivalProb);
1794 r["serviceProb"] = encode_vector(s.serviceProb);
1795 r["pmf"] = encode_vector(s.pmf);
1796 r["arrivalPmf"] = encode_vector(s.arrivalPmf);
1797 r["emptyProb"] = encode_scalar(s.emptyProb);
1798 r["utilization"] = encode_scalar(s.utilization);
1799 r["throughput"] = encode_scalar(s.throughput);
1800 r["lossProb"] = encode_scalar(s.lossProb);
1801 r["meanQueueLength"] = encode_scalar(s.meanQueueLength);
1802 r["meanWaitingQueue"] = encode_scalar(s.meanWaitingQueue);
1803 r["meanSojournTime"] = encode_scalar(s.meanSojournTime);
1804 r["meanWaitingTime"] = encode_scalar(s.meanWaitingTime);
1805 r["normConst"] = encode_scalar(s.normConst);
1806 r["analyzer"] = s.analyzer;
1807 return r;
1808 }
1809};
1810
1811// ---------------------------------------------------------------------------
1812// MAP and phase-type helpers
1813// ---------------------------------------------------------------------------
1814
1815struct OpMap2ph {
1816 template <class T>
1817 static Json run(Args& a) {
1818 const mam::Map<T> m = map_from_json<T>(a.get("MAPIN"), "map2ph: MAPIN");
1819 a.done();
1820 const mam::PhType<T> ph = mam::map2ph(m);
1821 Json r;
1822 r["alpha"] = encode_vector(ph.alpha);
1823 r["T"] = encode_matrix(ph.subgen);
1824 return r;
1825 }
1826};
1827
1828struct OpMap2mmpp {
1829 template <class T>
1830 static Json run(Args& a) {
1831 const mam::Map<T> m = map_from_json<T>(a.get("MAP"), "map2mmpp: MAP");
1832 a.done();
1833 const mam::Map2mmppResult<T> s = mam::map2mmpp(m);
1834 Json r;
1835 r["Q"] = encode_matrix(s.Q);
1836 r["LAMBDA"] = encode_matrix(s.LAMBDA);
1837 return r;
1838 }
1839};
1840
1841struct OpAutocov {
1842 template <class T>
1843 static Json run(Args& a) {
1844 const std::vector<T> X = a.vector<T>("X");
1845 a.done();
1846 Json r;
1847 r["acv"] = encode_vector(trace::autocov(X));
1848 return r;
1849 }
1850};
1851
1852/** aph_fit(e1, e2, e3[, nmax]); tol is the port's moment-matching tolerance and needs nmax. */
1853struct OpAphFit {
1854 template <class T>
1855 static Json run(Args& a) {
1856 const T e1 = a.number<T>("e1");
1857 const T e2 = a.number<T>("e2");
1858 const T e3 = a.number<T>("e3");
1859 const bool hn = a.has("nmax"), ht = a.has("tol");
1860 if (ht && !hn)
1861 throw InputError("aph_fit: tol follows nmax in the port's overloads; give nmax too");
1862 mam::AphFitResult<T> s;
1863 if (ht) {
1864 const unsigned nmax = static_cast<unsigned>(a.count("nmax"));
1865 const T tol = a.number<T>("tol");
1866 a.done();
1867 s = mam::aph_fit(e1, e2, e3, nmax, tol);
1868 } else if (hn) {
1869 const unsigned nmax = static_cast<unsigned>(a.count("nmax"));
1870 a.done();
1871 s = mam::aph_fit(e1, e2, e3, nmax);
1872 } else {
1873 a.done();
1874 s = mam::aph_fit(e1, e2, e3);
1875 }
1876 Json r;
1877 r["APH"] = encode_map(s.aph);
1878 r["isexact"] = s.isexact;
1879 return r;
1880 }
1881};
1882
1883/** m3pp22_fitc_approx_cov: FIT is the MMAP cell {D0, D1, D1_1, D1_2}. */
1884struct OpM3pp22FitcApproxCov {
1885 template <class T>
1886 static Json run(Args& a) {
1887 const T av = a.number<T>("a");
1888 const T bt1 = a.number<T>("bt1");
1889 const T bt2 = a.number<T>("bt2");
1890 const T binf = a.number<T>("binf");
1891 const T m3t2 = a.number<T>("m3t2");
1892 const T t1 = a.number<T>("t1");
1893 const T t2 = a.number<T>("t2");
1894 const std::vector<T> ai = a.vector<T>("ai");
1895 const T st3 = a.number<T>("st3");
1896 const T t3 = a.number<T>("t3");
1897 a.done();
1898 const mam::M3pp22FitcCovResult<T> s =
1899 mam::m3pp22_fitc_approx_cov(av, bt1, bt2, binf, m3t2, t1, t2, ai, st3, t3);
1900 Json fit = Json::array();
1901 fit.push_back(encode_matrix(s.mmap.D0));
1902 fit.push_back(encode_matrix(s.mmap.D1));
1903 for (const auto& d : s.mmap.Dc) fit.push_back(encode_matrix(d));
1904 Json r;
1905 r["FIT"] = fit;
1906 return r;
1907 }
1908};
1909
1910struct OpPhMultisets {
1911 template <class T>
1912 static Json run(Args& a) {
1913 const std::size_t p = a.count("p");
1914 const std::size_t k = a.count("k");
1915 a.done();
1916 const std::vector<std::vector<int> > M = mam::ph_multisets(p, k);
1917 Json rows = Json::array();
1918 for (const auto& row : M) rows.push_back(encode_ints(row));
1919 Json r;
1920 r["M"] = rows;
1921 return r;
1922 }
1923};
1924
1925struct OpMmdpIsfeasible {
1926 template <class T>
1927 static Json run(Args& a) {
1928 const Matrix<T> Q = a.matrix<T>("Q");
1929 const Matrix<T> R = a.matrix<T>("R");
1930 a.done();
1931 Json r;
1932 r["bool"] = mam::mmdp_isfeasible(Q, R);
1933 return r;
1934 }
1935};
1936
1937// ---------------------------------------------------------------------------
1938// pfqn helpers
1939// ---------------------------------------------------------------------------
1940
1941struct OpInfradiusH {
1942 template <class T>
1943 static Json run(Args& a) {
1944 const std::vector<T> x = a.vector<T>("x");
1945 const Matrix<T> L = a.matrix<T>("L");
1946 const std::vector<T> N = a.vector<T>("N");
1947 const Matrix<T> alpha = a.matrix<T>("alpha");
1948 a.done();
1949 Json r;
1950 r["y"] = encode_scalar(pfqn::infradius_h(x, L, N, alpha));
1951 return r;
1952 }
1953};
1954
1955/**
1956 * pas_placement(H) -> P. MATLAB's second output is the closure placeable(x),
1957 * which has no JSON form; give the remaining per-class counts x to have it
1958 * evaluated, as 0-based class indices.
1959 */
1960struct OpPasPlacement {
1961 template <class T>
1962 static Json run(Args& a) {
1963 const Matrix<T> H = a.matrix<T>("H");
1964 const bool hx = a.has("x");
1965 const std::vector<T> x = hx ? a.vector<T>("x") : std::vector<T>();
1966 a.done();
1967 const pfqn::PasPlacement<T> s = pfqn::pas_placement(H);
1968 Json r;
1969 r["P"] = encode_matrix(s.P);
1970 if (hx) r["placeable"] = encode_sizes(s.placeable(x));
1971 return r;
1972 }
1973};
1974
1975// ---------------------------------------------------------------------------
1976// mapqn: MAP queueing networks
1977// ---------------------------------------------------------------------------
1978
1979struct OpMapqnAmva {
1980 template <class T>
1981 static Json run(Args& a) {
1982 const std::vector<T> mu = a.vector<T>("mu");
1983 const std::vector<Matrix<T> > D0s = matrices_from_json<T>(a.get("D0s"), "mapqn_amva: D0s");
1984 const std::vector<Matrix<T> > D1s = matrices_from_json<T>(a.get("D1s"), "mapqn_amva: D1s");
1985 const std::vector<int> N = a.ints("N");
1986 a.done();
1987 const mapqn::MapqnAmvaResult<T> s = mapqn::mapqn_amva(mu, D0s, D1s, N);
1988 Json r;
1989 r["X"] = encode_vector(s.X);
1990 r["Qq"] = encode_vector(s.Qq);
1991 r["U"] = encode_vector(s.U);
1992 r["pik"] = encode_vector(s.pi);
1993 r["ES"] = encode_vector(s.ES);
1994 return r;
1995 }
1996};
1997
1998/** sense is 'max' or 'min'; absent, the port's own default overload is called. */
1999mapqn::MapqnSense mapqn_sense(const std::string& s, const char* fn) {
2000 if (s == "max" || s == "Max" || s == "MAX") return mapqn::MapqnSense::Max;
2001 if (s == "min" || s == "Min" || s == "MIN") return mapqn::MapqnSense::Min;
2002 throw InputError(std::string(fn) + ": sense must be 'max' or 'min', got '" + s + "'");
2003}
2004
2005/** The MATLAB params fields every mapqn bound shares: M, N, K, mu, v, r. */
2006template <class T, class P>
2007void read_mapqn_core(Args& p, P& out, const char* fn) {
2008 out.M = p.required_integer("M");
2009 out.N = p.required_integer("N");
2010 out.K = p.ints("K");
2011 out.mu = matrices_from_json<T>(p.get("mu"), where_of(fn, "params.mu"));
2012 out.v = matrices_from_json<T>(p.get("v"), where_of(fn, "params.v"));
2013 out.r = p.matrix<T>("r");
2014 p.unsupported("verbose", "the port prints nothing, so there is no verbosity to set.");
2015 p.unsupported("lpAlgorithm", "the port solves the LP with its own simplex and offers no "
2016 "algorithm choice.");
2017}
2018
2019const Json& params_object(Args& a, const char* fn) {
2020 const Json& j = a.get("params");
2021 if (!j.is_object()) throw InputError(std::string(fn) + ": params must be an object");
2022 return j;
2023}
2024
2025template <class R>
2026Json mapqn_lp_status(const R& s) {
2027 Json r;
2028 r["objective"] = encode_scalar(s.objective);
2029 r["ok"] = s.ok;
2030 r["status"] = s.status;
2031 return r;
2032}
2033
2034/**
2035 * mapqn_bnd_lr and mapqn_bnd_qr share the signature
2036 * (params, objective_queue, objective_phase[, sense]) and the result
2037 * {objective, U, IT, Q}; queues and phases are 0-based. The port has no
2038 * default for objective_queue and objective_phase, so both are required.
2039 */
2040#define LINE_MAPQN_UIQ_OP(OPNAME, FN, RESULT) \
2041 struct OPNAME { \
2042 template <class T> \
2043 static Json run(Args& a) { \
2044 Args p(params_object(a, #FN), #FN " params"); \
2045 mapqn::MapqnParams<T> prm; \
2046 read_mapqn_core<T>(p, prm, #FN); \
2047 p.done(); \
2048 const int q = a.required_integer("objective_queue"); \
2049 const int ph = a.required_integer("objective_phase"); \
2050 const bool hs = a.has("sense"); \
2051 const std::string sense = a.text("sense", ""); \
2052 a.done(); \
2053 const mapqn::RESULT<T> s = hs ? mapqn::FN(prm, q, ph, mapqn_sense(sense, #FN))\
2054 : mapqn::FN(prm, q, ph); \
2055 Json res = mapqn_lp_status(s); \
2056 res["U"] = encode_matrix(s.U); \
2057 res["IT"] = encode_matrix(s.IT); \
2058 res["Q"] = encode_matrix(s.Q); \
2059 Json r; \
2060 r["result"] = res; \
2061 r["x"] = encode_vector(s.x); \
2062 r["fval"] = encode_scalar(s.objective); \
2063 return r; \
2064 } \
2065 };
2066
2067LINE_MAPQN_UIQ_OP(OpMapqnBndLr, mapqn_bnd_lr, MapqnBndLrResult)
2068LINE_MAPQN_UIQ_OP(OpMapqnBndQr, mapqn_bnd_qr, MapqnBndQrResult)
2069#undef LINE_MAPQN_UIQ_OP
2070
2071/**
2072 * mapqn_bnd_qr_ld and mapqn_bnd_qr_delay: (params, objective_queue,
2073 * objective_phase, objective_n[, sense]) with the load-dependent scalings
2074 * params.alpha (M x N); the delay variant also reads params.Z and params.D1.
2075 */
2076template <class T>
2077Json mapqn_qr_ld_like(Args& a, const char* fn, bool delay) {
2078 Args p(params_object(a, fn), std::string(fn) + " params");
2079 mapqn::MapqnParams<T> prm;
2080 read_mapqn_core<T>(p, prm, fn);
2081 prm.alpha = p.matrix<T>("alpha");
2082 if (delay) {
2083 prm.Z = p.number<T>("Z");
2084 prm.D1 = p.number<T>("D1");
2085 }
2086 p.done();
2087 const int q = a.required_integer("objective_queue");
2088 const int ph = a.required_integer("objective_phase");
2089 const int n = a.required_integer("objective_n");
2090 const bool hs = a.has("sense");
2091 const std::string sense = a.text("sense", "");
2092 a.done();
2093 mapqn::MapqnQrResult<T> s;
2094 if (delay)
2095 s = hs ? mapqn::mapqn_bnd_qr_delay(prm, q, ph, n, mapqn_sense(sense, fn))
2096 : mapqn::mapqn_bnd_qr_delay(prm, q, ph, n);
2097 else
2098 s = hs ? mapqn::mapqn_bnd_qr_ld(prm, q, ph, n, mapqn_sense(sense, fn))
2099 : mapqn::mapqn_bnd_qr_ld(prm, q, ph, n);
2100 Json res = mapqn_lp_status(s);
2101 res["p2marginals"] = encode_matrices(s.p2marginals);
2102 Json r;
2103 r["result"] = res;
2104 r["x"] = encode_vector(s.x);
2105 r["fval"] = encode_scalar(s.objective);
2106 return r;
2107}
2108
2109struct OpMapqnBndQrLd {
2110 template <class T>
2111 static Json run(Args& a) { return mapqn_qr_ld_like<T>(a, "mapqn_bnd_qr_ld", false); }
2112};
2113
2114struct OpMapqnBndQrDelay {
2115 template <class T>
2116 static Json run(Args& a) { return mapqn_qr_ld_like<T>(a, "mapqn_bnd_qr_delay", true); }
2117};
2118
2119/**
2120 * mapqn_bnd_lr_mva(params, objective_queue, objective_level[, sense[, objective_var]]):
2121 * params {M, N, K, muM, muMAP, v, r}; objective_level -1 is the sum over levels;
2122 * objective_var 'UN' or 'QN'.
2123 */
2124struct OpMapqnBndLrMva {
2125 template <class T>
2126 static Json run(Args& a) {
2127 const char* fn = "mapqn_bnd_lr_mva";
2128 Args p(params_object(a, fn), "mapqn_bnd_lr_mva params");
2129 mapqn::LrMvaParams<T> prm;
2130 prm.M = p.required_integer("M");
2131 prm.N = p.required_integer("N");
2132 prm.K = p.required_integer("K");
2133 prm.muM = p.vector<T>("muM");
2134 prm.muMAP = p.matrix<T>("muMAP");
2135 prm.v = p.matrix<T>("v");
2136 prm.r = p.matrix<T>("r");
2137 p.unsupported("verbose", "the port prints nothing, so there is no verbosity to set.");
2138 p.unsupported("lpAlgorithm", "the port solves the LP with its own simplex and offers no "
2139 "algorithm choice.");
2140 p.done();
2141 const int q = a.required_integer("objective_queue");
2142 const int lev = a.required_integer("objective_level");
2143 const bool hs = a.has("sense"), hv = a.has("objective_var");
2144 if (hv && !hs)
2145 throw InputError("mapqn_bnd_lr_mva: objective_var follows sense in the port's "
2146 "signature; give sense too");
2147 const std::string sense = a.text("sense", "");
2148 const std::string var = a.text("objective_var", "");
2149 a.done();
2150 mapqn::MapqnBndLrMvaResult<T> s;
2151 if (hv) {
2153 if (var == "UN") ov = mapqn::MapqnObjectiveVar::UN;
2154 else if (var == "QN") ov = mapqn::MapqnObjectiveVar::QN;
2155 else throw InputError("mapqn_bnd_lr_mva: objective_var must be 'UN' or 'QN', got '" + var + "'");
2156 s = mapqn::mapqn_bnd_lr_mva(prm, q, lev, mapqn_sense(sense, fn), ov);
2157 } else if (hs) {
2158 s = mapqn::mapqn_bnd_lr_mva(prm, q, lev, mapqn_sense(sense, fn));
2159 } else {
2160 s = mapqn::mapqn_bnd_lr_mva(prm, q, lev);
2161 }
2162 Json res = mapqn_lp_status(s);
2163 res["UN"] = encode_matrix(s.UN);
2164 res["QN"] = encode_matrix(s.QN);
2165 Json r;
2166 r["result"] = res;
2167 return r;
2168 }
2169};
2170
2171std::vector<std::vector<int> > int_rows_from_json(const Json& j, const std::string& where) {
2172 if (!j.is_array()) throw InputError(where + ": expected a list of integer rows");
2173 std::vector<std::vector<int> > out;
2174 for (std::size_t k = 0; k < j.size(); ++k) out.push_back(int_vector_from_json(j[k], at(where, k)));
2175 return out;
2176}
2177
2178/**
2179 * mapqn_qr_bounds_bas(params, objective_queue[, sense]). There is no MATLAB
2180 * function; the keys are the port's QrBasParams fields (those of the JAR and
2181 * native Python parameter objects), all indices 0-based.
2182 */
2183struct OpMapqnQrBoundsBas {
2184 template <class T>
2185 static Json run(Args& a) {
2186 const char* fn = "mapqn_qr_bounds_bas";
2187 Args p(params_object(a, fn), "mapqn_qr_bounds_bas params");
2188 mapqn::QrBasParams<T> prm;
2189 prm.M = p.required_integer("M");
2190 prm.N = p.required_integer("N");
2191 prm.f = p.required_integer("f");
2192 prm.F = p.ints("F");
2193 prm.K = p.ints("K");
2194 prm.mu = matrices_from_json<T>(p.get("mu"), where_of(fn, "params.mu"));
2195 prm.v = matrices_from_json<T>(p.get("v"), where_of(fn, "params.v"));
2196 prm.r = p.matrix<T>("r");
2197 prm.MR = p.required_integer("MR");
2198 prm.BB = int_rows_from_json(p.get("BB"), where_of(fn, "params.BB"));
2199 prm.MM = int_rows_from_json(p.get("MM"), where_of(fn, "params.MM"));
2200 prm.ZZ = p.ints("ZZ");
2201 prm.ZM = p.required_integer("ZM");
2202 prm.MM1 = int_rows_from_json(p.get("MM1"), where_of(fn, "params.MM1"));
2203 p.done();
2204 const int q = a.required_integer("objective_queue");
2205 const bool hs = a.has("sense");
2206 const std::string sense = a.text("sense", "");
2207 a.done();
2208 const mapqn::QrBasResult<T> s = hs ? mapqn::mapqn_qr_bounds_bas(prm, q, mapqn_sense(sense, fn))
2209 : mapqn::mapqn_qr_bounds_bas(prm, q);
2210 Json r = mapqn_lp_status(s);
2211 r["U"] = encode_vector(s.U);
2212 r["occupancy"] = encode_vector(s.occupancy);
2213 r["e"] = encode_matrix(s.e);
2214 r["x"] = encode_vector(s.x);
2215 return r;
2216 }
2217};
2218
2219/** mapqn_qr_bounds_rsrd(params, objective_queue[, sense]); params as QrRsrdParams, alpha optional. */
2220struct OpMapqnQrBoundsRsrd {
2221 template <class T>
2222 static Json run(Args& a) {
2223 const char* fn = "mapqn_qr_bounds_rsrd";
2224 Args p(params_object(a, fn), "mapqn_qr_bounds_rsrd params");
2225 mapqn::QrRsrdParams<T> prm;
2226 prm.M = p.required_integer("M");
2227 prm.N = p.required_integer("N");
2228 prm.F = p.ints("F");
2229 prm.K = p.ints("K");
2230 prm.mu = matrices_from_json<T>(p.get("mu"), where_of(fn, "params.mu"));
2231 prm.v = matrices_from_json<T>(p.get("v"), where_of(fn, "params.v"));
2232 prm.r = p.matrix<T>("r");
2233 if (p.has("alpha")) prm.alpha = vectors_from_json<T>(p.get("alpha"), where_of(fn, "params.alpha"));
2234 p.done();
2235 const int q = a.required_integer("objective_queue");
2236 const bool hs = a.has("sense");
2237 const std::string sense = a.text("sense", "");
2238 a.done();
2239 const mapqn::QrRsrdResult<T> s = hs ? mapqn::mapqn_qr_bounds_rsrd(prm, q, mapqn_sense(sense, fn))
2240 : mapqn::mapqn_qr_bounds_rsrd(prm, q);
2241 Json r = mapqn_lp_status(s);
2242 r["U"] = encode_vector(s.U);
2243 r["Ueff"] = encode_vector(s.Ueff);
2244 r["pb"] = encode_vector(s.pb);
2245 r["x"] = encode_vector(s.x);
2246 return r;
2247 }
2248};
2249
2250// ---------------------------------------------------------------------------
2251// retrieval: caches with a retrieval (fetching) system
2252// ---------------------------------------------------------------------------
2253
2254/**
2255 * retrieval_metrics and retrieval_mva share (m, lambda, eta, gamma) and the
2256 * [pmiss, phit, pdh] return list.
2257 */
2258#define LINE_RETRIEVAL_EXACT_OP(OPNAME, FN) \
2259 struct OPNAME { \
2260 template <class T> \
2261 static Json run(Args& a) { \
2262 const std::vector<int> m = a.ints("m"); \
2263 const std::vector<T> lambda = a.vector<T>("lambda"); \
2264 const Matrix<T> eta = a.matrix<T>("eta"); \
2265 const Matrix<T> gamma = a.matrix<T>("gamma"); \
2266 a.done(); \
2267 const auto s = retrieval::FN(m, lambda, eta, gamma); \
2268 Json r; \
2269 r["pmiss"] = encode_vector(s.pmiss); \
2270 r["phit"] = encode_matrix(s.phit); \
2271 r["pdh"] = encode_matrix(s.pdh); \
2272 return r; \
2273 } \
2274 };
2275
2276LINE_RETRIEVAL_EXACT_OP(OpRetrievalMetrics, retrieval_metrics)
2277LINE_RETRIEVAL_EXACT_OP(OpRetrievalMva, retrieval_mva)
2278#undef LINE_RETRIEVAL_EXACT_OP
2279
2280/** The trailing (max_iter, tol) of the MATLAB FPI functions; each key sets only its own field. */
2281retrieval::FpiOptions read_fpi_options(Args& a) {
2282 retrieval::FpiOptions o;
2283 if (a.has("max_iter")) o.max_iter = a.count("max_iter");
2284 if (a.has("tol")) o.tol = a.number<double>("tol");
2285 return o;
2286}
2287
2288struct OpRetrievalFpi {
2289 template <class T>
2290 static Json run(Args& a) {
2291 const std::vector<int> m = a.ints("m");
2292 const std::vector<T> lambda = a.vector<T>("lambda");
2293 const Matrix<T> eta = a.matrix<T>("eta");
2294 const Matrix<T> gamma = a.matrix<T>("gamma");
2295 const retrieval::FpiOptions o = read_fpi_options(a);
2296 a.done();
2297 const retrieval::RetrievalFpiResult<T> s = retrieval::retrieval_fpi(m, lambda, eta, gamma, o);
2298 Json r;
2299 r["pmiss"] = encode_vector(s.pmiss);
2300 r["phit"] = encode_matrix(s.phit);
2301 r["pdh"] = encode_matrix(s.pdh);
2302 return r;
2303 }
2304};
2305
2306retrieval::RetrievalStationType retrieval_station_type(const Json& j, const std::string& where) {
2307 if (!j.is_string()) throw InputError(where + ": a station type is a string");
2308 const std::string s = j.get<std::string>();
2309 if (s == "IS") return retrieval::RetrievalStationType::IS;
2310 if (s == "PS") return retrieval::RetrievalStationType::PS;
2311 if (s == "SIRO") return retrieval::RetrievalStationType::SIRO;
2312 if (s == "FCFS") return retrieval::RetrievalStationType::FCFS;
2313 if (s == "LCFSPR") return retrieval::RetrievalStationType::LCFSPR;
2314 throw InputError(where + ": station type must be IS, PS, SIRO, FCFS or LCFSPR, got '" + s + "'");
2315}
2316
2317/**
2318 * retrieval_fpi_latency(m, lambda, gamma, alpha, T, R, station_type).
2319 * alpha{s} is the (n x f) matrix of per-item entry vectors of station s, T{s}
2320 * the list of its n per-item subgenerators, and R the list of n routing
2321 * matrices, (S+1) x (S+1) with index 0 = outside (MATLAB's third dimension
2322 * becomes the list index). max_iter and tol are the port's FpiOptions; MATLAB
2323 * fixes them inside the call.
2324 */
2325struct OpRetrievalFpiLatency {
2326 template <class T>
2327 static Json run(Args& a) {
2328 const char* fn = "retrieval_fpi_latency";
2329 const std::vector<int> m = a.ints("m");
2330 const std::vector<T> lambda = a.vector<T>("lambda");
2331 const Matrix<T> gamma = a.matrix<T>("gamma");
2332 const std::vector<Matrix<T> > alpha = matrices_from_json<T>(a.get("alpha"), where_of(fn, "alpha"));
2333 const std::vector<std::vector<Matrix<T> > > Tsub =
2334 matrix_lists_from_json<T>(a.get("T"), where_of(fn, "T"));
2335 const std::vector<Matrix<T> > R = matrices_from_json<T>(a.get("R"), where_of(fn, "R"));
2336 const Json& ty = a.get("station_type");
2337 const retrieval::FpiOptions o = read_fpi_options(a);
2338 a.done();
2339 const std::size_t S = alpha.size();
2340 if (Tsub.size() != S)
2341 throw InputError("retrieval_fpi_latency: alpha and T disagree on the station count");
2342 std::vector<retrieval::RetrievalStationType> types;
2343 if (ty.is_string()) {
2344 types.push_back(retrieval_station_type(ty, where_of(fn, "station_type")));
2345 } else if (ty.is_array()) {
2346 for (std::size_t s = 0; s < ty.size(); ++s)
2347 types.push_back(retrieval_station_type(ty[s], at(where_of(fn, "station_type"), s)));
2348 } else {
2349 throw InputError("retrieval_fpi_latency: station_type is a list of strings");
2350 }
2351 if (types.size() != S)
2352 throw InputError("retrieval_fpi_latency: station_type and alpha disagree on the station count");
2353 std::vector<retrieval::RetrievalStationPH<T> > st(S);
2354 for (std::size_t s = 0; s < S; ++s) {
2355 st[s].alpha = alpha[s];
2356 st[s].sub = Tsub[s];
2357 st[s].type = types[s];
2358 }
2359 const retrieval::RetrievalFpiLatencyResult<T> res =
2360 retrieval::retrieval_fpi_latency(m, lambda, gamma, st, R, o);
2361 Json r;
2362 r["Z"] = encode_scalar(res.Z);
2363 r["d"] = encode_vector(res.d);
2364 r["phi"] = encode_vector(res.phi);
2365 r["pi0"] = encode_vector(res.pi0);
2366 return r;
2367 }
2368};
2369
2370struct OpRetrievalNc {
2371 template <class T>
2372 static Json run(Args& a) {
2373 const std::vector<int> v = a.ints("v");
2374 const std::vector<int> m = a.ints("m");
2375 const std::vector<T> lambda = a.vector<T>("lambda");
2376 const Matrix<T> eta = a.matrix<T>("eta");
2377 const Matrix<T> gamma = a.matrix<T>("gamma");
2378 a.done();
2379 Json r;
2380 r["E"] = encode_scalar(retrieval::retrieval_nc(v, m, lambda, eta, gamma));
2381 return r;
2382 }
2383};
2384
2385/** retrieval_rayint(gamma, m): the matrix form. n and nquad belong to the gfun form, whose gamma is a callable. */
2386struct OpRetrievalRayint {
2387 template <class T>
2388 static Json run(Args& a) {
2389 const Matrix<T> gamma = a.matrix<T>("gamma");
2390 const std::vector<int> m = a.ints("m");
2391 a.unsupported("n", "it selects the form whose gamma is a function handle gfun(t), which "
2392 "has no JSON form; give gamma as an (n x h) matrix instead.");
2393 a.unsupported("nquad", "it is the quadrature size of the gfun form, whose gamma is a "
2394 "function handle with no JSON form.");
2395 a.done();
2396 const retrieval::RetrievalRayintResult<T> s = retrieval::retrieval_rayint(gamma, m);
2397 Json out;
2398 out["xi"] = encode_vector(s.xi);
2399 out["logdetSigma"] = encode_scalar(s.logdet_sigma);
2400 out["iter"] = encode_count(s.iterations);
2401 out["relerrEst"] = s.relerr_est;
2402 out["method"] = s.method;
2403 Json r;
2404 r["E"] = encode_scalar(s.e);
2405 r["logE"] = encode_scalar(s.log_e);
2406 r["out"] = out;
2407 return r;
2408 }
2409};
2410
2411// ---------------------------------------------------------------------------
2412// sum: the summation method
2413// ---------------------------------------------------------------------------
2414
2415/** mi(i): a server count, or "Inf" for a delay station. */
2416std::vector<sum::Servers> servers_from_json(const Json& j, const std::string& where) {
2417 if (!j.is_array()) throw InputError(where + ": expected a list of server counts");
2418 std::vector<sum::Servers> out;
2419 for (std::size_t k = 0; k < j.size(); ++k) {
2420 const Json& e = j[k];
2421 if (e.is_string()) {
2422 const std::string s = e.get<std::string>();
2423 if (s == "Inf" || s == "inf") {
2424 out.push_back(sum::Servers::inf());
2425 continue;
2426 }
2427 }
2428 const long m = static_cast<long>(size_from_json(e, at(where, k)));
2429 if (m < 1) throw InputError(at(where, k) + ": a station needs at least one server");
2430 out.push_back(sum::Servers::of(m));
2431 }
2432 return out;
2433}
2434
2435/** N(r) as a long; "Inf" is accepted only where allowInf (an open class in sum_closing). */
2436std::vector<long> populations_from_json(const Json& j, const std::string& where,
2437 const std::vector<bool>* allowInf) {
2438 if (!j.is_array()) throw InputError(where + ": expected a list of populations");
2439 std::vector<long> out;
2440 for (std::size_t k = 0; k < j.size(); ++k) {
2441 const Json& e = j[k];
2442 if (e.is_string() && (e.get<std::string>() == "Inf" || e.get<std::string>() == "inf")) {
2443 if (!allowInf || k >= allowInf->size() || !(*allowInf)[k])
2444 throw InputError(at(where, k) + ": Inf marks an open class (lambda0 > 0) only");
2445 out.push_back(0); // the port ignores the N entry of an open class
2446 continue;
2447 }
2448 out.push_back(static_cast<long>(size_from_json(e, at(where, k))));
2449 }
2450 return out;
2451}
2452
2453sum::SumOptions read_sum_options(Args& a) {
2454 sum::SumOptions o;
2455 if (a.has("tol")) o.tol = a.number<double>("tol");
2456 if (a.has("maxiter")) o.maxiter = a.count("maxiter");
2457 return o;
2458}
2459
2460/**
2461 * sum_closed(L, N, Z, mi, scv, tol, maxiter). MATLAB defaults Z, mi and scv;
2462 * the port takes all three as required parameters, so they are required here.
2463 */
2464struct OpSumClosed {
2465 template <class T>
2466 static Json run(Args& a) {
2467 const Matrix<T> L = a.matrix<T>("L");
2468 const std::vector<long> N = populations_from_json(a.get("N"), "sum_closed: N", nullptr);
2469 const std::vector<T> Z = a.vector<T>("Z");
2470 const std::vector<sum::Servers> mi = servers_from_json(a.get("mi"), "sum_closed: mi");
2471 const Matrix<T> scv = a.matrix<T>("scv");
2472 const sum::SumOptions o = read_sum_options(a);
2473 a.done();
2474 const sum::SumClosedResult<T> s = sum::sum_closed(L, N, Z, mi, scv, o);
2475 Json r;
2476 r["XN"] = encode_vector(s.XN);
2477 r["QN"] = encode_matrix(s.QN);
2478 r["UN"] = encode_matrix(s.UN);
2479 r["RN"] = encode_matrix(s.RN);
2480 r["it"] = encode_count(s.it);
2481 return r;
2482 }
2483};
2484
2485/**
2486 * sum_closing(lambda0, scva, L, mi, scv, N, Z, Kclosed, tol, maxiter). As for
2487 * sum_closed, every vector the port takes is required. N(r) may be "Inf" only
2488 * for an open class, whose entry the port ignores.
2489 */
2490struct OpSumClosing {
2491 template <class T>
2492 static Json run(Args& a) {
2493 const std::vector<T> lambda0 = a.vector<T>("lambda0");
2494 const std::vector<T> scva = a.vector<T>("scva");
2495 const Matrix<T> L = a.matrix<T>("L");
2496 const std::vector<sum::Servers> mi = servers_from_json(a.get("mi"), "sum_closing: mi");
2497 const Matrix<T> scv = a.matrix<T>("scv");
2498 std::vector<bool> open(lambda0.size());
2499 for (std::size_t r = 0; r < lambda0.size(); ++r)
2500 open[r] = num_traits<T>::to_double(lambda0[r]) > 0.0;
2501 const std::vector<long> N = populations_from_json(a.get("N"), "sum_closing: N", &open);
2502 const std::vector<T> Z = a.vector<T>("Z");
2503 sum::ClosingOptions o;
2504 if (a.has("Kclosed")) o.Kclosed = static_cast<long>(a.count("Kclosed"));
2505 o.sum = read_sum_options(a);
2506 a.done();
2507 const sum::SumClosingResult<T> s = sum::sum_closing(lambda0, scva, L, mi, scv, N, Z, o);
2508 Json r;
2509 r["XN"] = encode_vector(s.XN);
2510 r["QN"] = encode_matrix(s.QN);
2511 r["UN"] = encode_matrix(s.UN);
2512 r["RN"] = encode_matrix(s.RN);
2513 r["TN"] = encode_vector(s.TN);
2514 r["it"] = encode_count(s.it);
2515 return r;
2516 }
2517};
2518
2519// ---------------------------------------------------------------------------
2520// wf: workflow pattern detectors (JAR reference; no MATLAB counterpart)
2521// ---------------------------------------------------------------------------
2522//
2523// Each detector takes the (nedges x 3) linkMatrix [from, to, probability] and
2524// the node-id lists the JAR method takes (serviceNodes, joinNodes, routerNodes,
2525// forkNodes; node ids as they appear in the link matrix), and returns the
2526// detected patterns together with the statistics map the JAR's get*Stats
2527// method computes over them, keyed as the JAR keys it.
2528
2529struct OpWfBranchDetector {
2530 template <class T>
2531 static Json run(Args& a) {
2532 const Matrix<T> link = a.matrix<T>("linkMatrix");
2533 const std::vector<int> serviceNodes = a.ints("serviceNodes");
2534 const std::vector<int> joinNodes = a.ints("joinNodes");
2535 a.done();
2536 const std::vector<wf::BranchPattern<T> > p = wf::detect_branches(link, serviceNodes, joinNodes);
2537 Json pats = Json::array();
2538 for (std::size_t k = 0; k < p.size(); ++k) {
2539 Json e;
2540 e["branchNodes"] = encode_ints(p[k].branchNodes);
2541 e["probabilities"] = encode_vector(p[k].probabilities);
2542 e["forkNode"] = p[k].forkNode;
2543 e["joinNode"] = p[k].hasJoinNode ? Json(p[k].joinNode) : Json(nullptr);
2544 const wf::BranchDiversity<T> d = wf::calculate_branch_diversity(p[k]);
2545 Json dj;
2546 dj["entropy"] = encode_scalar(d.entropy);
2547 dj["normalizedEntropy"] = encode_scalar(d.normalizedEntropy);
2548 dj["gini"] = encode_scalar(d.gini);
2549 dj["balance"] = encode_scalar(d.balance);
2550 e["diversity"] = dj;
2551 pats.push_back(e);
2552 }
2553 const wf::BranchStats<T> s = wf::get_branch_stats(p);
2554 Json st;
2555 st["numPatterns"] = encode_count(s.numPatterns);
2556 st["totalBranchNodes"] = encode_count(s.totalBranchNodes);
2557 st["avgBranches"] = encode_scalar(s.avgBranches);
2558 st["maxBranches"] = encode_count(s.maxBranches);
2559 st["minBranches"] = encode_count(s.minBranches);
2560 st["avgEntropy"] = encode_scalar(s.avgEntropy);
2561 st["avgBalance"] = encode_scalar(s.avgBalance);
2562 Json r;
2563 r["patterns"] = pats;
2564 r["stats"] = st;
2565 return r;
2566 }
2567};
2568
2569/** routerNodes is required; joinNodes absent calls the JAR's three-argument detectLoops. */
2570struct OpWfLoopDetector {
2571 template <class T>
2572 static Json run(Args& a) {
2573 const Matrix<T> link = a.matrix<T>("linkMatrix");
2574 const std::vector<int> serviceNodes = a.ints("serviceNodes");
2575 const std::vector<int> routerNodes = a.ints("routerNodes");
2576 const bool hj = a.has("joinNodes");
2577 const std::vector<int> joinNodes = a.ints_or_empty("joinNodes");
2578 a.done();
2579 const std::vector<int> loops = hj ? wf::detect_loops(link, serviceNodes, routerNodes, joinNodes)
2580 : wf::detect_loops(link, serviceNodes, routerNodes);
2581 Json lj = Json::array();
2582 for (std::size_t k = 0; k < loops.size(); ++k) {
2583 Json e;
2584 e["node"] = loops[k];
2585 const T p = wf::get_loop_probability(loops[k], link, routerNodes);
2586 e["probability"] = encode_scalar(p);
2587 const wf::ExpectedIterations<T> it = wf::get_expected_loop_iterations(p);
2588 e["expectedIterations"] = it.infinite ? Json("Inf") : encode_scalar(it.value);
2589 lj.push_back(e);
2590 }
2591 const wf::LoopStats<T> s = wf::get_loop_stats(loops, link, routerNodes);
2592 Json st;
2593 st["numLoops"] = encode_count(s.numLoops);
2594 st["avgLoopProbability"] = encode_scalar(s.avgLoopProbability);
2595 st["maxLoopProbability"] = encode_scalar(s.maxLoopProbability);
2596 st["minLoopProbability"] = encode_scalar(s.minLoopProbability);
2597 st["avgExpectedIterations"] = encode_scalar(s.avgExpectedIterations);
2598 st["maxExpectedIterations"] = encode_scalar(s.maxExpectedIterations);
2599 Json r;
2600 r["loops"] = lj;
2601 r["stats"] = st;
2602 return r;
2603 }
2604};
2605
2606Json encode_int_rows(const std::vector<std::vector<int> >& v) {
2607 Json j = Json::array();
2608 for (std::size_t k = 0; k < v.size(); ++k) j.push_back(encode_ints(v[k]));
2609 return j;
2610}
2611
2612struct OpWfParallelDetector {
2613 template <class T>
2614 static Json run(Args& a) {
2615 const Matrix<T> link = a.matrix<T>("linkMatrix");
2616 const std::vector<int> serviceNodes = a.ints("serviceNodes");
2617 const std::vector<int> forkNodes = a.ints("forkNodes");
2618 const std::vector<int> joinNodes = a.ints("joinNodes");
2619 a.done();
2620 const std::vector<std::vector<int> > p = wf::detect_parallel(link, serviceNodes, forkNodes, joinNodes);
2621 const wf::ParallelStats<T> s = wf::get_parallel_stats<T>(p);
2622 Json st;
2623 st["numPatterns"] = encode_count(s.numPatterns);
2624 st["totalParallelNodes"] = encode_count(s.totalParallelNodes);
2625 st["avgParallelism"] = encode_scalar(s.avgParallelism);
2626 st["maxParallelism"] = encode_count(s.maxParallelism);
2627 Json r;
2628 r["patterns"] = encode_int_rows(p);
2629 r["stats"] = st;
2630 return r;
2631 }
2632};
2633
2634struct OpWfSequenceDetector {
2635 template <class T>
2636 static Json run(Args& a) {
2637 const Matrix<T> link = a.matrix<T>("linkMatrix");
2638 const std::vector<int> serviceNodes = a.ints("serviceNodes");
2639 a.done();
2640 const std::vector<std::vector<int> > p = wf::detect_sequences(link, serviceNodes);
2641 const wf::SequenceStats<T> s = wf::get_sequence_stats<T>(p);
2642 Json st;
2643 st["numSequences"] = encode_count(s.numSequences);
2644 st["totalNodes"] = encode_count(s.totalNodes);
2645 st["avgLength"] = encode_scalar(s.avgLength);
2646 st["maxLength"] = encode_count(s.maxLength);
2647 st["minLength"] = encode_count(s.minLength);
2648 Json r;
2649 r["sequences"] = encode_int_rows(p);
2650 r["stats"] = st;
2651 return r;
2652 }
2653};
2654
2655// ---------------------------------------------------------------------------
2656// mdd / spn: decision-diagram product forms and stochastic Petri nets
2657// ---------------------------------------------------------------------------
2658
2659/** An MDD.toStruct: {K, domain, root, nnodes, node}, node{k}{p}{v} the child of arc v of node id p+1. */
2660mdd::MddStruct mdd_struct_from_json(const Json& j, const std::string& where) {
2661 if (!j.is_object()) throw InputError(where + ": expected the MDD.toStruct object {K, domain, root, nnodes, node}");
2662 Args s(j, where);
2663 mdd::MddStruct m;
2664 m.K = s.count("K");
2665 m.domain = s.ints("domain");
2666 m.root = s.required_integer("root");
2667 m.nnodes = s.ints("nnodes");
2668 const Json& node = s.get("node");
2669 s.done();
2670 if (!node.is_array()) throw InputError(where + ".node: expected one list of nodes per level");
2671 for (std::size_t k = 0; k < node.size(); ++k)
2672 m.node.push_back(int_rows_from_json(node[k], at(where + ".node", k)));
2673 if (m.domain.size() != m.K || m.nnodes.size() != m.K || m.node.size() != m.K)
2674 throw InputError(where + ": domain, nnodes and node need one entry per level (K)");
2675 for (std::size_t k = 0; k < m.K; ++k)
2676 if (static_cast<int>(m.node[k].size()) != m.nnodes[k])
2677 throw InputError(at(where + ".node", k) + ": holds a different node count from nnodes");
2678 return m;
2679}
2680
2681Json encode_mdd_struct(const mdd::MddStruct& m) {
2682 Json j;
2683 j["K"] = encode_count(m.K);
2684 j["domain"] = encode_ints(m.domain);
2685 j["root"] = m.root;
2686 j["nnodes"] = encode_ints(m.nnodes);
2687 Json node = Json::array();
2688 for (std::size_t k = 0; k < m.node.size(); ++k) node.push_back(encode_int_rows(m.node[k]));
2689 j["node"] = node;
2690 return j;
2691}
2692
2693/** mdd_rec(mdds, g): g{l}(v+1) is g_l(v). */
2694struct OpMddRec {
2695 template <class T>
2696 static Json run(Args& a) {
2697 const mdd::MddStruct m = mdd_struct_from_json(a.get("mdds"), "mdd_rec: mdds");
2698 const std::vector<std::vector<T> > g = vectors_from_json<T>(a.get("g"), "mdd_rec: g");
2699 a.done();
2700 Json r;
2701 r["G"] = encode_scalar(mdd::mdd_rec(m, g));
2702 return r;
2703 }
2704};
2705
2706/** mdd_closedqn(mu, P, servers, N[, options]); servers(i) may be "Inf" for a delay. */
2707struct OpMddClosedqn {
2708 template <class T>
2709 static Json run(Args& a) {
2710 const std::vector<T> mu = a.vector<T>("mu");
2711 const Matrix<T> P = a.matrix<T>("P");
2712 const std::vector<double> servers = a.vector<double>("servers");
2713 const int N = a.required_integer("N");
2714 if (const Json* o = a.opt("options")) {
2715 Args op(*o, "mdd_closedqn options");
2716 op.unsupported("verbose", "the port prints no storage summary; the same figures are "
2717 "returned in stats.");
2718 op.unsupported("mdd", "it is a live MDD object to reuse, which has no JSON form; the "
2719 "reachable set is rebuilt instead.");
2720 op.done();
2721 }
2722 a.done();
2723 const mdd::MddClosedQnResult<T> s = mdd::mdd_closedqn(mu, P, servers, N);
2724 const mdd::MddStats st = s.mdd.stats();
2725 Json sj;
2726 sj["levels"] = encode_count(st.levels);
2727 sj["nodesPerLevel"] = encode_ints(st.nodes_per_level);
2728 sj["numNodes"] = st.num_nodes;
2729 sj["liveNodes"] = st.num_nodes;
2730 sj["tableNodes"] = st.table_nodes;
2731 sj["numStates"] = st.num_states;
2732 sj["mddInts"] = st.mdd_ints;
2733 sj["explicitInts"] = st.explicit_ints;
2734 sj["compression"] = st.compression();
2735 Json tj;
2736 tj["reach"] = s.time_reach;
2737 tj["gen"] = s.time_gen;
2738 tj["solve"] = s.time_solve;
2739 tj["metrics"] = s.time_metrics;
2740 Json r;
2741 r["mdd"] = encode_mdd_struct(s.mdd.to_struct());
2742 r["Q"] = encode_matrix(s.Q);
2743 r["pi"] = encode_vector(s.pi);
2744 r["states"] = encode_int_rows(s.states);
2745 r["QLen"] = encode_vector(s.QLen);
2746 r["U"] = encode_vector(s.U);
2747 r["X"] = encode_vector(s.X);
2748 r["stats"] = sj;
2749 r["times"] = tj;
2750 return r;
2751 }
2752};
2753
2754std::vector<long long> ll_vector_from_json(const Json& j, const std::string& where) {
2755 if (!j.is_array()) throw InputError(where + ": expected a list of integers");
2756 std::vector<long long> out;
2757 for (std::size_t k = 0; k < j.size(); ++k) {
2758 const Json& e = j[k];
2759 if (!e.is_number_integer() && !e.is_number_unsigned())
2760 throw InputError(at(where, k) + ": expected an integer");
2761 out.push_back(e.get<long long>());
2762 }
2763 return out;
2764}
2765
2766std::vector<std::vector<long long> > ll_rows_from_json(const Json& j, const std::string& where) {
2767 if (!j.is_array()) throw InputError(where + ": expected a list of integer rows");
2768 std::vector<std::vector<long long> > out;
2769 for (std::size_t k = 0; k < j.size(); ++k) out.push_back(ll_vector_from_json(j[k], at(where, k)));
2770 return out;
2771}
2772
2773/**
2774 * spn_conv(S, V, g), or spn_conv(INV, g) with S the struct spn_sinvariants
2775 * returns ({places, S, V, m0}). In the struct form MATLAB binds g to its
2776 * second parameter; by name it is always "g".
2777 */
2778struct OpSpnConv {
2779 template <class T>
2780 static Json run(Args& a) {
2781 const Json& Sj = a.get("S");
2782 const std::vector<std::vector<T> > g = vectors_from_json<T>(a.get("g"), "spn_conv: g");
2783 T G;
2784 if (Sj.is_object()) {
2785 if (a.has("V"))
2786 throw InputError("spn_conv: S is the spn_sinvariants struct, which carries V; do not give V too");
2787 Args s(Sj, "spn_conv S");
2788 spn::SpnInvariants inv;
2789 inv.S = ll_rows_from_json(s.get("S"), "spn_conv: S.S");
2790 inv.V = ll_vector_from_json(s.get("V"), "spn_conv: S.V");
2791 if (const Json* p = s.opt("places")) {
2792 const std::vector<int> pl = int_vector_from_json(*p, "spn_conv: S.places");
2793 inv.places.assign(pl.begin(), pl.end());
2794 }
2795 if (const Json* m0 = s.opt("m0")) inv.m0 = ll_vector_from_json(*m0, "spn_conv: S.m0");
2796 s.done();
2797 a.done();
2798 G = spn::spn_conv(inv, g);
2799 } else {
2800 const std::vector<std::vector<long long> > S = ll_rows_from_json(Sj, "spn_conv: S");
2801 const std::vector<long long> V = ll_vector_from_json(a.get("V"), "spn_conv: V");
2802 a.done();
2803 G = spn::spn_conv(S, V, g);
2804 }
2805 Json r;
2806 r["G"] = encode_scalar(G);
2807 return r;
2808 }
2809};
2810
2811/**
2812 * spn_ispn(pre, post, inh, rate, imm, srv, m0, p0, options): p0 is 1-based as
2813 * in MATLAB. inh, imm and srv may be empty (no inhibitors, all timed, one
2814 * server). options carries extra, maxlevel, maxstates, tol, distmax and
2815 * qbdmethod; rewards are callables and have no JSON form.
2816 */
2817struct OpSpnIspn {
2818 static std::vector<std::vector<double> > rows(const Matrix<double>& m) {
2819 std::vector<std::vector<double> > r(m.rows(), std::vector<double>(m.cols()));
2820 for (std::size_t i = 0; i < m.rows(); ++i)
2821 for (std::size_t j = 0; j < m.cols(); ++j) r[i][j] = m(i, j);
2822 return r;
2823 }
2824 static Json run(Args& a) {
2825 const std::vector<std::vector<double> > pre = rows(a.matrix<double>("pre"));
2826 const std::vector<std::vector<double> > post = rows(a.matrix<double>("post"));
2827 const std::vector<std::vector<double> > inh = rows(a.matrix_or_empty<double>("inh"));
2828 const std::vector<double> rate = a.vector<double>("rate");
2829 const std::vector<double> immd = a.vector_or_empty<double>("imm");
2830 const std::vector<double> srv = a.vector_or_empty<double>("srv");
2831 const std::vector<double> m0 = a.vector<double>("m0");
2832 const std::size_t P0 = a.count("p0");
2833 spn::IspnOptions o;
2834 if (const Json* oj = a.opt("options")) {
2835 Args op(*oj, "spn_ispn options");
2836 if (op.has("extra")) o.extra = static_cast<int>(op.count("extra"));
2837 if (op.has("maxlevel")) o.maxlevel = static_cast<int>(op.count("maxlevel"));
2838 if (op.has("maxstates")) o.maxstates = op.count("maxstates");
2839 if (op.has("tol")) o.tol = op.number<double>("tol");
2840 if (op.has("distmax")) o.distmax = op.number<double>("distmax");
2841 o.qbdmethod = op.text("qbdmethod", o.qbdmethod);
2842 op.unsupported("rewards", "they are functions of the marking, callables with no JSON form.");
2843 op.done();
2844 }
2845 a.done();
2846 if (P0 < 1) throw InputError(ErrorKind{"Argument"}, "spn_ispn: p0 is a 1-based place index");
2847 std::vector<bool> imm(immd.size());
2848 for (std::size_t t = 0; t < immd.size(); ++t) imm[t] = immd[t] != 0;
2849 const spn::IspnResult r = spn::spn_ispn(pre, post, inh, rate, imm, srv, m0, P0 - 1, o);
2850 Json res;
2851 res["K"] = encode_count(static_cast<std::size_t>(r.K));
2852 res["L"] = encode_count(r.L);
2853 res["A0"] = encode_vectors(r.A0);
2854 res["A1"] = encode_vectors(r.A1);
2855 res["A2"] = encode_vectors(r.A2);
2856 res["R"] = encode_vectors(r.R);
2857 res["B"] = encode_vectors(r.B);
2858 res["x"] = encode_vectors(r.x);
2859 Json lm = Json::array();
2860 for (const auto& m : r.levelmarkings) lm.push_back(encode_vectors(m));
2861 res["levelmarkings"] = lm;
2862 res["drift"] = encode_vector(r.drift);
2863 res["spR"] = encode_scalar(r.spR);
2864 res["meanmarking"] = encode_vector(r.meanmarking);
2865 res["varlevel"] = encode_scalar(r.varlevel);
2866 res["leveldist"] = encode_vector(r.leveldist);
2867 res["leveltail"] = encode_scalar(r.leveltail);
2868 res["marginal"] = encode_vectors(r.marginal);
2869 res["tput"] = encode_vector(r.tput);
2870 res["ntangible"] = encode_count(r.ntangible);
2871 res["nvanishing"] = encode_count(r.nvanishing);
2872 Json out;
2873 out["res"] = res;
2874 return out;
2875 }
2876};
2877
2878/** spn_ispn_covered(C) with C = POST - PRE, P x T. */
2879struct OpSpnIspnCovered {
2880 static Json run(Args& a) {
2881 const Matrix<double> C = a.matrix<double>("C");
2882 a.done();
2883 const std::vector<bool> cov = spn::spn_ispn_covered(OpSpnIspn::rows(C));
2884 Json v = Json::array();
2885 for (bool b : cov) v.push_back(b);
2886 Json out;
2887 out["covered"] = v;
2888 return out;
2889 }
2890};
2891
2892/**
2893 * spn_rec_enabled(mdds, g, mde, nplacelevels). mde is one entry of
2894 * INFO.modes from spn_mdd: enab and inhib (an entry may be "Inf") are what the
2895 * recursion reads; trans, mode, fire, D0, D1, pie, nph and srv are carried
2896 * into the port's SpnMode as given. dep is a callable.
2897 */
2898struct OpSpnRecEnabled {
2899 template <class T>
2900 static Json run(Args& a) {
2901 const mdd::MddStruct m = mdd_struct_from_json(a.get("mdds"), "spn_rec_enabled: mdds");
2902 const std::vector<std::vector<T> > g = vectors_from_json<T>(a.get("g"), "spn_rec_enabled: g");
2903 const Json& mj = a.get("mde");
2904 const std::size_t L = a.count("nplacelevels");
2905 a.done();
2906 if (!mj.is_object()) throw InputError("spn_rec_enabled: mde is one entry of INFO.modes, an object");
2907 Args md(mj, "spn_rec_enabled mde");
2908 spn::SpnMode<T> mode;
2909 mode.enab = md.vector<double>("enab");
2910 mode.inhib = md.vector<double>("inhib");
2911 if (md.has("fire")) mode.fire = md.vector<double>("fire");
2912 if (md.has("trans")) mode.trans = md.count("trans");
2913 if (md.has("mode")) mode.mode = md.count("mode");
2914 if (md.has("D0")) mode.D0 = md.matrix<T>("D0");
2915 if (md.has("D1")) mode.D1 = md.matrix<T>("D1");
2916 if (md.has("pie")) mode.pie = md.vector<T>("pie");
2917 if (md.has("nph")) mode.nph = md.count("nph");
2918 if (md.has("srv")) mode.srv = md.number<double>("srv");
2919 md.unsupported("dep", "it is a marking-dependent rate function, a callable with no JSON form.");
2920 md.done();
2921 if (mode.enab.size() < L || mode.inhib.size() < L)
2922 throw InputError("spn_rec_enabled: mde.enab and mde.inhib need one entry per place level");
2923 const spn::SpnEnabling<T> en = spn::spn_rec_enabled(m, g, mode, L);
2924 Json r;
2925 Json ej;
2926 ej["ge"] = encode_vector(en.ge);
2927 ej["eq"] = encode_vector(en.eq);
2928 ej["maxDegree"] = encode_count(en.max_degree);
2929 r["en"] = ej;
2930 return r;
2931 }
2932};
2933
2934
2935} // namespace
2936
2938 t["polling_qsys_1limited"] = make<OpPolling1Limited>();
2939 t["polling_qsys_decrementing"] = make<OpPollingDecrementing>();
2940 t["polling_pcl"] = make<OpPollingPcl>();
2941 t["polling_qsys_exhaustive"] = make<OpPollingExhaustive>();
2942 t["polling_qsys_gated"] = make<OpPollingGated>();
2943 t["polling_qsys_klimited"] = make<OpPollingKLimited>();
2944 t["polling_qsys_table"] = make<OpPollingTable>();
2945 t["ljd_linearize"] = make<OpLjdLinearize>();
2946 t["fes_compute_throughputs"] = make<OpFesComputeThroughputs>();
2947 t["fes_compute_metrics"] = make<OpFesComputeMetrics>();
2948 t["fes_build_isolated"] = make<OpFesBuildIsolated>();
2949 t["fes_map_levels"] = make<OpFesMapLevels>();
2950 t["fes_map_interdeparture"] = make<OpFesMapInterdeparture>();
2951 t["fes_map_interp"] = make<OpFesMapInterp>();
2952 t["fes_map_moments"] = make<OpFesMapMoments>();
2953 t["fes_map_solve"] = make<OpFesMapSolve>();
2954 t["fes_map_aggregate"] = make<NeedsTranscendental<OpFesMapAggregate> >();
2955 t["cache_erec"] = make<OpCacheErec>();
2956 t["cache_prob_erec"] = make<OpCacheProbErec>();
2957 t["cache_cost"] = make<OpCacheCost>();
2958 t["cache_cost_pathcheck"] = make<OpCacheCostPathcheck>();
2959 t["cache_gamma"] = make<OpCacheGamma>();
2960 t["cache_gamma_lp"] = make<OpCacheGammaLp>();
2961 t["cache_miss"] = make<OpCacheMiss>();
2962 t["cache_mva"] = make<OpCacheMva>();
2963 t["cache_mva_miss"] = make<OpCacheMvaMiss>();
2964 t["cache_rrm_meanfield_ode"] = make<OpCacheRrmMeanfieldOde>();
2965 t["cache_spm_size"] = make<NeedsTranscendental<OpCacheSpmSize> >();
2966 t["cache_miss_asy"] = make<NeedsTranscendental<OpCacheMissAsy> >();
2967 t["cache_miss_fpi"] = make<NeedsTranscendental<OpCacheMissFpi> >();
2968 t["cache_miss_spm"] = make<NeedsTranscendental<OpCacheMissSpm> >();
2969 t["cache_miss_rmf"] = make<NeedsTranscendental<OpCacheMissRmf> >();
2970 t["cache_rrm_meanfield"] = make<NeedsTranscendental<OpCacheRrmMeanfield> >();
2971 t["cache_prob_fpi"] = make<NeedsTranscendental<OpCacheProbFpi> >();
2972 t["cache_prob_spm"] = make<NeedsTranscendental<OpCacheProbSpm> >();
2973 t["cache_ttl_hlru"] = make<NeedsTranscendental<OpCacheTtlHlru> >();
2974 t["cache_t_hlru"] = make<NeedsTranscendental<OpCacheTHlru> >();
2975 t["cache_spm"] = make<NeedsTranscendental<OpCacheSpm> >();
2976 t["cache_xi_fp"] = make<NeedsTranscendental<OpCacheXiFp> >();
2977 t["cache_xi_iter"] = make<NeedsTranscendental<OpCacheXiIter> >();
2978 t["cache_is"] = make<NeedsTranscendental<OpCacheIs> >();
2979 t["cache_prob_is"] = make<NeedsTranscendental<OpCacheProbIs> >();
2980 t["cache_miss_is"] = make<NeedsTranscendental<OpCacheMissIs> >();
2981 t["cache_lrum_map_levelstats"] = make<NeedsTranscendental<OpCacheLrumMapLevelstats> >();
2982 t["cache_t_lrum_map"] = make<NeedsTranscendental<OpCacheTLrumMap> >();
2983 t["cache_ttl_lrum_map"] = make<NeedsTranscendental<OpCacheTtlLrumMap> >();
2984 t["cache_ttl_lrua"] = make<NeedsTranscendental<OpCacheTtlLrua> >();
2985 t["da_cache_isolate"] = make<OpDaCacheIsolate>();
2986 t["da_traffic_superpos"] = make<OpDaTrafficSuperpos>();
2987 t["lossn_manjunath"] = make<OpLossnManjunath>();
2988 t["lossn_rec"] = make<OpLossnRec>();
2989 t["lossn_kr"] = make<OpLossnKr>();
2990 t["lossn_kr_dim"] = make<OpLossnKrDim>();
2991 t["dqsys_bernoulli1"] = make<OpDqsysBernoulli1>();
2992 t["map2ph"] = make<OpMap2ph>();
2993 t["map2mmpp"] = make<OpMap2mmpp>();
2994 t["autocov"] = make<OpAutocov>();
2995 t["ph_multisets"] = make<OpPhMultisets>();
2996 t["mmdp_isfeasible"] = make<OpMmdpIsfeasible>();
2997 t["pas_placement"] = make<OpPasPlacement>();
2998 t["lossn_erlangfp"] = make<NeedsTranscendental<OpLossnErlangfp> >();
2999 t["lossn_mci"] = make<NeedsTranscendental<OpLossnMci> >();
3000 t["lossn_kr_asy"] = make<NeedsTranscendental<OpLossnKrAsy> >();
3001 t["lossn_asy"] = make<NeedsTranscendental<OpLossnAsy> >();
3002 t["lossn_links"] = make<NeedsTranscendental<OpLossnLinks> >();
3003 t["lossn_revsens"] = make<NeedsTranscendental<OpLossnRevsens> >();
3004 t["aph_fit"] = make<NeedsTranscendental<OpAphFit> >();
3005 t["m3pp22_fitc_approx_cov"] = make<NeedsTranscendental<OpM3pp22FitcApproxCov> >();
3006 t["infradius_h"] = make<NeedsTranscendental<OpInfradiusH> >();
3007 t["mapqn_amva"] = make<OpMapqnAmva>();
3008 t["mapqn_bnd_lr"] = make<OpMapqnBndLr>();
3009 t["mapqn_bnd_lr_mva"] = make<OpMapqnBndLrMva>();
3010 t["mapqn_bnd_qr"] = make<OpMapqnBndQr>();
3011 t["mapqn_bnd_qr_ld"] = make<OpMapqnBndQrLd>();
3012 t["mapqn_bnd_qr_delay"] = make<OpMapqnBndQrDelay>();
3013 t["mapqn_qr_bounds_bas"] = make<OpMapqnQrBoundsBas>();
3014 t["mapqn_qr_bounds_rsrd"] = make<OpMapqnQrBoundsRsrd>();
3015 t["retrieval_metrics"] = make<OpRetrievalMetrics>();
3016 t["retrieval_mva"] = make<OpRetrievalMva>();
3017 t["retrieval_nc"] = make<OpRetrievalNc>();
3018 t["wf_loop_detector"] = make<OpWfLoopDetector>();
3019 t["wf_parallel_detector"] = make<OpWfParallelDetector>();
3020 t["wf_sequence_detector"] = make<OpWfSequenceDetector>();
3021 t["mdd_rec"] = make<OpMddRec>();
3022 t["mdd_closedqn"] = make<OpMddClosedqn>();
3023 t["spn_conv"] = make<OpSpnConv>();
3024 t["spn_ispn"] = make<DoubleOnly<OpSpnIspn> >();
3025 t["spn_ispn_covered"] = make<DoubleOnly<OpSpnIspnCovered> >();
3026 t["spn_rec_enabled"] = make<OpSpnRecEnabled>();
3027 t["retrieval_fpi"] = make<NeedsTranscendental<OpRetrievalFpi> >();
3028 t["retrieval_fpi_latency"] = make<NeedsTranscendental<OpRetrievalFpiLatency> >();
3029 t["retrieval_rayint"] = make<NeedsTranscendental<OpRetrievalRayint> >();
3030 t["sum_closed"] = make<NeedsTranscendental<OpSumClosed> >();
3031 t["sum_closing"] = make<NeedsTranscendental<OpSumClosing> >();
3032 t["wf_branch_detector"] = make<NeedsTranscendental<OpWfBranchDetector> >();
3033}
3034
3036 const std::string fn_arg =
3037 "it takes a C++ callable as an argument, which has no JSON form.";
3038 t["cd_peak_scaling"] = fn_arg;
3039 t["da_fpi"] = fn_arg;
3040 t["hyperexp_fit_longtail"] = fn_arg;
3041 t["laplace_invert"] = fn_arg;
3042 t["laplaceapprox"] = fn_arg;
3043 t["pas_swap2order"] = fn_arg;
3044 t["mdd_reachset"] = fn_arg;
3045 t["fes_beta_handle"] =
3046 "it returns a C++ callable (the per-class rate function of the aggregate), which has "
3047 "no JSON form.";
3048 const std::string mdd_desc =
3049 "it returns a Kronecker rate descriptor whose successor function is a C++ callable, "
3050 "which has no JSON form; run line-cli -s ctmc -a <analysis> on the model instead.";
3051 t["mdd_descriptor"] = mdd_desc;
3052 t["mdd_ps"] = mdd_desc;
3053 t["mdd_mcd"] =
3054 "it takes a Kronecker rate descriptor whose successor function is a C++ callable, "
3055 "which has no JSON form; run line-cli -s ctmc -a <analysis> on the model instead.";
3056 const std::string sn_mva =
3057 "it takes a NetworkStruct; build the model and run line-cli -s mva -a <analysis> "
3058 "on it instead.";
3059 t["cache_retrieval_inputs"] = sn_mva;
3060 t["fes_validate"] = sn_mva;
3061 t["da_cacheqn"] = sn_mva;
3062 t["da_cacheqn_retrieval"] = sn_mva;
3063 t["spn_lpbnd"] =
3064 "it takes a NetworkStruct; build the model and run line-cli -s ba -a <analysis> "
3065 "on it instead.";
3066 t["spn_mdd"] =
3067 "it takes a NetworkStruct; build the model and run line-cli -s ctmc -a <analysis> "
3068 "on it instead.";
3069 const std::string sn_nc =
3070 "it takes a NetworkStruct; build the model and run line-cli -s nc -a <analysis> "
3071 "on it instead.";
3072 t["spn_pf"] = sn_nc;
3073 t["spn_sinvariants"] = sn_nc;
3074 const std::string sn_props =
3075 "it takes a NetworkStruct and returns the Petri-net property structs; build the "
3076 "model and call it from C++ (line/api/spn/spn_properties.h) instead.";
3077 // the shared net model the property functions build; same carrier, same reason
3078 t["spn_net"] = sn_props;
3079 t["spn_properties"] = sn_props;
3080 t["spn_reachgraph"] = sn_props;
3081 t["spn_reachprops"] = sn_props;
3082 t["spn_siphons"] = sn_props;
3083 t["spn_structprops"] = sn_props;
3084 t["spn_tinvariants"] = sn_props;
3085 t["spn_traps"] = sn_props;
3086 t["spn_metrics"] =
3087 "it takes the SpnInfo that spn_mdd returns, which holds an MDD object and callable "
3088 "marking-dependence laws with no JSON form; build the net and run line-cli -s nc "
3089 "-a <analysis> on it instead.";
3090}
3091
3092} // namespace reg
3093} // namespace line
Minimal-order acyclic phase-type fit of the first three moments (matlab/lib/kpctoolbox/aph/aph_fit....
Internal to the –api dispatcher: what a per-domain translation unit needs to add entries to the table...
#define LINE_POLLING_OP(Name, fn)
#define LINE_CACHE_GM_MATRIX_OP(Name, fn, out,...)
The (gamma, m) -> prob family.
#define LINE_MAPQN_UIQ_OP(OPNAME, FN, RESULT)
mapqn_bnd_lr and mapqn_bnd_qr share the signature (params, objective_queue, objective_phase[,...
#define LINE_RETRIEVAL_EXACT_OP(OPNAME, FN)
retrieval_metrics and retrieval_mva share (m, lambda, eta, gamma) and the [pmiss, phit,...
Sample autocovariance sequence of a trace, lags 0 .
Mean per-list storage cost of a cache with item sizes, and the screen for promotion paths that storag...
Exact recursive normalizing constant of a multi-list cache model.
Access factors of a multi-list cache whose lists form a general access GRAPH.
Access factors of a tree-structured multi-list cache.
Importance-sampling estimate of the cache normalizing constant.
Level statistics of one item's embedded (list, phase) chain in the LRU(m)-MAP TTL approximation.
Exact cache miss rates from the recursive normalizing constant.
Asymptotic (large-cache) miss ratio by a rank-threshold fixed point.
Cache miss rates from the fixed-point multipliers.
Cache miss rates from the importance-sampling hit probabilities.
Refined mean field (RMF) miss rates of a multi-list RANDOM(m) cache.
Saddle-point approximation of the cache miss rates.
Exact mean value analysis of a multi-list cache.
Per-item and global cache miss probabilities by mean value analysis.
Exact per-item hit and miss probabilities of a multi-list cache.
Cache hit and miss probabilities from the fixed-point multipliers.
Importance-sampling estimate of the cache hit-probability distribution.
Saddle-point approximation of the per-item cache hit probabilities.
Steady state of the RANDOM(m) multi-list mean field.
Mean-field drift of the RANDOM(m) multi-list cache.
Grid-independent sojourn-weighted average of a cache mean-field transient.
Saddle-point approximation of the cache normalizing constant.
Ray (WKB) asymptotic expansion of the cost-capped cache normalizing constant.
Characteristic times of the h-LRU / LRU(m) TTL approximation.
Characteristic times of the LRU(m)-MAP TTL approximation.
TTL (characteristic-time) approximation of an h-LRU / LRU(m) cache.
TTL (characteristic-time) approximation of an LRU cache whose lists form an arbitrary access graph.
TTL approximation of an LRU(m) cache whose items are requested by Markovian arrival processes.
Lagrange multipliers of a multi-list cache by fixed-point iteration.
Lagrange multipliers of a multi-list cache by the Gast-Van Houdt iteration.
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
Isolated-cache input construction for the decomposition methods.
Superposition of independent renewal flows (Whitt's QNA stationary-interval method).
State dependent Bernoulli server on a discrete time scale.
Service demands and visit ratios of an isolated subnetwork, from the stochastic complement of its rou...
Per-station metrics of the ISOLATED subnetwork at every population state, the companion of fes_comput...
Per-class throughput table of an isolated subnetwork, tabulated over the population lattice,...
Recursive MAP flow-equivalent server for a station subset.
Inter-departure MAP of a closed subnetwork made of one MAP station and one MAP flow-equivalent server...
Shape-preserving interpolation of the flow-equivalent descriptors, and the population grid they are e...
Per-level processes of a load-dependent flow-equivalent server.
Moments and index of dispersion of an inter-departure MAP.
Closed model left by a MAP flow-equivalent server, and the per-station metrics behind it.
Integrands of the Norlund-Rice inversion of the normalizing constant, in their two changes of variabl...
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Port of ldqbd_mphc.m and ph_multisets.m: the exact level-dependent QBD blocks of an M/PH/c queue.
Linearized index of a per-class population vector, for Limited Joint Dependence (LJD) tables.
Large-capacity (Kelly limit) approximation of loss networks.
Erlang fixed-point (reduced-load) approximation for a loss network.
The multirate single-link loss system (stochastic knapsack): exact blocking by the Kaufman-Roberts re...
Exact analysis of a loss network by the Manjunath-Sikdar transform.
Monte Carlo importance-sampling summation for product-form loss networks.
Exact analysis of a loss network by MDD-rec.
Revenue sensitivities and implied costs of a loss network.
M3PP(2, 2) fitted to the count COVARIANCE between its two classes (matlab/lib/m3a/m3a/m3pp/m3pp22_fit...
Read a MAP as an MMPP (matlab/lib/kpctoolbox/map/map2mmpp.m).
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
MAP constructors and structural transformations.
Horizontal-cut mean value analysis for a MAP server (SolverMVA method 'amva.mapqn').
General linear-reduction (LR) bound on the utilization of one queue-phase of a closed MAP queueing ne...
MVA-shaped linear-reduction bound for a closed network of M - 1 exponential queues and ONE MAP queue.
General quadratic-reduction (QR) bound on the utilization of one queue-phase of a closed MAP queueing...
Quadratic-reduction bound for a MAP queueing network with a delay (think time) station.
Quadratic-reduction bound on a marginal of a load-dependent MAP queueing network.
Quadratic-reduction bound on the utilization of one queue of a closed MAP queueing network with a FIN...
Quadratic-reduction bound on the utilization of one queue of a closed MAP queueing network under RS-R...
Quasi-reduced ordered Multi-valued Decision Diagram.
Exact solve of a single-class closed exponential queueing network whose CTMC state space (reachable o...
MDD-rec: the normalising constant of a product-form model whose reachable set is held in a decision d...
Feasibility predicate for a Markov-modulated deterministic process.
std::vector< CacheBlockedPair > cache_cost_pathcheck(const Matrix< T > &gamma, const std::vector< int > &sigma, const std::vector< int > &k, const std::vector< int > &parent)
Definition cache_cost.h:95
CacheIsResult< T > cache_is(const Matrix< T > &gamma_in, const std::vector< int > &m, std::size_t samples, std::uint64_t seed, const std::vector< int > &sigma_in, const std::vector< int > &k)
Importance-sampling estimate of the cache normalizing constant.
Definition cache_is.h:113
std::vector< T > cache_cost(const Matrix< T > &gamma, const std::vector< int > &m, const std::vector< int > &sigma, const std::vector< int > &k, const Matrix< T > &pij)
Mean per-list storage cost of a cache with item sizes, and the screen for promotion paths that storag...
Definition cache_cost.h:63
std::vector< T > cache_rrm_meanfield_ode(const std::vector< T > &x, const std::vector< T > &lambda, const std::vector< int > &m)
Mean-field drift of the RANDOM(m) multi-list cache.
std::vector< T > cache_xi_iter(const Matrix< T > &gamma, const std::vector< int > &m)
Lagrange multipliers of a multi-list cache by the Gast-Van Houdt iteration.
CacheMissRmfResult< T > cache_miss_rmf(const std::vector< T > &gamma, const std::vector< int > &m_in, const Matrix< T > &lambda, const T &tmax, const std::vector< std::vector< Matrix< T > > > &accost)
Refined mean-field miss rates of a RANDOM(m) multi-list cache.
CacheGammaGraphResult< T > cache_gamma(const std::vector< Matrix< T > > &lambda, const std::vector< std::vector< Matrix< T > > > &R)
Access factors of a multi-list cache whose lists form a general access GRAPH.
CacheGammaResult< T > cache_gamma_lp(const std::vector< Matrix< T > > &lambda, const std::vector< std::vector< Matrix< T > > > &R)
Access factors of a tree-structured multi-list cache.
CacheMissResult< T > cache_miss(const Matrix< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda)
Exact cache miss rates from the recursive normalizing constant.
Definition cache_miss.h:65
CacheMvaResult< T > cache_mva(const Matrix< T > &gamma, const std::vector< int > &m)
Exact mean value analysis of a multi-list cache.
Definition cache_mva.h:66
CacheMissIsResult< T > cache_miss_is(const Matrix< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda, std::size_t samples, std::uint64_t seed, const std::vector< int > &sigma, const std::vector< int > &cap)
Cache miss rates from the importance-sampling hit probabilities.
CacheXiFpResult< T > cache_xi_fp(const Matrix< T > &gamma, const std::vector< int > &m)
Lagrange multipliers of a multi-list cache by fixed-point iteration.
Definition cache_xi_fp.h:65
CacheMissResult< T > cache_miss_fpi(const Matrix< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda)
Cache miss rates from the fixed-point multipliers.
std::vector< T > cache_t_hlru(const Matrix< T > &gamma, const std::vector< int > &m)
Characteristic times of the h-LRU / LRU(m) TTL approximation.
CacheMissRmfResult< T > cache_miss_rmf_transient(const std::vector< int > &m_in, const Matrix< T > &lambda, const T &t0, const T &t1, const std::vector< T > &x0init, const std::vector< std::vector< Matrix< T > > > &accost, const CacheSojournClock< T > &clk=CacheSojournClock< T >())
Transient mean-field trajectory over [t0,t1] from a given initial occupancy, the optional TSPAN/X0INI...
CacheLrumMapLevelStats< T > cache_lrum_map_levelstats(const Matrix< T > &D0, const Matrix< T > &D1, const std::vector< T > &Tv)
Level statistics of one item's embedded (list, phase) chain in the LRU(m)-MAP TTL approximation.
CacheMvaMissResult< T > cache_mva_miss(const std::vector< T > &p, const std::vector< int > &m, const Matrix< T > &R)
Per-item and global cache miss probabilities by mean value analysis.
Matrix< T > cache_prob_erec(const Matrix< T > &gamma, const std::vector< int > &m, const std::vector< int > &sigma, const std::vector< int > &k)
Per-item hit and miss probabilities under per-list storage cost caps, pi_ij = m_j gamma(i,...
CacheCostMode
Whether the caps bound the cost from above (matching cache_erec) or resolve it exactly.
Matrix< T > cache_prob_is(const Matrix< T > &gamma, const std::vector< int > &m, std::size_t samples, std::uint64_t seed, const std::vector< int > &sigma, const std::vector< int > &k)
Importance-sampling estimate of the cache hit-probability distribution.
CacheSpmSizeResult< T > cache_spm_size(const Matrix< T > &gamma, const std::vector< int > &m, const std::vector< int > &sigma, const std::vector< int > &k, CacheCostMode mode=CacheCostMode::AtMost)
Ray (WKB) asymptotic expansion of the cost-capped cache normalizing constant.
CacheSpmResult< T > cache_spm(const Matrix< T > &gamma_in, const std::vector< int > &m)
Saddle-point approximation of the cache normalizing constant.
Definition cache_spm.h:152
T cache_miss_asy(const Matrix< T > &gamma, const std::vector< int > &m, int maxIter, const T &tolerance)
Asymptotic (large-cache) miss ratio by a rank-threshold fixed point.
std::vector< T > cache_t_lrum_map(const std::vector< mam::Map< T > > &items, const std::vector< T > &m, const T &tol, unsigned maxswp=200)
Characteristic times of the LRU(m)-MAP TTL approximation.
CacheTtlLrumMapResult< T > cache_ttl_lrum_map(const std::vector< mam::Map< T > > &items, const std::vector< T > &m, const T &tol)
TTL approximation of an LRU(m) cache whose items are requested by Markovian arrival processes.
T cache_erec(const Matrix< T > &gamma, const std::vector< int > &m)
Exact recursive normalizing constant of a multi-list cache model.
Definition cache_erec.h:201
CacheRrmMeanfieldResult< T > cache_rrm_meanfield(const std::vector< T > &lambda, const std::vector< int > &m, const T &tmax)
Steady state of the RANDOM(m) multi-list mean field.
Matrix< T > cache_ttl_lrua(const Matrix< T > &lambda, const std::vector< Matrix< T > > &R, const std::vector< T > &m, const T &tol, unsigned maxswp=200)
TTL (characteristic-time) approximation of an LRU cache whose lists form an arbitrary access graph.
CacheMissSpmResult< T > cache_miss_spm(const Matrix< T > &gamma, const std::vector< int > &m, const Matrix< T > &lambda)
Saddle-point approximation of the cache miss rates.
T da_traffic_superpos(const std::vector< T > &lambda, const std::vector< T > &a2)
Superposition of independent renewal flows (Whitt's QNA stationary-interval method).
CacheIsolateResult< T > da_cache_isolate(const CacheParam< T > &ch, const std::vector< T > &lambda)
Isolated-cache input construction for the decomposition methods.
Bernoulli1InfiniteResult< T > dqsys_bernoulli1(const T &b, const T &p)
dqsys_bernoulli1(b, p) with the reference's default L = Inf.
std::vector< std::vector< T > > fes_compute_throughputs(const Matrix< T > &L, const std::vector< int > &mi, const std::vector< bool > &isDelay, const std::vector< int > &cutoffs)
Per-class throughput table of an isolated subnetwork, tabulated over the population lattice,...
FesIsolated< T > fes_build_isolated(const Matrix< T > &rates, const Matrix< T > &stochCompS)
Build the isolated subnetwork's demands and visit ratios.
std::vector< mam::Map< T > > fes_map_levels(const mam::Map< T > &map, std::size_t n, double mi=1.0)
Replicate a load independent MAP over n levels, scaling level k by min(k, mi).
FesMapMoments< T > fes_map_moments(const mam::Map< T > &map, const std::string &method="ssolve", double step_safety=0.1, double tol=1e-12, std::size_t iter_max=1000000)
Moments and index of dispersion of an inter-departure MAP.
mam::Map< T > fes_map_interdeparture(const std::vector< mam::Map< T > > &maps, const std::vector< mam::Map< T > > &fes, std::size_t n)
Inter-departure MAP of a closed subnetwork made of one MAP station and one MAP flow-equivalent server...
FesMapSolveResult< T > fes_map_solve(const std::vector< mam::Map< T > > &fes, const mam::Map< T > &think_map, std::size_t n)
Closed model left by a MAP flow-equivalent server, and the per-station metrics behind it.
FesMapAggregateResult< T > fes_map_aggregate(const std::vector< mam::Map< T > > &maps, const std::vector< double > &servers, std::size_t n, const std::vector< std::size_t > &grid, const std::string &method="ssolve")
Recursive MAP flow-equivalent server for a station subset.
std::vector< T > fes_map_interp(const std::vector< T > &x, const std::vector< T > &y, const std::vector< T > &xq)
Monotone piecewise cubic Hermite interpolation of one series.
std::size_t ljd_linearize(const std::vector< int > &nvec, const std::vector< int > &cutoffs)
Linearized index of a per-class population vector, for Limited Joint Dependence (LJD) tables.
FesConditionalMetrics< T > fes_compute_metrics(const Matrix< T > &L, const std::vector< int > &mi, const std::vector< bool > &isDelay, const std::vector< int > &cutoffs)
Per-station metrics of the ISOLATED subnetwork at every population state, the companion of fes_comput...
PollingType
Polling service disciplines, with the values of MATLAB PollingType.
Definition lang_types.h:385
@ KLIMITED
serve at most K per visit (K in pollingPar)
Definition lang_types.h:388
@ EXHAUSTIVE
serve until the queue empties
Definition lang_types.h:387
@ GATED
serve exactly the jobs present at the polling instant
Definition lang_types.h:386
@ GLIMITED
serve at most K of the jobs present at the polling instant
Definition lang_types.h:390
@ DECREMENTING
serve until the queue is one shorter than at arrival
Definition lang_types.h:389
LossnLinksResult< T > lossn_links(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const std::string &method="exact", const LossnMciOptions< T > &mciopt=LossnMciOptions< T >(), std::uint64_t seed=0)
Per-link blocking and occupancy of a loss network.
LossnAsyResult< T > lossn_asy(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C)
Fluid (Kelly) limit of a loss network by convex duality.
Definition lossn_asy.h:72
LossnManjunathResult< T > lossn_manjunath(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const LossnManjunathOptions &options=LossnManjunathOptions())
Exact normalizing constant, carried load and blocking of a loss network.
KrResult< T > lossn_kr(const std::vector< T > &rho, const std::vector< T > &b, const T &C)
Exact occupancy distribution and per-class blocking of a stochastic knapsack.
Definition lossn_kr.h:147
KrDimResult< T > lossn_kr_dim(const std::vector< T > &rho, const std::vector< T > &b, const std::vector< T > &Btarget)
Smallest link capacity meeting per-class blocking targets.
Definition lossn_kr.h:219
ErlangFpResult< T > lossn_erlangfp(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const da::FpiOptions &options=lossn_erlangfp_options())
Erlang fixed-point (reduced-load) approximation for a loss network.
LossnMciResult< T > lossn_mci(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const LossnMciOptions< T > &opt, Rng &gen)
Estimate the normalizing constant and the blocking probabilities.
Definition lossn_mci.h:184
KrAsyResult< T > lossn_kr_asy(const std::vector< T > &rho, const std::vector< T > &b, const T &C)
Normal approximation of multirate single-link blocking for large C.
Definition lossn_kr.h:310
LossnRecResult< T > lossn_rec(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C)
Exact loss-network analysis by MDD-rec.
Definition lossn_rec.h:141
LossnRevsensResult< T > lossn_revsens(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const std::vector< T > &w, const std::string &method="exact", const LossnMciOptions< T > &mciopt=LossnMciOptions< T >(), std::uint64_t seed=0)
Revenue sensitivities and implied costs of a loss network.
std::vector< std::vector< int > > ph_multisets(std::size_t p, std::size_t k)
Configurations of k identical servers over p service phases.
Definition ldqbd_mphc.h:65
Map2mmppResult< T > map2mmpp(const Map< T > &m)
Modulating generator and arrival-rate matrix of a MAP read as an MMPP.
Definition map2mmpp.h:39
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
Definition map_moment.h:101
T map_var(const Map< T > &m)
Variance of the inter-arrival time.
Definition map_moment.h:133
AphFitResult< T > aph_fit(const T &e1, const T &e2, const T &e3, unsigned nmax, const T &tol)
Fit an APH(n) with n <= nmax to the raw moments e1, e2, e3.
Definition aph_fit.h:176
M3pp22FitcCovResult< T > m3pp22_fitc_approx_cov(const T &a, const T &bt1, const T &bt2, const T &binf, const T &m3t2, const T &t1, const T &t2, const std::vector< T > &ai, const T &st3, const T &t3, const AugLagOptions< T > &opt)
Fit the underlying MMPP(2) by optimization, then apply the covariance split.
bool mmdp_isfeasible(const Matrix< T > &Q, const Matrix< T > &R, const T &tol)
True when (Q, R) is a valid MMDP pair up to the given tolerance.
T map_moment(const Map< T > &m, unsigned k)
Raw moment of order k of the inter-arrival time: k!
Definition map_moment.h:118
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
PhType< T > map2ph(const Map< T > &in)
(alpha, T) of the inter-arrival distribution: alpha = pie, T = D0 (map2ph.m).
MapqnQrResult< T > mapqn_bnd_qr_delay(const MapqnParams< T > &p, int objective_queue, int objective_phase, int objective_n, MapqnSense sense=MapqnSense::Max)
Bound P(queue objective_queue holds objective_n jobs in phaseobjective_phase) for a network with a de...
MapqnBndLrMvaResult< T > mapqn_bnd_lr_mva(const LrMvaParams< T > &p, int objective_queue, int objective_level, MapqnSense sense=MapqnSense::Max, MapqnObjectiveVar objective_var=MapqnObjectiveVar::UN)
Bound UN or QN at (objective_queue, objective_level) over the MVA-shaped LR polytope.
MapqnSense
Which direction the bound is taken in.
QrRsrdResult< T > mapqn_qr_bounds_rsrd(const QrRsrdParams< T > &p, int objective_queue, MapqnSense sense=MapqnSense::Min)
Bound the utilization of one queue over the RS-RD polytope.
MapqnAmvaResult< T > mapqn_amva(const std::vector< T > &mu_in, const std::vector< Matrix< T > > &D0s, const std::vector< Matrix< T > > &D1s, const std::vector< int > &N)
Definition mapqn_amva.h:108
MapqnObjectiveVar
The variable family the objective is taken over.
QrBasResult< T > mapqn_qr_bounds_bas(const QrBasParams< T > &p, int objective_queue, MapqnSense sense=MapqnSense::Min)
Bound the utilization of one queue over the BAS polytope.
MapqnQrResult< T > mapqn_bnd_qr_ld(const MapqnParams< T > &p, int objective_queue, int objective_phase, int objective_n, MapqnSense sense=MapqnSense::Max)
Bound P(queue objective_queue holds objective_n jobs in phaseobjective_phase), in the requested direc...
MddClosedQnResult< T > mdd_closedqn(const std::vector< T > &mu, const Matrix< T > &P, const std::vector< double > &servers, int N, const MDD *reuse=nullptr)
Exact solve of a single-class closed exponential queueing network whose CTMC state space (reachable o...
T mdd_rec(const MddStruct &mdds, const std::vector< std::vector< T > > &g)
The normalising constant G = sum_{s in S} prod_l g_l(s_l).
Definition mdd_rec.h:139
PasPlacement< T > pas_placement(const Matrix< T > &H)
Precedence closure of a swap graph.
T infradius_h(const std::vector< T > &x, const Matrix< T > &L, const std::vector< T > &N, const Matrix< T > &alpha)
Logistic-substitution integrand (matlab/src/api/pfqn/infradius_h.m).
PollingPcl< T > polling_pcl(const PollingMoments< T > &m, const std::string &code)
Pseudo-conservation law SUM_i w_i E[W_i] = rhs of a cyclic polling system with Poisson arrivals (Taka...
std::vector< T > polling_qsys_klimited(const PollingMoments< T > &m, std::vector< long > K)
PollingTableResult< T > polling_qsys_table_full(const std::vector< T > &lambda, const std::vector< T > &b, const std::vector< T > &b2, const std::vector< std::size_t > &table, const std::vector< T > &s_mean, const std::vector< T > &s_var, bool gated)
Polling table, exhaustive or gated.
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
std::vector< int > int_vector_from_json(const Json &j, const std::string &where)
An integer vector; a non-integral entry is an error, never a truncation.
Definition api_json.h:267
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_library_only_misc(LibraryOnlyTable &t)
std::vector< T > vector_from_json(const Json &j, const std::string &where)
A flat numeric vector: 1-D array, or a 1-row / 1-column 2-D array.
Definition api_json.h:255
Json encode_vector(const std::vector< T > &v)
Definition api_json.h:333
Json encode_matrix(const Matrix< T > &m)
Definition api_json.h:340
std::string decimal_text(const Json &j, const std::string &where)
The decimal literal behind a JSON scalar, per the policy in the file header.
Definition api_json.h:199
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
void register_api_misc(DispatchTable &t)
std::map< std::string, std::string > LibraryOnlyTable
name -> why the function has no named-JSON-argument form.
Json encode_ints(const std::vector< int > &v)
Definition api_json.h:350
std::map< std::string, Invoker > DispatchTable
RetrievalStationType
Scheduling of a retrieval station, mirroring the MATLAB station_type strings.
RetrievalRayintResult< T > retrieval_rayint(const Matrix< T > &gamma, const std::vector< int > &m)
Discrete (saddle) form.
T retrieval_nc(const std::vector< int > &v, const std::vector< int > &m, const std::vector< T > &lambda, const Matrix< T > &eta, const Matrix< T > &gamma)
Exact normalizing constant E(v,m) of a delayed-hit (list-based) cache.
RetrievalFpiLatencyResult< T > retrieval_fpi_latency(const std::vector< int > &m, const std::vector< T > &lambda, const Matrix< T > &gamma, const std::vector< RetrievalStationPH< T > > &station, const std::vector< Matrix< T > > &R, const FpiOptions &options=FpiOptions())
FPI-based approximation of the delayed-hit count and the expected latency of a list-based cache with ...
RetrievalFpiResult< T > retrieval_fpi(const std::vector< int > &m, const std::vector< T > &lambda, const Matrix< T > &eta, const Matrix< T > &gamma, const FpiOptions &options=FpiOptions())
Fixed-point heuristic for a delayed-hit (list-based) cache.
Mat inv(const Mat &A)
Definition spn_ispn.h:253
std::vector< bool > spn_ispn_covered(const std::vector< std::vector< double > > &C)
Places covered by a non-negative S-invariant (P-semiflow) y' C = 0, by Farkas' algorithm on [C | I] k...
Definition spn_ispn.h:579
IspnResult spn_ispn(const std::vector< std::vector< double > > &pre, const std::vector< std::vector< double > > &post, std::vector< std::vector< double > > inh, const std::vector< double > &rate, std::vector< bool > imm, std::vector< double > srv, const std::vector< double > &m0, std::size_t p0, const IspnOptions &opt=IspnOptions())
Steady state of an infinite-state SPN.
Definition spn_ispn.h:631
SpnEnabling< T > spn_rec_enabled(const mdd::MddStruct &mdds, const std::vector< std::vector< T > > &g, const SpnMode< T > &mde, std::size_t nplacelevels)
Enabling-degree masses of mode mde over the reachable set in mdds.
T spn_conv(const std::vector< std::vector< long long > > &S, const std::vector< long long > &V, const std::vector< std::vector< T > > &g)
The normalising constant by convolution over the invariant load vector.
Definition spn_conv.h:105
SumClosedResult< T > sum_closed(const Matrix< T > &L, const std::vector< long > &N, const std::vector< T > &Z, const std::vector< Servers > &mi, const Matrix< T > &scv, const SumOptions &options=SumOptions())
Summation method (SUM) and its extension (ESUM) for closed queueing networks, including non-product-f...
Definition sum_closed.h:196
SumClosingResult< T > sum_closing(const std::vector< T > &lambda0, const std::vector< T > &scva, const Matrix< T > &L, const std::vector< Servers > &mi, const Matrix< T > &scv, const std::vector< long > &N, const std::vector< T > &Z, const ClosingOptions &options=ClosingOptions())
Closing method for open and mixed non-product-form queueing networks, solved with the summation metho...
Definition sum_closing.h:81
std::vector< T > autocov(const std::vector< T > &S)
Sample autocovariance sequence of a trace, lags 0 .
Definition autocov.h:51
std::vector< int > detect_loops(const Matrix< T > &linkMatrix, const std::vector< int > &serviceNodes, const std::vector< int > &routerNodes, const std::vector< int > &joinNodes=std::vector< int >())
BranchStats< T > get_branch_stats(const std::vector< BranchPattern< T > > &patterns)
Count, total, mean/max/min alternatives, and the mean entropy and balance.
ExpectedIterations< T > get_expected_loop_iterations(const T &loopProbability)
Mean number of visits of a geometric loop, 1/(1-p).
BranchDiversity< T > calculate_branch_diversity(const BranchPattern< T > &pattern)
Shannon entropy of the branch probabilities, the same entropy normalized by log(n),...
std::vector< std::vector< int > > detect_sequences(const Matrix< T > &linkMatrix, const std::vector< int > &serviceNodes)
SequenceStats< T > get_sequence_stats(const std::vector< std::vector< int > > &sequences)
Count, total, mean, maximum and minimum chain length.
std::vector< BranchPattern< T > > detect_branches(const Matrix< T > &linkMatrix, const std::vector< int > &serviceNodes, const std::vector< int > &joinNodes)
ParallelStats< T > get_parallel_stats(const std::vector< std::vector< int > > &patterns)
Count, total, mean and maximum degree of parallelism.
T get_loop_probability(int serviceNode, const Matrix< T > &linkMatrix, const std::vector< int > &routerNodes)
Probability on the router-to-service edge that closes the loop, 0 when the node is not on a simple lo...
LoopStats< T > get_loop_stats(const std::vector< int > &loopNodes, const Matrix< T > &linkMatrix, const std::vector< int > &routerNodes)
Count and moments of the loop probabilities and iteration counts.
std::vector< std::vector< int > > detect_parallel(const Matrix< T > &linkMatrix, const std::vector< int > &serviceNodes, const std::vector< int > &forkNodes, const std::vector< int > &joinNodes)
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Placement order of a pass-and-swap (P&S) order-independent network.
Mean waiting times in polling systems: 1-limited and decrementing service, and the pseudo-conservatio...
Mean waiting times of a polling system under exhaustive service.
Approximate mean waiting times of a cyclic k-limited polling system.
Exact mean waiting times of a polling system with a polling table.
Fixed-point heuristic for a delayed-hit (list-based) cache.
FPI-based approximation of the delayed-hit count and the expected latency of a list-based cache with ...
Exact miss, hit and delayed-hit metrics of a delayed-hit (list-based) cache.
Exact MVA-style recursion for delayed-hit (list-based) cache metrics.
Exact normalizing constant E(v,m) of a delayed-hit (list-based) cache.
Ray (WKB) asymptotic expansion of the list-based cache normalizing constant.
Convolution algorithm for the normalising constant of an S-invariant reachable product-form stochasti...
Infinite-state stochastic Petri nets (iSPN) by the matrix-geometric method.
Decision-diagram reachable set and Kronecker rate descriptor of a stochastic Petri net,...
Enabling-degree distribution of one mode of a product-form stochastic Petri net, by the masked MDD-re...
Minimal-support S-invariants (P-invariants) of a stochastic Petri net, and the load vector V = S m0.
static Servers of(long m)
Definition sum_closed.h:68
static Servers inf()
Definition sum_closed.h:74
Summation method (SUM) and its extension (ESUM) for closed queueing networks, including non-product-f...
Closing method for open and mixed non-product-form queueing networks, solved with the summation metho...
Branch (probabilistic choice) pattern detection in a workflow network.
Loop pattern detection in a workflow network.
Parallel (fork-join) pattern detection in a workflow network.
Sequence pattern detection in a workflow network.