LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
api_dispatch_mc.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 'mc' group (see api_dispatch_detail.h): the
6 * Markov-chain functions not already in api_dispatch.cpp, the npfqn traffic and
7 * bound functions, the discrete-time cycle constants dpfqn_nc / dpfqn_ncld and
8 * the stochastic network calculus envelopes snc_*.
9 *
10 * CONVENTIONS, shared with the mc entries of api_dispatch.cpp:
11 * - a state, station or stage INDEX is 0-based on the way in and is reported
12 * unchanged on the way out (an index is not a measurement). The one
13 * exception is npfqn_sqd's stationToStateful, which the port documents as
14 * ONE-based because it is sn.stationToStateful copied verbatim;
15 * - a MATLAB struct output (ctx, info, aux, result, out) is a nested object
16 * keyed by the MATLAB field names;
17 * - an MMAP is the MATLAB cell {D0, D1, D1^(1), ..., D1^(R)} as a list of
18 * matrices, and a list of MMAPs is a list of such lists;
19 * - a complex number is a pair [re, im]; a plain number is read as real.
20 *
21 * WHERE MATLAB TAKES AN sn. npfqn_nonexp_approx and npfqn_sqd take `sn` in
22 * MATLAB, but their ports take the handful of arrays the reference unpacks
23 * from it as their first act (see the headers). Those arrays are plain data,
24 * so the entries read them under the PORT's parameter names and refuse `sn`.
25 */
26
27#include "api_dispatch_detail.h"
28
29#include <cmath>
30#include <complex>
31#include <cstdint>
32#include <cstdlib>
33#include <string>
34#include <vector>
35
70
71namespace line {
72namespace reg {
73namespace {
74
75// ---------------------------------------------------------------------------
76// Shared readers and encoders
77// ---------------------------------------------------------------------------
78
79/** One non-negative index from a JSON number or numeric string. */
80std::size_t index_from_json(const Json& j, const std::string& where) {
81 const std::string text = decimal_text(j, where);
82 char* end = nullptr;
83 const long long v = std::strtoll(text.c_str(), &end, 10);
84 if (end == text.c_str() || *end != '\0')
85 throw InputError(where + ": an index must be an integer, got " + text);
86 if (v < 0) throw InputError(where + ": an index cannot be negative, got " + text);
87 return static_cast<std::size_t>(v);
88}
89
90/** A flat list of 0-based indices; a bare number is a one-element list. */
91std::vector<std::size_t> indices_from_json(const Json& j, const std::string& where) {
92 std::vector<std::size_t> out;
93 if (!j.is_array()) {
94 out.push_back(index_from_json(j, where));
95 return out;
96 }
97 for (std::size_t k = 0; k < j.size(); ++k)
98 out.push_back(index_from_json(j[k], where + "[" + std::to_string(k) + "]"));
99 return out;
100}
101
102std::vector<std::size_t> read_indices(Args& a, const char* key) {
103 return indices_from_json(a.get(key), a.get(key).is_null() ? key : std::string(key));
104}
105
106/** A list of index lists (sigma of npfqn_bnd_bgt). */
107std::vector<std::vector<std::size_t> > read_index_lists(Args& a, const char* key) {
108 const Json& j = a.get(key);
109 const std::string where = std::string(key);
110 if (!j.is_array()) throw InputError(where + ": expected a list of index lists");
111 std::vector<std::vector<std::size_t> > out;
112 for (std::size_t b = 0; b < j.size(); ++b)
113 out.push_back(indices_from_json(j[b], where + "[" + std::to_string(b) + "]"));
114 return out;
115}
116
117Json encode_indices(const std::vector<std::size_t>& v) {
118 Json a = Json::array();
119 for (std::size_t i = 0; i < v.size(); ++i) a.push_back(static_cast<std::uint64_t>(v[i]));
120 return a;
121}
122
123/** A per-station flag vector: an array of booleans or of 0/1. */
124std::vector<bool> read_flags(Args& a, const char* key) {
125 const Json& j = a.get(key);
126 const std::string where = std::string(key);
127 std::vector<bool> out;
128 const Json flat = j.is_array() ? j : Json::array({j});
129 for (std::size_t k = 0; k < flat.size(); ++k) {
130 const Json& e = flat[k];
131 if (e.is_boolean()) {
132 out.push_back(e.get<bool>());
133 } else {
134 const std::string text = decimal_text(e, where);
135 if (text != "0" && text != "1")
136 throw InputError(where + ": a flag must be true, false, 0 or 1, got " + text);
137 out.push_back(text == "1");
138 }
139 }
140 return out;
141}
142
143/** A list of numeric vectors (mu of npfqn_bnd_bgt). */
144template <class T>
145std::vector<std::vector<T> > read_vector_list(Args& a, const char* key) {
146 const Json& j = a.get(key);
147 const std::string where = std::string(key);
148 if (!j.is_array()) throw InputError(where + ": expected a list of vectors");
149 std::vector<std::vector<T> > out;
150 for (std::size_t i = 0; i < j.size(); ++i)
151 out.push_back(vector_from_json<T>(j[i], where + "[" + std::to_string(i) + "]"));
152 return out;
153}
154
155/** A matrix as the list of its rows (P of dpfqn_ncld, J x N). */
156template <class T>
157std::vector<std::vector<T> > rows_of(const Matrix<T>& m) {
158 std::vector<std::vector<T> > out(m.rows(), std::vector<T>(m.cols()));
159 for (std::size_t i = 0; i < m.rows(); ++i)
160 for (std::size_t k = 0; k < m.cols(); ++k) out[i][k] = m(i, k);
161 return out;
162}
163
164/** An MMAP cell {D0, D1, D1^(1), ...} as the mam::Mmap struct; [] is the empty flow. */
165template <class T>
166mam::Mmap<T> mmap_from_json(const Json& j, const std::string& where) {
167 if (!j.is_array()) throw InputError(where + ": expected an MMAP, a list of matrices");
168 mam::Mmap<T> m;
169 if (j.empty()) return m;
170 if (j.size() < 2)
171 throw InputError(where + ": an MMAP is {D0, D1, D1^(1), ...}, got one matrix");
172 m.D0 = matrix_from_json<T>(j[0], where + "[0]");
173 m.D1 = matrix_from_json<T>(j[1], where + "[1]");
174 for (std::size_t k = 2; k < j.size(); ++k)
175 m.Dc.push_back(matrix_from_json<T>(j[k], where + "[" + std::to_string(k) + "]"));
176 return m;
177}
178
179template <class T>
180std::vector<mam::Mmap<T> > read_mmap_list(Args& a, const char* key) {
181 const Json& j = a.get(key);
182 const std::string where = std::string(key);
183 if (!j.is_array()) throw InputError(where + ": expected a list of MMAPs");
184 std::vector<mam::Mmap<T> > out;
185 for (std::size_t i = 0; i < j.size(); ++i)
186 out.push_back(mmap_from_json<T>(j[i], where + "[" + std::to_string(i) + "]"));
187 return out;
188}
189
190template <class T>
191Json encode_mmap(const mam::Mmap<T>& m) {
192 std::vector<Matrix<T> > cell;
193 cell.push_back(m.D0);
194 cell.push_back(m.D1);
195 for (const Matrix<T>& d : m.Dc) cell.push_back(d);
196 return encode_matrices(cell);
197}
198
199/** Complex points: a number is real, a pair [re, im] is complex. */
200std::vector<std::complex<double> > read_complex(Args& a, const char* key) {
201 const Json& j = a.get(key);
202 const std::string where = std::string(key);
203 const Json flat = j.is_array() ? j : Json::array({j});
204 std::vector<std::complex<double> > out;
205 for (std::size_t k = 0; k < flat.size(); ++k) {
206 const Json& e = flat[k];
207 const std::string w = where + "[" + std::to_string(k) + "]";
208 if (e.is_array()) {
209 if (e.size() != 2) throw InputError(w + ": a complex number is a pair [re, im]");
210 out.emplace_back(number_from_json<double>(e[0], w), number_from_json<double>(e[1], w));
211 } else {
212 out.emplace_back(number_from_json<double>(e, w), 0.0);
213 }
214 }
215 return out;
216}
217
218Json encode_complex(const std::vector<std::complex<double> >& v) {
219 Json a = Json::array();
220 for (const std::complex<double>& z : v) a.push_back(Json::array({z.real(), z.imag()}));
221 return a;
222}
223
224/** A nested struct argument (config, options, corrections), or null when absent. */
225const Json* sub_object(Args& a, const char* key) {
226 const Json* j = a.opt(key);
227 if (j && !j->is_object())
228 throw InputError(std::string(key) + ": expected an object with named fields");
229 return j;
230}
231
232/** The C++ default arguments of options.method / options.lti_method. */
233struct PassageOptions {
234 bool hasMethod = false;
235 std::string method;
236 bool hasLti = false;
237 std::string lti;
238};
239
240/**
241 * MATLAB's `options` of ctmc_passage_time / ctmc_passage_taboo. lti_method
242 * picks the Laplace inversion of method 'lt' and has no effect under any other
243 * method, so naming it without method 'lt' is refused rather than ignored.
244 */
245PassageOptions read_passage_options(Args& a, const std::string& fn) {
246 PassageOptions o;
247 const Json* j = sub_object(a, "options");
248 if (!j) return o;
249 Args op(*j, fn + ": options");
250 o.hasMethod = op.has("method");
251 if (o.hasMethod) o.method = op.text("method", "");
252 o.hasLti = op.has("lti_method");
253 if (o.hasLti) o.lti = op.text("lti_method", "");
254 op.done();
255 if (o.hasLti && !(o.hasMethod && o.method == "lt"))
256 throw InputError(fn + ": options.lti_method selects the inversion of method 'lt' and "
257 "has no effect otherwise; set options.method to 'lt' or drop it");
258 return o;
259}
260
261// ---------------------------------------------------------------------------
262// mc
263// ---------------------------------------------------------------------------
264
265/** [S,Q11,Q12,Q21,Q22,T,X] = ctmc_stochcomp(Q, I); I is 0-based and optional. */
266struct OpCtmcStochcomp {
267 template <class T>
268 static Json run(Args& a) {
269 const Matrix<T> Q = a.matrix<T>("Q");
270 const bool hasI = a.has("I");
271 const std::vector<std::size_t> I = hasI ? read_indices(a, "I") : std::vector<std::size_t>();
272 a.done();
273 const mc::StochCompResult<T> r = hasI ? mc::ctmc_stochcomp(Q, I) : mc::ctmc_stochcomp(Q);
274 Json out;
275 out["S"] = encode_matrix(r.S);
276 out["Q11"] = encode_matrix(r.Q11);
277 out["Q12"] = encode_matrix(r.Q12);
278 out["Q21"] = encode_matrix(r.Q21);
279 out["Q22"] = encode_matrix(r.Q22);
280 out["T"] = encode_matrix(r.T12);
281 out["X"] = encode_matrix(r.X);
282 return out;
283 }
284};
285
286/**
287 * tol / maxiter of ctmc_uniformization and ctmc_timeaverage. They are
288 * positional in MATLAB, so maxiter cannot be named without tol there; the same
289 * holds here, which keeps every call one of the port's three arities.
290 */
291template <class R, class F>
292R call_tol_maxiter(Args& a, const std::string& fn, F f) {
293 const bool hasTol = a.has("tol"), hasMax = a.has("maxiter");
294 if (hasMax && !hasTol)
295 throw InputError(fn + ": 'maxiter' follows 'tol' in the MATLAB signature; name both");
296 const double tol = hasTol ? a.number<double>("tol") : 0.0;
297 const long maxiter = hasMax ? static_cast<long>(a.required_integer("maxiter")) : 0;
298 a.done();
299 if (hasMax) return f(3, tol, maxiter);
300 if (hasTol) return f(2, tol, maxiter);
301 return f(1, tol, maxiter);
302}
303
304/** [pi,kmax] = ctmc_uniformization(pi0,Q,t,tol,maxiter). */
305struct OpCtmcUniformization {
306 template <class T>
307 static Json run(Args& a) {
308 const std::vector<T> pi0 = a.vector<T>("pi0");
309 const Matrix<T> Q = a.matrix<T>("Q");
310 const T t = a.number<T>("t");
311 const mc::UniformizationResult<T> r = call_tol_maxiter<mc::UniformizationResult<T> >(
312 a, "ctmc_uniformization", [&](int n, double tol, long maxiter) {
313 if (n == 3) return mc::ctmc_uniformization(pi0, Q, t, tol, maxiter);
314 if (n == 2) return mc::ctmc_uniformization(pi0, Q, t, tol);
315 return mc::ctmc_uniformization(pi0, Q, t);
316 });
317 Json out;
318 out["pi"] = encode_vector(r.pi);
319 out["kmax"] = encode_count(r.kmax);
320 return out;
321 }
322};
323
324/** [piTimeAvg,piExit,kmax] = ctmc_timeaverage(pi0,Q,t,tol,maxiter). */
325struct OpCtmcTimeaverage {
326 template <class T>
327 static Json run(Args& a) {
328 const std::vector<T> pi0 = a.vector<T>("pi0");
329 const Matrix<T> Q = a.matrix<T>("Q");
330 const T t = a.number<T>("t");
331 const mc::TimeAverageResult<T> r = call_tol_maxiter<mc::TimeAverageResult<T> >(
332 a, "ctmc_timeaverage", [&](int n, double tol, long maxiter) {
333 if (n == 3) return mc::ctmc_timeaverage(pi0, Q, t, tol, maxiter);
334 if (n == 2) return mc::ctmc_timeaverage(pi0, Q, t, tol);
335 return mc::ctmc_timeaverage(pi0, Q, t);
336 });
337 Json out;
338 out["piTimeAvg"] = encode_vector(r.piTimeAvg);
339 out["piExit"] = encode_vector(r.piExit);
340 out["kmax"] = encode_count(r.kmax);
341 return out;
342 }
343};
344
345/**
346 * [p,flag,relres,iter] = ctmc_power / ctmc_jacobi / ctmc_gaussseidel(Q,tol,maxiter,p0)
347 * and [p,flag,relres,iter,omega] = ctmc_sor(Q,tol,maxiter,omega,p0). Every
348 * argument but Q is optional and named, a nonpositive tol / maxiter / omega
349 * selecting the default (omega <= 0 is the adaptive SOR rule).
350 */
351template <mc::detail::SweepKind K>
352struct OpCtmcClassical {
353 template <class T>
354 static Json run(Args& a) {
355 const Matrix<T> Q = a.matrix<T>("Q");
356 const double tol = a.scalar<double>("tol", mc::CLASSICAL_DEFAULT_TOL);
357 const long maxiter = static_cast<long>(a.integer("maxiter", static_cast<int>(mc::CLASSICAL_DEFAULT_MAXITER)));
358 const double omega = (K == mc::detail::SweepKind::Sor) ? a.scalar<double>("omega", 0.0)
359 : (K == mc::detail::SweepKind::GaussSeidel) ? 1.0 : 0.0;
360 const std::vector<T> p0 = a.vector_or_empty<T>("p0");
361 a.done();
362 const mc::ClassicalResult<T> r = mc::detail::stationary_sweep_iterate(K, Q, tol, maxiter, omega, p0);
363 Json out;
364 out["p"] = encode_vector(r.p);
365 out["flag"] = r.flag;
366 out["relres"] = encode_scalar(r.relres);
367 out["iter"] = static_cast<std::int64_t>(r.iter);
368 if (K == mc::detail::SweepKind::Sor) out["omega"] = encode_scalar(r.omega);
369 return out;
370 }
371};
372
373/**
374 * [p,flag] = ctmc_gth(Q) / dtmc_gth(P): GTH elimination at every arithmetic
375 * (no subtraction, so exact at --arith exact). With several closed classes p is
376 * NaN and flag 1, as the MATLAB two-output form returns.
377 */
378template <bool Dtmc>
379struct OpGth {
380 template <class T>
381 static Json run(Args& a) {
382 const Matrix<T> Q = a.matrix<T>(Dtmc ? "P" : "Q");
383 a.done();
384 const mc::GthResult<T> r = mc::ctmc_gth_flag(Q);
385 Json out;
386 if (r.flag == 0) {
387 out["p"] = encode_vector(r.p);
388 } else {
389 Json nan = Json::array();
390 for (std::size_t i = 0; i < Q.rows(); ++i) nan.push_back("NaN");
391 out["p"] = nan;
392 }
393 out["flag"] = r.flag;
394 return out;
395 }
396};
397
398/** J = ctmc_cesaro(Q) / dtmc_cesaro(P): limit matrix of any finite chain. */
399template <bool Dtmc>
400struct OpCesaro {
401 template <class T>
402 static Json run(Args& a) {
403 const Matrix<T> Q = a.matrix<T>(Dtmc ? "P" : "Q");
404 a.done();
405 Json out;
406 out["J"] = encode_matrix(mc::ctmc_cesaro(Q));
407 return out;
408 }
409};
410
411/** p = ctmc_ssd(C,R) / dtmc_ssd(C,R); an absent edge is R = "Inf" or C = 0. */
412struct OpSsd {
413 static Json run(Args& a) {
414 const Matrix<double> C = a.matrix<double>("C");
415 const Matrix<double> R = a.matrix<double>("R");
416 a.done();
417 Json out;
418 out["p"] = encode_vector(mc::ctmc_ssd(C, R));
419 return out;
420 }
421};
422
423/** [L,piT,kmax] = ctmc_accumulated(pi0,Q,t,tol,maxiter); t may be a vector. */
424struct OpCtmcAccumulated {
425 template <class T>
426 static Json run(Args& a) {
427 const std::vector<T> pi0 = a.vector<T>("pi0");
428 const Matrix<T> Q = a.matrix<T>("Q");
429 const std::vector<T> t = a.vector<T>("t");
430 const mc::AccumulatedResult<T> r = call_tol_maxiter<mc::AccumulatedResult<T> >(
431 a, "ctmc_accumulated", [&](int n, double tol, long maxiter) {
432 if (n == 3) return mc::ctmc_accumulated(pi0, Q, t, tol, maxiter);
433 if (n == 2) return mc::ctmc_accumulated(pi0, Q, t, tol);
434 return mc::ctmc_accumulated(pi0, Q, t);
435 });
436 Json out;
437 out["L"] = encode_matrix(r.L);
438 out["piT"] = encode_matrix(r.piT);
439 out["kmax"] = encode_count(r.kmax);
440 return out;
441 }
442};
443
444/**
445 * [ok,msg] = ctmc_memory_gate(logNstates,force,verbose,safetyFraction). The
446 * kernel is double-valued whatever the arithmetic; 'verbose' is refused because
447 * it prints the calibration on stdout, the stream that carries the JSON reply.
448 */
449struct OpCtmcMemoryGate {
450 template <class T>
451 static Json run(Args& a) {
452 const double logN = a.number<double>("logNstates");
453 a.unsupported("verbose",
454 "ctmc_memory_gate: 'verbose' prints to stdout, which carries the JSON "
455 "reply; the decision and its message are returned as ok and msg");
456 const bool hasForce = a.has("force"), hasSafety = a.has("safetyFraction");
457 const bool force = hasForce ? a.boolean("force", false) : false;
458 const double safety = hasSafety ? a.number<double>("safetyFraction") : 0.0;
459 a.done();
460 mc::CtmcGateResult r;
461 if (hasSafety)
462 r = mc::ctmc_memory_gate(logN, force, safety);
463 else if (hasForce)
464 r = mc::ctmc_memory_gate(logN, force);
465 else
466 r = mc::ctmc_memory_gate(logN);
467 Json out;
468 out["ok"] = r.ok;
469 out["msg"] = r.message;
470 return out;
471 }
472};
473
474/** bool = ctmc_isfeasible(Q, tol). */
475struct OpCtmcIsfeasible {
476 template <class T>
477 static Json run(Args& a) {
478 const Matrix<T> Q = a.matrix<T>("Q");
479 const bool hasTol = a.has("tol");
480 const T tol = hasTol ? a.number<T>("tol") : T();
481 a.done();
482 Json out;
483 out["bool"] = hasTol ? mc::ctmc_isfeasible(Q, tol) : mc::ctmc_isfeasible(Q);
484 return out;
485 }
486};
487
488/** h = ctmc_hitting_time(Q, targetStates) and h = dtmc_hitting_time(P, targetStates). */
489struct OpCtmcHittingTime {
490 template <class T>
491 static Json run(Args& a) {
492 const Matrix<T> Q = a.matrix<T>("Q");
493 const std::vector<std::size_t> target = read_indices(a, "targetStates");
494 a.done();
495 Json out;
496 out["h"] = encode_vector(mc::ctmc_hitting_time(Q, target));
497 return out;
498 }
499};
500
501struct OpDtmcHittingTime {
502 template <class T>
503 static Json run(Args& a) {
504 const Matrix<T> P = a.matrix<T>("P");
505 const std::vector<std::size_t> target = read_indices(a, "targetStates");
506 a.done();
507 Json out;
508 out["h"] = encode_vector(mc::dtmc_hitting_time(P, target));
509 return out;
510 }
511};
512
513/**
514 * pi_t = dtmc_transient(P, pi0, steps). An absent pi0 is the uniform law (the
515 * port's empty-vector form). The port has no (P, pi0) overload, so a call that
516 * names pi0 but not steps passes MATLAB's steps = 1 explicitly.
517 */
518struct OpDtmcTransient {
519 template <class T>
520 static Json run(Args& a) {
521 const Matrix<T> P = a.matrix<T>("P");
522 const bool hasPi0 = a.has("pi0"), hasSteps = a.has("steps");
523 const std::vector<T> pi0 = a.vector_or_empty<T>("pi0");
524 const std::size_t steps = hasSteps ? a.count("steps") : 1;
525 a.done();
526 Json out;
527 out["pi_t"] = encode_matrix((hasPi0 || hasSteps) ? mc::dtmc_transient(P, pi0, steps)
528 : mc::dtmc_transient(P));
529 return out;
530 }
531};
532
533/** [alpha,S,s0,keep,atom] = ctmc_passage_ph(Q,pi0,target); an absent pi0 is the stationary law. */
534struct OpCtmcPassagePh {
535 template <class T>
536 static Json run(Args& a) {
537 const Matrix<T> Q = a.matrix<T>("Q");
538 const std::vector<T> pi0 = a.vector_or_empty<T>("pi0");
539 const std::vector<std::size_t> target = read_indices(a, "target");
540 a.done();
541 const mc::PassagePh<T> r = mc::ctmc_passage_ph(Q, pi0, target);
542 Json out;
543 out["alpha"] = encode_vector(r.alpha);
544 out["S"] = encode_matrix(r.S);
545 out["s0"] = encode_vector(r.s0);
546 out["keep"] = encode_indices(r.keep);
547 out["atom"] = encode_scalar(r.atom);
548 return out;
549 }
550};
551
552/** L = ctmc_passage_lst(Q,pi0,target,s); s and L are lists of [re, im]. */
553struct OpCtmcPassageLst {
554 template <class T>
555 static Json run(Args& a) {
556 const Matrix<T> Q = a.matrix<T>("Q");
557 const std::vector<T> pi0 = a.vector_or_empty<T>("pi0");
558 const std::vector<std::size_t> target = read_indices(a, "target");
559 const std::vector<std::complex<double> > s = read_complex(a, "s");
560 a.done();
561 Json out;
562 out["L"] = encode_complex(mc::ctmc_passage_lst(Q, pi0, target, s));
563 return out;
564 }
565};
566
567/** [mall,m] = ctmc_passage_moments(Q,pi0,target,nmax). */
568struct OpCtmcPassageMoments {
569 template <class T>
570 static Json run(Args& a) {
571 const Matrix<T> Q = a.matrix<T>("Q");
572 const std::vector<T> pi0 = a.vector_or_empty<T>("pi0");
573 const std::vector<std::size_t> target = read_indices(a, "target");
574 const bool hasN = a.has("nmax");
575 const std::size_t nmax = hasN ? a.count("nmax") : 0;
576 a.done();
577 const mc::PassageMoments<T> r = hasN ? mc::ctmc_passage_moments(Q, pi0, target, nmax)
578 : mc::ctmc_passage_moments(Q, pi0, target);
579 Json out;
580 out["mall"] = encode_matrix(r.mall);
581 out["m"] = encode_vector(r.m);
582 return out;
583 }
584};
585
586/**
587 * [F,f,out] = ctmc_passage_time(Q,pi0,target,tset,options). out carries atom
588 * and the phase-type representation (alpha, S, s0, keep) the curve is computed
589 * from, which is ctmc_passage_ph of the same arguments.
590 */
591struct OpCtmcPassageTime {
592 template <class T>
593 static Json run(Args& a) {
594 const Matrix<T> Q = a.matrix<T>("Q");
595 const std::vector<T> pi0 = a.vector_or_empty<T>("pi0");
596 const std::vector<std::size_t> target = read_indices(a, "target");
597 const std::vector<double> tset = a.vector<double>("tset");
598 const PassageOptions o = read_passage_options(a, "ctmc_passage_time");
599 a.done();
600 const mc::PassageCurve<T> c =
601 o.hasLti ? mc::ctmc_passage_time(Q, pi0, target, tset, o.method, o.lti)
602 : o.hasMethod ? mc::ctmc_passage_time(Q, pi0, target, tset, o.method)
603 : mc::ctmc_passage_time(Q, pi0, target, tset);
604 const mc::PassagePh<T> ph = mc::ctmc_passage_ph(Q, pi0, target);
605 Json out;
606 out["F"] = encode_vector(c.F);
607 out["f"] = encode_vector(c.f);
608 Json o2;
609 o2["atom"] = encode_scalar(c.atom);
610 o2["alpha"] = encode_vector(ph.alpha);
611 o2["S"] = encode_matrix(ph.S);
612 o2["s0"] = encode_vector(ph.s0);
613 o2["keep"] = encode_indices(ph.keep);
614 out["out"] = o2;
615 return out;
616 }
617};
618
619/** [F,f,out] = ctmc_passage_taboo(Q,pi0,target,taboo,tset,options). */
620struct OpCtmcPassageTaboo {
621 template <class T>
622 static Json run(Args& a) {
623 const Matrix<T> Q = a.matrix<T>("Q");
624 const std::vector<T> pi0 = a.vector_or_empty<T>("pi0");
625 const std::vector<std::size_t> target = read_indices(a, "target");
626 const std::vector<std::size_t> taboo = read_indices(a, "taboo");
627 const bool hasT = a.has("tset");
628 const std::vector<double> tset = a.vector_or_empty<double>("tset");
629 const PassageOptions o = read_passage_options(a, "ctmc_passage_taboo");
630 a.done();
631 const mc::PassageTaboo<T> r =
632 o.hasLti ? mc::ctmc_passage_taboo(Q, pi0, target, taboo, tset, o.method, o.lti)
633 : o.hasMethod ? mc::ctmc_passage_taboo(Q, pi0, target, taboo, tset, o.method)
634 : hasT ? mc::ctmc_passage_taboo(Q, pi0, target, taboo, tset)
635 : mc::ctmc_passage_taboo(Q, pi0, target, taboo);
636 Json out;
637 out["F"] = encode_vector(r.F);
638 out["f"] = encode_vector(r.f);
639 Json o2;
640 o2["atom"] = encode_scalar(r.atom);
641 o2["reachprob"] = encode_scalar(r.reachprob);
642 o2["tabooprob"] = encode_scalar(r.tabooprob);
643 o2["pB"] = encode_vector(r.pB);
644 o2["pC"] = encode_vector(r.pC);
645 o2["alpha"] = encode_vector(r.alpha);
646 o2["S"] = encode_matrix(r.S);
647 o2["sB"] = encode_vector(r.sB);
648 o2["sC"] = encode_vector(r.sC);
649 o2["keep"] = encode_indices(r.keep);
650 o2["target"] = encode_indices(r.target);
651 o2["taboo"] = encode_indices(r.taboo);
652 o2["method"] = r.method;
653 out["out"] = o2;
654 return out;
655 }
656};
657
658/** [mall,m,pB] = ctmc_passage_taboo_moments(Q,pi0,target,taboo,nmax). */
659struct OpCtmcPassageTabooMoments {
660 template <class T>
661 static Json run(Args& a) {
662 const Matrix<T> Q = a.matrix<T>("Q");
663 const std::vector<T> pi0 = a.vector_or_empty<T>("pi0");
664 const std::vector<std::size_t> target = read_indices(a, "target");
665 const std::vector<std::size_t> taboo = read_indices(a, "taboo");
666 const bool hasN = a.has("nmax");
667 const std::size_t nmax = hasN ? a.count("nmax") : 0;
668 a.done();
669 const mc::PassageTabooMoments<T> r =
670 hasN ? mc::ctmc_passage_taboo_moments(Q, pi0, target, taboo, nmax)
671 : mc::ctmc_passage_taboo_moments(Q, pi0, target, taboo);
672 Json out;
673 out["mall"] = encode_matrix(r.mall);
674 out["m"] = encode_vector(r.m);
675 out["pB"] = encode_vector(r.pB);
676 return out;
677 }
678};
679
680// ---------------------------------------------------------------------------
681// dpfqn
682// ---------------------------------------------------------------------------
683
684/** [lG,G,G1] = dpfqn_nc(p, N). */
685struct OpDpfqnNc {
686 template <class T>
687 static Json run(Args& a) {
688 const std::vector<T> p = a.vector<T>("p");
689 const std::size_t N = a.count("N");
690 a.done();
691 const dpfqn::DtNcResult<T> r = dpfqn::dpfqn_nc(p, N);
692 Json out;
693 out["lG"] = encode_scalar(r.lG);
694 out["G"] = encode_scalar(r.G);
695 out["G1"] = encode_vector(r.G1);
696 return out;
697 }
698};
699
700/** [lG,G,W,Gc,Wa] = dpfqn_ncld(P, N), P a J-by-N matrix with P(j,n) = p_j(n). */
701struct OpDpfqnNcld {
702 template <class T>
703 static Json run(Args& a) {
704 const Matrix<T> P = a.matrix<T>("P");
705 const std::size_t N = a.count("N");
706 a.done();
707 const dpfqn::DtNcLdResult<T> r = dpfqn::dpfqn_ncld(rows_of(P), N);
708 Json out;
709 out["lG"] = encode_scalar(r.lG);
710 out["G"] = encode_vector(r.G);
711 out["W"] = encode_vectors(r.W);
712 out["Gc"] = encode_vectors(r.Gc);
713 out["Wa"] = encode_vectors(r.Wa);
714 return out;
715 }
716};
717
718// ---------------------------------------------------------------------------
719// npfqn
720// ---------------------------------------------------------------------------
721
722/**
723 * ctx = npfqn_traffic_idc(lambda0,P,c2a0,a0IdcFun,mu,cs2,sIdcFun,corrections).
724 * The two IDC callables are stored in MATLAB's ctx only for npfqn_traffic_idc_at
725 * (library-only) and play no part in ctx itself, so they are refused by name.
726 */
727struct OpNpfqnTrafficIdc {
728 template <class T>
729 static Json run(Args& a) {
730 const std::vector<T> lambda0 = a.vector<T>("lambda0");
731 const Matrix<T> P = a.matrix<T>("P");
732 const std::vector<T> c2a0 = a.vector<T>("c2a0");
733 const std::vector<T> mu = a.vector<T>("mu");
734 const std::vector<T> cs2 = a.vector<T>("cs2");
735 a.unsupported("a0IdcFun", "npfqn_traffic_idc: a0IdcFun is a function handle with no "
736 "JSON form; it is used only by npfqn_traffic_idc_at");
737 a.unsupported("sIdcFun", "npfqn_traffic_idc: sIdcFun is a function handle with no "
738 "JSON form; it is used only by npfqn_traffic_idc_at");
739 const Json* cj = sub_object(a, "corrections");
740 npfqn::TrafficIdcCorrections corr;
741 if (cj) {
742 Args c(*cj, "npfqn_traffic_idc: corrections");
743 if (c.has("alpha")) corr.alpha = c.boolean("alpha", corr.alpha);
744 if (c.has("beta")) corr.beta = c.boolean("beta", corr.beta);
745 c.done();
746 }
747 a.done();
748 const npfqn::TrafficIdcContext<T> x = cj ? npfqn::npfqn_traffic_idc(lambda0, P, c2a0, mu, cs2, corr)
749 : npfqn::npfqn_traffic_idc(lambda0, P, c2a0, mu, cs2);
750 Json ctx;
751 ctx["K"] = encode_count(x.K);
752 ctx["lambda"] = encode_vector(x.lambda);
753 ctx["lambda0"] = encode_vector(x.lambda0);
754 ctx["mu"] = encode_vector(x.mu);
755 ctx["rho"] = encode_vector(x.rho);
756 ctx["cs2"] = encode_vector(x.cs2);
757 ctx["c2a0"] = encode_vector(x.c2a0);
758 ctx["c2a"] = encode_vector(x.c2a);
759 ctx["c2d"] = encode_vector(x.c2d);
760 ctx["c2x"] = encode_vector(x.c2x);
761 ctx["P"] = encode_matrix(x.P);
762 ctx["Xi"] = encode_matrix(x.Xi);
763 ctx["lam_ji"] = encode_matrix(x.lam_ji);
764 ctx["c2aij"] = encode_matrix(x.c2aij);
765 ctx["c2alpha"] = encode_matrix(x.c2alpha);
766 ctx["zetaAll"] = encode_matrices(x.zetaAll);
767 Json out;
768 out["ctx"] = ctx;
769 return out;
770 }
771};
772
773/**
774 * SMMAP = npfqn_traffic_split_cs(MMAP, P, config). The reference never reads
775 * config, so naming it is refused rather than silently dropped.
776 */
777struct OpNpfqnTrafficSplitCs {
778 template <class T>
779 static Json run(Args& a) {
780 const std::vector<Matrix<T> > M = a.matrices<T>("MMAP");
781 const Matrix<T> P = a.matrix<T>("P");
782 a.unsupported("config", "npfqn_traffic_split_cs: the reference accepts config but never "
783 "reads it; there is nothing for it to select");
784 a.done();
785 const std::vector<npfqn::Mmap<T> > s = npfqn::npfqn_traffic_split_cs(M, P);
786 Json out = Json::array();
787 for (const npfqn::Mmap<T>& m : s) out.push_back(encode_matrices(m));
788 Json r;
789 r["SMMAP"] = out;
790 return r;
791 }
792};
793
794npfqn::Merge merge_from_text(const std::string& s, const std::string& fn) {
795 if (s == "default") return npfqn::Merge::Default;
796 if (s == "super") return npfqn::Merge::Super;
797 if (s == "mixture") return npfqn::Merge::Mixture;
798 if (s == "interpos") return npfqn::Merge::Interpos;
799 throw InputError(fn + ": config.merge must be 'default', 'super', 'mixture' or 'interpos', got '" +
800 s + "'");
801}
802
803npfqn::Compress compress_from_text(const std::string& s, const std::string& fn) {
804 if (s == "default") return npfqn::Compress::Default;
805 if (s == "none") return npfqn::Compress::None;
806 throw InputError(fn + ": config.compress must be 'default' or 'none', got '" + s + "'");
807}
808
809/** SMMAP = npfqn_traffic_merge_cs(MMAP, prob, config); config.merge only. */
810struct OpNpfqnTrafficMergeCs {
811 template <class T>
812 static Json run(Args& a) {
813 const std::vector<mam::Mmap<T> > flows = read_mmap_list<T>(a, "MMAP");
814 const Matrix<T> prob = a.matrix<T>("prob");
815 const Json* cj = sub_object(a, "config");
816 bool hasMerge = false;
818 if (cj) {
819 Args c(*cj, "npfqn_traffic_merge_cs: config");
820 hasMerge = c.has("merge");
821 if (hasMerge) merge = merge_from_text(c.text("merge", ""), "npfqn_traffic_merge_cs");
822 c.done();
823 }
824 a.done();
825 const mam::Mmap<T> s = hasMerge ? npfqn::npfqn_traffic_merge_cs(flows, prob, merge)
826 : npfqn::npfqn_traffic_merge_cs(flows, prob);
827 Json out;
828 out["SMMAP"] = encode_mmap(s);
829 return out;
830 }
831};
832
833/** SMMAP = npfqn_traffic_merge(MMAP, config); config.merge and config.compress. */
834struct OpNpfqnTrafficMerge {
835 template <class T>
836 static Json run(Args& a) {
837 const std::vector<mam::Mmap<T> > flows = read_mmap_list<T>(a, "MMAP");
838 const Json* cj = sub_object(a, "config");
839 npfqn::MergeConfig cfg;
840 if (cj) {
841 Args c(*cj, "npfqn_traffic_merge: config");
842 if (c.has("merge")) cfg.merge = merge_from_text(c.text("merge", ""), "npfqn_traffic_merge");
843 if (c.has("compress"))
844 cfg.compress = compress_from_text(c.text("compress", ""), "npfqn_traffic_merge");
845 c.done();
846 }
847 a.done();
848 const mam::Mmap<T> s = cj ? npfqn::npfqn_traffic_merge(flows, cfg) : npfqn::npfqn_traffic_merge(flows);
849 Json out;
850 out["SMMAP"] = encode_mmap(s);
851 return out;
852 }
853};
854
855/** w = npfqn_rqna_weight(t): a scalar t gives a scalar w, an array an array. */
856struct OpNpfqnRqnaWeight {
857 template <class T>
858 static Json run(Args& a) {
859 const bool vec = a.get("t").is_array();
860 Json out;
861 if (vec) {
862 const std::vector<T> t = a.vector<T>("t");
863 a.done();
865 } else {
866 const T t = a.number<T>("t");
867 a.done();
869 }
870 return out;
871 }
872};
873
874/** [lambda,Gamma,alpha] = npfqn_traffic_rqt(lambda0,Gamma0,alpha0,F). */
875struct OpNpfqnTrafficRqt {
876 template <class T>
877 static Json run(Args& a) {
878 const std::vector<T> lambda0 = a.vector<T>("lambda0");
879 const std::vector<T> Gamma0 = a.vector<T>("Gamma0");
880 const std::vector<T> alpha0 = a.vector<T>("alpha0");
881 const Matrix<T> F = a.matrix<T>("F");
882 a.done();
883 const npfqn::TrafficRqt<T> r = npfqn::npfqn_traffic_rqt(lambda0, Gamma0, alpha0, F);
884 Json out;
885 out["lambda"] = encode_vector(r.lambda);
886 out["Gamma"] = encode_vector(r.Gamma);
887 out["alpha"] = encode_vector(r.alpha);
888 return out;
889 }
890};
891
892/**
893 * [zlb,x,info] = npfqn_bnd_bpt(lambda0,mu,P,stationOf,c); stationOf is 0-based
894 * and an absent c is MATLAB's empty c (all ones).
895 */
896struct OpNpfqnBndBpt {
897 template <class T>
898 static Json run(Args& a) {
899 const std::vector<T> lambda0 = a.vector<T>("lambda0");
900 const std::vector<T> mu = a.vector<T>("mu");
901 const Matrix<T> P = a.matrix<T>("P");
902 const std::vector<std::size_t> stationOf = read_indices(a, "stationOf");
903 const std::vector<T> c = a.vector_or_empty<T>("c");
904 a.done();
905 const npfqn::BndBpt<T> r = npfqn::npfqn_bnd_bpt(lambda0, mu, P, stationOf, c);
906 Json out;
907 out["zlb"] = encode_scalar(r.zlb);
908 out["x"] = encode_vector(r.x);
909 Json info;
910 info["lambda"] = encode_vector(r.lambda);
911 info["rho"] = encode_vector(r.rho);
912 info["rhoStation"] = encode_vector(r.rhoStation);
913 info["nvars"] = encode_count(r.nvars);
914 info["nrows"] = encode_count(r.nrows);
915 out["info"] = info;
916 return out;
917 }
918};
919
920/**
921 * [Qub,info] = npfqn_bnd_bgt(lambda,mu,sigma,J). mu and sigma are lists with
922 * one entry per type; sigma holds 0-based stations. An absent J is derived from
923 * sigma as MATLAB derives it (one past the largest station index); the port has
924 * no shorter overload. info.finite marks where Qub is a bound at all (MATLAB
925 * writes Inf there, which JSON cannot carry).
926 */
927struct OpNpfqnBndBgt {
928 template <class T>
929 static Json run(Args& a) {
930 const std::vector<T> lambda = a.vector<T>("lambda");
931 const std::vector<std::vector<T> > mu = read_vector_list<T>(a, "mu");
932 const std::vector<std::vector<std::size_t> > sigma = read_index_lists(a, "sigma");
933 std::size_t J = 0;
934 if (a.has("J")) {
935 J = a.count("J");
936 } else {
937 for (const std::vector<std::size_t>& s : sigma)
938 for (std::size_t j : s) J = std::max(J, j + 1);
939 }
940 a.done();
941 const npfqn::BndBgt<T> r = npfqn::npfqn_bnd_bgt(lambda, mu, sigma, J);
942 Json out;
943 out["Qub"] = encode_vectors(r.Qub);
944 Json info;
945 Json fin = Json::array();
946 for (bool b : r.finite) fin.push_back(b);
947 info["finite"] = fin;
948 info["gamma"] = encode_scalar(r.gamma);
949 info["Lmax"] = encode_scalar(r.Lmax);
950 info["L"] = encode_vectors(r.L);
951 info["V"] = encode_vector(r.V);
952 info["B"] = encode_scalar(r.B);
953 info["U"] = encode_scalar(r.U);
954 info["tailRatio"] = encode_scalar(r.tailRatio);
955 info["tailStep"] = encode_scalar(r.tailStep);
956 info["rho"] = encode_vector(r.rho);
957 info["rhoStation"] = encode_vector(r.rhoStation);
958 info["scale"] = encode_scalar(r.scale);
959 info["classType"] = encode_indices(r.classType);
960 info["classStage"] = encode_indices(r.classStage);
961 info["classStation"] = encode_indices(r.classStation);
962 out["info"] = info;
963 return out;
964 }
965};
966
967/**
968 * [ST,gamma,nservers,rho,scva,scvs,eta] =
969 * npfqn_nonexp_approx(method,sn,ST,V,SCV,T,U,gamma,nservers).
970 * The two fields the reference reads from sn are named as the port names them:
971 * isFCFS (sn.sched(i) == FCFS) and rates (sn.rates).
972 */
973struct OpNpfqnNonexpApprox {
974 template <class T>
975 static Json run(Args& a) {
976 a.unsupported("sn", "npfqn_nonexp_approx: pass the two sn fields the routine reads, "
977 "isFCFS (sn.sched == FCFS per station) and rates (sn.rates)");
978 const std::string method = a.text("method", "");
979 if (method.empty()) throw InputError("npfqn_nonexp_approx: missing required argument 'method'");
980 const std::vector<bool> isFCFS = read_flags(a, "isFCFS");
981 const Matrix<T> rates = a.matrix<T>("rates");
982 const Matrix<T> ST = a.matrix<T>("ST");
983 const Matrix<T> V = a.matrix<T>("V");
984 const Matrix<T> SCV = a.matrix<T>("SCV");
985 const Matrix<T> Tput = a.matrix<T>("T");
986 const Matrix<T> U = a.matrix<T>("U");
987 const std::vector<T> gamma = a.vector<T>("gamma");
988 const std::vector<T> nservers = a.vector<T>("nservers");
989 a.done();
990 const npfqn::NonexpApproxResult<T> r =
991 npfqn::npfqn_nonexp_approx(method, isFCFS, rates, ST, V, SCV, Tput, U, gamma, nservers);
992 Json out;
993 out["ST"] = encode_matrix(r.ST);
994 out["gamma"] = encode_vector(r.gamma);
995 out["nservers"] = encode_vector(r.nservers);
996 out["rho"] = encode_vector(r.rho);
997 out["scva"] = encode_vector(r.scva);
998 out["scvs"] = encode_vector(r.scvs);
999 out["eta"] = encode_vector(r.eta);
1000 return out;
1001 }
1002};
1003
1004/**
1005 * [X,Q,U,R] = npfqn_sqd(sn,N,calibrationMode,serverBlockingTime,neighborMode,
1006 * v1Policy,initialV1). The sn is replaced by the arrays the port takes (see
1007 * npfqn_sqd.h): ST, V, cap, isDelay, rt, stationToStateful (ONE-based, as in
1008 * sn) and nclasses. N is required: MATLAB's fallback reads sn.nclosedjobs.
1009 */
1010struct OpNpfqnSqd {
1011 template <class T>
1012 static Json run(Args& a) {
1013 a.unsupported("sn", "npfqn_sqd: pass the arrays the routine unpacks from sn: ST, V, cap, "
1014 "isDelay, rt, stationToStateful and nclasses (see npfqn_sqd.h)");
1015 const std::vector<T> ST = a.vector<T>("ST");
1016 const std::vector<T> V = a.vector<T>("V");
1017 const std::vector<T> cap = a.vector<T>("cap");
1018 const std::vector<bool> isDelay = read_flags(a, "isDelay");
1019 const Matrix<T> rt = a.matrix<T>("rt");
1020 const std::vector<std::size_t> s2s = read_indices(a, "stationToStateful");
1021 const std::size_t nclasses = a.count("nclasses");
1022 const int N = a.required_integer("N");
1023 npfqn::SqdOptions<T> opt;
1024 bool any = false;
1025 if (a.has("calibrationMode")) {
1026 opt.calibrationMode = a.required_integer("calibrationMode");
1027 any = true;
1028 }
1029 if (a.has("serverBlockingTime")) {
1030 opt.serverBlockingTime = a.boolean("serverBlockingTime", opt.serverBlockingTime);
1031 any = true;
1032 }
1033 if (a.has("neighborMode")) {
1034 const std::string m = a.text("neighborMode", "");
1035 if (m != "downstream" && m != "ownserver")
1036 throw InputError("npfqn_sqd: neighborMode must be 'downstream' or 'ownserver', got '" + m + "'");
1037 opt.ownServerNeighbor = (m == "ownserver");
1038 any = true;
1039 }
1040 if (a.has("v1Policy")) {
1041 const std::string m = a.text("v1Policy", "");
1042 if (m != "compound" && m != "fresh")
1043 throw InputError("npfqn_sqd: v1Policy must be 'compound' or 'fresh', got '" + m + "'");
1044 opt.freshV1 = (m == "fresh");
1045 any = true;
1046 }
1047 if (a.has("initialV1")) {
1048 opt.initialV1 = a.vector_or_empty<T>("initialV1");
1049 any = true;
1050 }
1051 a.done();
1052 const npfqn::SqdResult<T> r =
1053 any ? npfqn::npfqn_sqd(ST, V, cap, isDelay, rt, s2s, nclasses, N, opt)
1054 : npfqn::npfqn_sqd(ST, V, cap, isDelay, rt, s2s, nclasses, N);
1055 Json out;
1056 out["X"] = encode_vector(r.X);
1057 out["Q"] = encode_vector(r.Q);
1058 out["U"] = encode_vector(r.U);
1059 out["R"] = encode_vector(r.R);
1060 return out;
1061 }
1062};
1063
1064/** [Q,R,X,aux] = npfqn_dps_morrison(N,Z,S,w). */
1065struct OpNpfqnDpsMorrison {
1066 template <class T>
1067 static Json run(Args& a) {
1068 const std::vector<T> N = a.vector<T>("N");
1069 const std::vector<T> Z = a.vector<T>("Z");
1070 const std::vector<T> S = a.vector<T>("S");
1071 const std::vector<T> w = a.vector<T>("w");
1072 a.done();
1073 const npfqn::DpsMorrisonResult<T> r = npfqn::npfqn_dps_morrison(N, Z, S, w);
1074 Json out;
1075 out["Q"] = encode_vector(r.Q);
1076 out["R"] = encode_vector(r.R);
1077 out["X"] = encode_vector(r.X);
1078 Json aux;
1079 aux["rho"] = encode_scalar(r.rho);
1080 aux["a"] = encode_scalar(r.a);
1081#define LINE_MC_AUX(f) aux[#f] = encode_scalar(r.f);
1085 LINE_MC_AUX(cV)
1086#undef LINE_MC_AUX
1087 aux["sigma"] = encode_vector(r.sigma);
1088 aux["W"] = encode_vector(r.W);
1089 aux["Qlead"] = encode_vector(r.Qlead);
1090 aux["Rlead"] = encode_vector(r.Rlead);
1091 out["aux"] = aux;
1092 return out;
1093 }
1094};
1095
1096/**
1097 * result = npfqn_feedback_elim(P, rho, 'cs2', .., 'lambda', .., 'immediateOnly', ..).
1098 * An absent cs2 or lambda is MATLAB's empty option, which skips that output.
1099 */
1100struct OpNpfqnFeedbackElim {
1101 template <class T>
1102 static Json run(Args& a) {
1103 const Matrix<T> P = a.matrix<T>("P");
1104 const std::vector<T> rho = a.vector<T>("rho");
1105 const std::vector<T> cs2 = a.vector_or_empty<T>("cs2");
1106 const std::vector<T> lambda = a.vector_or_empty<T>("lambda");
1107 const bool hasImm = a.has("immediateOnly");
1108 const bool imm = hasImm ? a.boolean("immediateOnly", false) : false;
1109 a.done();
1110 const npfqn::FeedbackElimResult<T> r = hasImm ? npfqn::npfqn_feedback_elim(P, rho, cs2, lambda, imm)
1111 : npfqn::npfqn_feedback_elim(P, rho, cs2, lambda);
1112 Json res;
1113 res["feedbackProb"] = encode_vector(r.feedbackProb);
1114 res["visitInflation"] = encode_vector(r.visitInflation);
1115 if (!r.modifiedScv.empty()) res["modifiedScv"] = encode_vector(r.modifiedScv);
1116 if (!r.modifiedRates.empty()) res["modifiedRates"] = encode_vector(r.modifiedRates);
1117 res["modifiedRouting"] = encode_matrix(r.modifiedRouting);
1118 res["reductionExact"] = r.reductionExact;
1119 Json out;
1120 out["result"] = res;
1121 return out;
1122 }
1123};
1124
1125// ---------------------------------------------------------------------------
1126// snc: every envelope function returns [sigma, rho]
1127// ---------------------------------------------------------------------------
1128
1129Json env_json(const snc::Env& e) {
1130 Json out;
1131 out["sigma"] = e.sigma;
1132 out["rho"] = e.rho;
1133 return out;
1134}
1135
1136/** The (sigma, rho) pair of an envelope passed as two scalars. */
1137snc::Env read_env(Args& a, const char* s, const char* r) {
1138 snc::Env e;
1139 e.sigma = a.number<double>(s);
1140 e.rho = a.number<double>(r);
1141 return e;
1142}
1143
1144struct OpSncEnvPoisson {
1145 static Json run(Args& a) {
1146 const double lambda = a.number<double>("lambda"), theta = a.number<double>("theta");
1147 a.done();
1148 return env_json(snc::snc_env_poisson(lambda, theta));
1149 }
1150};
1151
1152struct OpSncEnvCpoisson {
1153 static Json run(Args& a) {
1154 const double lambda = a.number<double>("lambda"), mu = a.number<double>("mu");
1155 const double theta = a.number<double>("theta");
1156 a.done();
1157 return env_json(snc::snc_env_cpoisson(lambda, mu, theta));
1158 }
1159};
1160
1161/** [sigma,rho] = snc_env_tokenbucket(b, r, theta); theta is optional and only validated. */
1162struct OpSncEnvTokenbucket {
1163 static Json run(Args& a) {
1164 const double b = a.number<double>("b"), r = a.number<double>("r");
1165 const bool hasTheta = a.has("theta");
1166 const double theta = hasTheta ? a.number<double>("theta") : 0.0;
1167 a.done();
1168 return env_json(hasTheta ? snc::snc_env_tokenbucket(b, r, theta) : snc::snc_env_tokenbucket(b, r));
1169 }
1170};
1171
1172struct OpSncEnvMap {
1173 static Json run(Args& a) {
1174 const Matrix<double> D0 = a.matrix<double>("D0"), D1 = a.matrix<double>("D1");
1175 const double theta = a.number<double>("theta");
1176 a.done();
1177 return env_json(snc::snc_env_map(D0, D1, theta));
1178 }
1179};
1180
1181/** [sigma,rho] = snc_srv_rate(C, theta); theta is optional and only validated. */
1182struct OpSncSrvRate {
1183 static Json run(Args& a) {
1184 const double C = a.number<double>("C");
1185 const bool hasTheta = a.has("theta");
1186 const double theta = hasTheta ? a.number<double>("theta") : 0.0;
1187 a.done();
1188 return env_json(hasTheta ? snc::snc_srv_rate(C, theta) : snc::snc_srv_rate(C));
1189 }
1190};
1191
1192struct OpSncSrvExp {
1193 static Json run(Args& a) {
1194 const double mu = a.number<double>("mu"), theta = a.number<double>("theta");
1195 a.done();
1196 return env_json(snc::snc_srv_exp(mu, theta));
1197 }
1198};
1199
1200struct OpSncLeftover {
1201 static Json run(Args& a) {
1202 const snc::Env srv = read_env(a, "sigmaS", "rhoS");
1203 const snc::Env cross = read_env(a, "sigmaX", "rhoX");
1204 a.done();
1205 return env_json(snc::snc_leftover(srv, cross));
1206 }
1207};
1208
1209/** [sigma,rho] = snc_conv(sigma1,rho1,sigma2,rho2,theta,delta). */
1210struct OpSncConv {
1211 static Json run(Args& a) {
1212 const snc::Env s1 = read_env(a, "sigma1", "rho1");
1213 const snc::Env s2 = read_env(a, "sigma2", "rho2");
1214 const double theta = a.number<double>("theta");
1215 const bool hasDelta = a.has("delta");
1216 const double delta = hasDelta ? a.number<double>("delta") : 0.0;
1217 a.done();
1218 return env_json(hasDelta ? snc::snc_conv(s1, s2, theta, delta) : snc::snc_conv(s1, s2, theta));
1219 }
1220};
1221
1222struct OpSncOutput {
1223 static Json run(Args& a) {
1224 const snc::Env arv = read_env(a, "sigmaA", "rhoA");
1225 const snc::Env srv = read_env(a, "sigmaS", "rhoS");
1226 const double theta = a.number<double>("theta");
1227 a.done();
1228 return env_json(snc::snc_output(arv, srv, theta));
1229 }
1230};
1231
1232
1233/**
1234 * Q = ctmc_rand(n, seed). The uniforms come from mc::LcgUniform seeded by the
1235 * caller: its variates are dyadic rationals, so the exact arm returns the same
1236 * generator the double arm does, entry for entry. MATLAB draws from the session
1237 * RNG instead, so the matrices agree with it in law, not draw for draw.
1238 */
1239struct OpCtmcRand {
1240 template <class T>
1241 static Json run(Args& a) {
1242 const std::size_t n = a.count("n");
1243 mc::LcgUniform gen(required_seed(a, "ctmc_rand"));
1244 a.done();
1245 Json out;
1246 out["Q"] = encode_matrix(mc::ctmc_rand<T>(n, gen));
1247 return out;
1248 }
1249};
1250
1251/**
1252 * [soujt, sts] = ctmc_simulate(Q, pi0, n, seed). pi0 may be empty, which draws
1253 * the initial state the way the reference does; sts is 0-based.
1254 */
1255struct OpCtmcSimulate {
1256 template <class T>
1257 static Json run(Args& a) {
1258 const Matrix<T> Q = a.matrix<T>("Q");
1259 const std::vector<T> pi0 = a.vector_or_empty<T>("pi0");
1260 const std::size_t n = a.count("n");
1261 pfqn::McRng rng(required_seed(a, "ctmc_simulate"));
1262 a.done();
1263 const mc::CtmcPath<T> p = mc::ctmc_simulate(Q, pi0, n, rng);
1264 Json out;
1265 out["soujt"] = encode_vector(p.sojourn);
1266 out["sts"] = encode_indices(p.states);
1267 return out;
1268 }
1269};
1270
1271} // namespace
1272
1274 // mc
1275 t["ctmc_stochcomp"] = make<OpCtmcStochcomp>();
1276 t["ctmc_uniformization"] = make<NeedsTranscendental<OpCtmcUniformization> >();
1277 t["ctmc_timeaverage"] = make<NeedsTranscendental<OpCtmcTimeaverage> >();
1278 t["ctmc_accumulated"] = make<NeedsTranscendental<OpCtmcAccumulated> >();
1283 t["ctmc_gth"] = make<OpGth<false> >();
1284 t["dtmc_gth"] = make<OpGth<true> >();
1285 t["ctmc_cesaro"] = make<OpCesaro<false> >();
1286 t["dtmc_cesaro"] = make<OpCesaro<true> >();
1287 t["ctmc_ssd"] = make<DoubleOnly<OpSsd> >();
1288 t["dtmc_ssd"] = make<DoubleOnly<OpSsd> >();
1289 t["ctmc_memory_gate"] = make<NeedsTranscendental<OpCtmcMemoryGate> >();
1290 t["ctmc_isfeasible"] = make<OpCtmcIsfeasible>();
1291 t["ctmc_hitting_time"] = make<NeedsTranscendental<OpCtmcHittingTime> >();
1292 // The registry lists Exact for dtmc_hitting_time, but the port static_asserts
1293 // an arithmetic with an infinity (an unreachable state's time is Inf), so the
1294 // exact arm is refused with that reason instead of failing to compile.
1295 t["dtmc_hitting_time"] = make<NeedsTranscendental<OpDtmcHittingTime> >();
1296 t["dtmc_transient"] = make<OpDtmcTransient>();
1297 t["ctmc_passage_ph"] = make<OpCtmcPassagePh>();
1298 t["ctmc_passage_lst"] = make<NeedsTranscendental<OpCtmcPassageLst> >();
1299 t["ctmc_passage_moments"] = make<NeedsTranscendental<OpCtmcPassageMoments> >();
1300 t["ctmc_passage_time"] = make<NeedsTranscendental<OpCtmcPassageTime> >();
1301 t["ctmc_passage_taboo"] = make<NeedsTranscendental<OpCtmcPassageTaboo> >();
1302 t["ctmc_passage_taboo_moments"] = make<NeedsTranscendental<OpCtmcPassageTabooMoments> >();
1303 // dpfqn
1304 t["dpfqn_nc"] = make<OpDpfqnNc>();
1305 t["dpfqn_ncld"] = make<OpDpfqnNcld>();
1306 // npfqn
1307 t["npfqn_traffic_idc"] = make<OpNpfqnTrafficIdc>();
1308 t["npfqn_traffic_split_cs"] = make<OpNpfqnTrafficSplitCs>();
1309 t["npfqn_traffic_merge_cs"] = make<OpNpfqnTrafficMergeCs>();
1310 t["npfqn_traffic_merge"] = make<NeedsTranscendental<OpNpfqnTrafficMerge> >();
1311 t["npfqn_rqna_weight"] = make<NeedsTranscendental<OpNpfqnRqnaWeight> >();
1312 t["npfqn_traffic_rqt"] = make<NeedsTranscendental<OpNpfqnTrafficRqt> >();
1313 t["npfqn_bnd_bpt"] = make<OpNpfqnBndBpt>();
1314 t["npfqn_bnd_bgt"] = make<OpNpfqnBndBgt>();
1315 t["npfqn_nonexp_approx"] = make<NeedsTranscendental<OpNpfqnNonexpApprox> >();
1316 t["npfqn_sqd"] = make<NeedsTranscendental<OpNpfqnSqd> >();
1317 t["npfqn_dps_morrison"] = make<NeedsTranscendental<OpNpfqnDpsMorrison> >();
1318 t["npfqn_feedback_elim"] = make<OpNpfqnFeedbackElim>();
1319 // snc (double-only kernels)
1320 t["snc_env_poisson"] = make<DoubleOnly<OpSncEnvPoisson> >();
1321 t["snc_env_cpoisson"] = make<DoubleOnly<OpSncEnvCpoisson> >();
1322 t["snc_env_tokenbucket"] = make<DoubleOnly<OpSncEnvTokenbucket> >();
1323 t["snc_env_map"] = make<DoubleOnly<OpSncEnvMap> >();
1324 t["snc_srv_rate"] = make<DoubleOnly<OpSncSrvRate> >();
1325 t["snc_srv_exp"] = make<DoubleOnly<OpSncSrvExp> >();
1326 t["snc_leftover"] = make<DoubleOnly<OpSncLeftover> >();
1327 t["snc_conv"] = make<DoubleOnly<OpSncConv> >();
1328 t["snc_output"] = make<DoubleOnly<OpSncOutput> >();
1329 t["ctmc_rand"] = make<OpCtmcRand>();
1330 t["ctmc_simulate"] = make<NeedsTranscendental<OpCtmcSimulate> >();
1331}
1332
1334 const std::string sn =
1335 "it takes a NetworkStruct; build the model and run line-cli -s ctmc -a <analysis> "
1336 "on it instead.";
1337 t["ctmc_ssg"] = sn;
1338 t["ctmc_ssg_reachability"] = sn;
1339 t["ctmc_state_space_logsize"] = sn;
1340 t["npfqn_traffic_split_rr"] =
1341 "it takes a NetworkStruct; build the model and run line-cli -s <solver> -a <analysis> "
1342 "on it instead.";
1343 t["npfqn_traffic_idc_at"] =
1344 "it evaluates two caller-supplied IDC functions (a0IdcFun and sIdcFun), which are "
1345 "callables with no JSON form.";
1346 const std::string env =
1347 "its arrival and service envelopes are functions of theta (e.g. snc_env_poisson as a "
1348 "handle), callables with no JSON form; the envelope values at one theta are exposed "
1349 "through the snc_env_* and snc_srv_* entries.";
1350 t["snc_bound_backlog"] = env;
1351 t["snc_bound_delay"] = env;
1352 t["snc_perc_backlog"] = env;
1353 t["snc_perc_delay"] = env;
1354 t["snc_mean_backlog"] = env;
1355 t["snc_mean_delay"] = env;
1356 t["snc_thetaopt"] =
1357 "its objective is a function of theta passed as a callable, which has no JSON form.";
1358}
1359
1360} // namespace reg
1361} // namespace line
Internal to the –api dispatcher: what a per-domain translation unit needs to add entries to the table...
#define LINE_MC_AUX(f)
InputError(const std::string &what)
Definition error.h:105
Named-argument reader over the parsed JSON object.
Definition api_json.h:391
Accumulated state probabilities L(t) = int_0^t pi(s) ds of a CTMC by uniformisation (Reibman and Triv...
Classical stationary iterative solvers for pi Q = 0, sum(pi) = 1: the power method on the uniformised...
Grassmann-Taksar-Heyman (GTH) elimination for the stationary vector, and the limit matrix J = lim exp...
Feasibility predicates for generators and stochastic matrices.
Host-aware memory pre-gate for SolverCTMC.
First passage times into a target STATE SET, for Markov and semi-Markov chains.
Random infinitesimal generator of a CTMC.
Sample path of a continuous-time Markov chain given its generator.
Stochastically stable distribution of a perturbed Markov chain (Wicks 2009).
Transient distribution of a CTMC by uniformization (Jensen's method), and the time-averaged distribut...
Normalizing constants of a discrete-time closed cycle of Bernoulli servers.
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
Discrete-time transient distributions, hitting times and uniformization.
DtNcLdResult< T > dpfqn_ncld(const std::vector< std::vector< T > > &P, std::size_t N)
Theorem 3.2 for a cycle whose service probabilities depend on the local queue length.
Definition dpfqn_nc.h:205
DtNcResult< T > dpfqn_nc(const std::vector< T > &p, std::size_t N)
Propositions 3.18 and 3.19 for a cycle with state independent service probabilities.
Definition dpfqn_nc.h:113
double z(double v)
Definition lqn_focus.h:261
double delta(double t)
std::vector< std::complex< double > > ctmc_passage_lst(const Matrix< T > &Q, const std::vector< T > &pi0, const std::vector< std::size_t > &target, const std::vector< std::complex< double > > &s)
L(s) = alpha (sI-S)^{-1} s0 + atom at the (complex) points s.
PassageMoments< T > ctmc_passage_moments(const Matrix< T > &Q, const std::vector< T > &pi0, const std::vector< std::size_t > &target, std::size_t nmax=1)
Moments of order 1..nmax of the first passage time into target.
Matrix< T > ctmc_cesaro(const Matrix< T > &Q)
Limit matrix J = lim expm(Q t) of any finite CTMC (Agaev-Chebotarev 2000).
Definition ctmc_gth.h:180
std::vector< double > ctmc_ssd(const Matrix< double > &Cin, const Matrix< double > &Rin)
Stochastically stable distribution, rates C(i,j) eps^R(i,j) (Wicks 2009).
Definition ctmc_ssd.h:216
StochCompResult< T > ctmc_stochcomp(const Matrix< T > &Q, const std::vector< std::size_t > &I)
Definition dtmc_solve.h:158
CtmcGateResult ctmc_memory_gate(double log_nstates, bool force=false, double safety_fraction=CTMC_DEFAULT_SAFETY_FRACTION, bool verbose=false)
Decide whether a state space of log-size log_nstates can be solved here.
UniformizationResult< T > ctmc_uniformization(const std::vector< T > &pi0, const Matrix< T > &Q, const T &t, double tol=1e-12, long maxiter=-1)
Transient distribution of a CTMC by uniformization (Jensen's method), and the time-averaged distribut...
PassageCurve< T > ctmc_passage_time(const Matrix< T > &Q, const std::vector< T > &pi0, const std::vector< std::size_t > &target, const std::vector< double > &tset, const std::string &method="expm", const std::string &lti_method="euler")
CDF and density of the first passage time on the grid tset: F(t) = 1 - alpha exp(St) 1 and f(t) = alp...
Matrix< T > ctmc_rand(std::size_t n, Gen &gen)
Random infinitesimal generator of a CTMC.
Definition ctmc_rand.h:71
std::vector< T > dtmc_hitting_time(const Matrix< T > &P, const std::vector< std::size_t > &target)
Expected number of steps to reach the target set, zero on the target set itself.
TimeAverageResult< T > ctmc_timeaverage(const std::vector< T > &pi0, const Matrix< T > &Q, const T &t, double tol=1e-12, long maxiter=-1)
Time-averaged distribution (1/t) int_0^t pi(u) du, plus pi(t) itself.
GthResult< T > ctmc_gth_flag(const Matrix< T > &Q)
Stationary vector of a CTMC by GTH elimination, with a flag.
Definition ctmc_gth.h:135
PassagePh< T > ctmc_passage_ph(const Matrix< T > &Q, const std::vector< T > &pi0, const std::vector< std::size_t > &target)
Phase-type representation of the first passage time from pi0 into target.
bool ctmc_isfeasible(const Matrix< T > &Q, const T &tol)
True when Q is square, has nonnegative off-diagonals, nonpositive diagonal and zero row sums.
constexpr long CLASSICAL_DEFAULT_MAXITER
PassageTabooMoments< T > ctmc_passage_taboo_moments(const Matrix< T > &Q, const std::vector< T > &pi0, const std::vector< std::size_t > &target, const std::vector< std::size_t > &taboo, std::size_t nmax=1)
Moments of order 1..nmax of the first passage time into target, CONDITIONAL on entering it before tab...
CtmcPath< T > ctmc_simulate(const Matrix< T > &Q, const std::vector< T > &pi0, std::size_t n, pfqn::McRng &rng)
Simulate n steps of the CTMC with generator Q.
PassageTaboo< T > ctmc_passage_taboo(const Matrix< T > &Q, const std::vector< T > &pi0, const std::vector< std::size_t > &target, const std::vector< std::size_t > &taboo, const std::vector< double > &tset=std::vector< double >(), const std::string &method="expm", const std::string &lti_method="euler")
First passage into target under the taboo set taboo, the reference's ctmc_passage_taboo.
Matrix< T > dtmc_transient(const Matrix< T > &P, const std::vector< T > &pi0, std::size_t steps)
Trajectory of the law over steps transitions, row k holding pi0 P^k.
std::vector< T > ctmc_hitting_time(const Matrix< T > &Q, const std::vector< std::size_t > &target)
Mean time to reach any state in target from each state of a CTMC.
AccumulatedResult< T > ctmc_accumulated(const std::vector< T > &pi0, const Matrix< T > &Q, const std::vector< T > &t, double tol=1e-12, long maxiter=-1)
Accumulated state probabilities int_0^t pi(s) ds at each time point.
constexpr double CLASSICAL_DEFAULT_TOL
Merge
Merge rule, MATLAB's config.merge.
@ Interpos
'interpos', lumped interleaving of per-flow M3PP(2, m) fits
@ Default
'default', identical to 'super'
@ Mixture
'mixture', superpose then refit with mmap_mixture_fit_mmap
TrafficIdcContext< T > npfqn_traffic_idc(const std::vector< T > &lambda0, const Matrix< T > &P, const std::vector< T > &c2a0, const std::vector< T > &mu, const std::vector< T > &cs2, const TrafficIdcCorrections &corrections)
Traffic variability equations of the Robust Queueing Network Analyzer (W.
T npfqn_rqna_weight(const T &t)
Canonical reflected-Brownian-motion correlation weight w*(t) used by the Robust Queueing Network Anal...
SqdResult< T > npfqn_sqd(const std::vector< T > &ST, const std::vector< T > &V, const std::vector< T > &cap, const std::vector< bool > &isDelay, const Matrix< T > &rt, const std::vector< std::size_t > &stationToStateful, std::size_t nclasses, int N, const SqdOptions< T > &opt)
Smith Queue Decomposition (SQD): approximate MVA for closed networks under Blocking-After-Service (ma...
Definition npfqn_sqd.h:248
NonexpApproxResult< T > npfqn_nonexp_approx(const std::string &method, const std::vector< bool > &isFCFS, const Matrix< T > &rates, const Matrix< T > &ST, const Matrix< T > &V, const Matrix< T > &SCV, const Matrix< T > &Tput, const Matrix< T > &U, const std::vector< T > &gamma, const std::vector< T > &nservers)
Handler for non-exponential service and arrival processes in AMVA and NC.
mam::Mmap< T > npfqn_traffic_merge_cs(const std::vector< mam::Mmap< T > > &flows, const Matrix< T > &prob, Merge merge=Merge::Default)
Merge flows that switch class on departure.
std::vector< Mmap< T > > npfqn_traffic_split_cs(const Mmap< T > &MMAP, const Matrix< T > &P)
Splitting of a marked MAP departure flow with class switching.
FeedbackElimResult< T > npfqn_feedback_elim(const Matrix< T > &P, const std::vector< T > &rho, const std::vector< T > &cs2=std::vector< T >(), const std::vector< T > &lambda=std::vector< T >(), bool immediateOnly=false)
Near-immediate feedback elimination for the robust queueing network analyzer.
BndBpt< T > npfqn_bnd_bpt(const std::vector< T > &lambda0, const std::vector< T > &mu, const Matrix< T > &P, const std::vector< std::size_t > &stationOf, const std::vector< T > &c)
First-order linear-programming relaxation of the achievable region of a multiclass open Markovian que...
TrafficRqt< T > npfqn_traffic_rqt(const std::vector< T > &lambda0, const std::vector< T > &Gamma0, const std::vector< T > &alpha0, const Matrix< T > &F)
Effective arrival processes of a network under the Robust Queueing calculus.
Compress
Post-merge compression, MATLAB's config.compress.
@ Default
'default', mmap_compress with its own default method
std::vector< Matrix< T > > Mmap
An MMAP as the MATLAB cell {D0, D1, D1^(1), ..., D1^(R)}.
BndBgt< T > npfqn_bnd_bgt(const std::vector< T > &lambda, const std::vector< std::vector< T > > &mu, const std::vector< std::vector< std::size_t > > &sigma, std::size_t J)
Piecewise-linear Lyapunov UPPER bound on the steady-state queue lengths of a multitype (deterministic...
DpsMorrisonResult< T > npfqn_dps_morrison(const std::vector< T > &N, const std::vector< T > &Z, const std::vector< T > &S, const std::vector< T > &w)
Evaluates Morrison's two-term approximation.
mam::Mmap< T > npfqn_traffic_merge(const std::vector< mam::Mmap< T > > &flows, const MergeConfig &config=MergeConfig())
Merge a list of MMAPs carrying the same classes.
std::mt19937_64 McRng
The generator type every Monte Carlo entry point in this tree accepts.
nlohmann::json Json
Definition api_json.h:63
std::uint64_t required_seed(Args &a, const std::string &fn, const char *spelled="seed")
The seed of a function whose port draws from a caller-owned generator.
Json encode_vectors(const std::vector< std::vector< T > > &v)
A list of vectors, the shape a per-class or per-segment result returns.
Definition api_json.h:366
Matrix< T > matrix_from_json(const Json &j, const std::string &where)
A matrix from a JSON value: 2-D array as rows, 1-D array as a row vector, scalar as 1x1,...
Definition api_json.h:223
Json encode_matrices(const std::vector< Matrix< T > > &v)
A list of matrices, the shape the MMAP/BMAP families return as {D0,D1,...}.
Definition api_json.h:358
void register_library_only_mc(LibraryOnlyTable &t)
T number_from_json(const Json &j, const std::string &where)
Definition api_json.h:210
std::vector< T > vector_from_json(const Json &j, const std::string &where)
A flat numeric vector: 1-D array, or a 1-row / 1-column 2-D array.
Definition api_json.h:255
Json encode_vector(const std::vector< T > &v)
Definition api_json.h:333
Json encode_matrix(const Matrix< T > &m)
Definition api_json.h:340
std::string 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
void register_api_mc(DispatchTable &t)
Json encode_scalar(const T &v)
Definition api_json.h:328
Json encode_count(std::size_t n)
A count.
Definition api_json.h:378
std::map< std::string, std::string > LibraryOnlyTable
name -> why the function has no named-JSON-argument form.
std::map< std::string, Invoker > DispatchTable
Env snc_leftover(const Env &srv, const Env &cross)
Leftover service envelope under blind (arbitrary) multiplexing.
Env snc_conv(const Env &s1, const Env &s2, double theta, double delta=-1.0)
Min-plus convolution of two service envelopes (tandem concatenation).
Definition snc_conv.h:49
Env snc_output(const Env &arv, const Env &srv, double theta)
Output (departure) arrival envelope of a flow leaving a server.
Definition snc_output.h:40
Env snc_srv_exp(double mu, double theta)
MGF service envelope of an exponential server, in JOB units.
Definition snc_srv_exp.h:46
Env snc_env_map(const Matrix< double > &D0, const Matrix< double > &D1, double theta)
MGF arrival envelope of a MAP/MMPP flow with unit-size jobs.
Definition snc_env_map.h:56
Env snc_srv_rate(double C)
MGF service envelope of a constant-rate work-conserving server.
Env snc_env_tokenbucket(double b, double r)
Deterministic token-bucket arrival envelope.
Env snc_env_poisson(double lambda, double theta)
MGF arrival envelope of a Poisson flow with unit-size jobs.
Env snc_env_cpoisson(double lambda, double mu, double theta)
MGF arrival envelope of a compound Poisson flow with Exp job sizes.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Piecewise-linear Lyapunov UPPER bound on the steady-state queue lengths of a multitype (deterministic...
First-order linear-programming relaxation of the achievable region of a multiclass open Markovian que...
Two-term heavy-usage asymptotic approximation for a closed queueing network with one infinite-server ...
Near-immediate feedback elimination for the robust queueing network analyzer.
Handler for non-exponential service and arrival processes in AMVA and NC.
Canonical reflected-Brownian-motion correlation weight w*(t) used by the Robust Queueing Network Anal...
Smith Queue Decomposition (SQD): approximate MVA for closed networks under Blocking-After-Service (ma...
Traffic variability equations of the Robust Queueing Network Analyzer (W.
Superposition of several marked arrival flows into one.
Merge of marked arrival flows with class switching.
Effective arrival processes of a network under the Robust Queueing calculus.
Splitting of a marked MAP departure flow with class switching.
Min-plus convolution of two service envelopes (tandem concatenation).
MGF arrival envelope of a compound Poisson flow with Exp job sizes.
MGF arrival envelope of a MAP/MMPP flow with unit-size jobs.
MGF arrival envelope of a Poisson flow with unit-size jobs.
Deterministic token-bucket arrival envelope.
Leftover service envelope under blind (arbitrary) multiplexing.
Output (departure) arrival envelope of a flow leaving a server.
MGF service envelope of an exponential server, in JOB units.
MGF service envelope of a constant-rate work-conserving server.