LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
api_dispatch_trace.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 'trace' group (see api_dispatch_detail.h):
6 * the trace, mtrace and infer domains.
7 *
8 * ARGUMENT AND RESULT NAMES are the MATLAB ones: a trace is a JSON vector, a
9 * marked trace a vector plus a label vector, and the results carry the MATLAB
10 * output names. Where the port leaves a statistic UNDEFINED (a class with no
11 * sample, which MATLAB reports as 0/0 = NaN) the entry is written as JSON null
12 * at every arithmetic, rather than as whatever the port left in the slot.
13 *
14 * POSITIONAL OPTIONALS. A MATLAB positional optional maps onto a C++ default
15 * argument. An absent optional calls the shorter overload, so no default is
16 * restated here; a later optional given without the earlier ones is refused,
17 * because reaching it would mean inventing the value of the earlier one.
18 */
19
20#include "api_dispatch_detail.h"
21
22#include <array>
23#include <cctype>
24#include <cmath>
25#include <cstddef>
26#include <functional>
27#include <limits>
28#include <string>
29#include <vector>
30
76
77namespace line {
78namespace reg {
79
80namespace {
81
82// ---------------------------------------------------------------------------
83// Shared readers and encoders
84// ---------------------------------------------------------------------------
85
86Json encode_longs(const std::vector<long>& v) {
87 Json a = Json::array();
88 for (long x : v) a.push_back(static_cast<long long>(x));
89 return a;
90}
91
92Json encode_long_matrix(const Matrix<long>& m) {
93 Json a = Json::array();
94 for (std::size_t i = 0; i < m.rows(); ++i) {
95 Json row = Json::array();
96 for (std::size_t k = 0; k < m.cols(); ++k) row.push_back(static_cast<long long>(m(i, k)));
97 a.push_back(row);
98 }
99 return a;
100}
101
102Json encode_sizes(const std::vector<std::size_t>& v) {
103 Json a = Json::array();
104 for (std::size_t x : v) a.push_back(encode_count(x));
105 return a;
106}
107
108/** An undefined statistic (no sample behind it) is null, at every arithmetic. */
109template <class T>
110Json encode_defined(const T& v, long count) {
111 return count == 0 ? Json(nullptr) : encode_scalar(v);
112}
113
114template <class T>
115Json encode_vector_defined(const std::vector<T>& v, const std::vector<long>& count) {
116 Json a = Json::array();
117 for (std::size_t k = 0; k < v.size(); ++k) a.push_back(encode_defined(v[k], count[k]));
118 return a;
119}
120
121template <class T>
122Json encode_matrix_defined(const Matrix<T>& m, const Matrix<long>& count) {
123 Json a = Json::array();
124 for (std::size_t i = 0; i < m.rows(); ++i) {
125 Json row = Json::array();
126 for (std::size_t k = 0; k < m.cols(); ++k) row.push_back(encode_defined(m(i, k), count(i, k)));
127 a.push_back(row);
128 }
129 return a;
130}
131
132std::string where_of(const std::string& fn, const char* key) { return fn + ": " + key; }
133
134std::vector<long> to_longs(const std::vector<int>& v) { return std::vector<long>(v.begin(), v.end()); }
135
136/** Nonnegative integers, e.g. moment orders or 0-based-or-sink state labels. */
137std::vector<std::size_t> nonneg_sizes(const std::vector<int>& v, const std::string& where) {
138 std::vector<std::size_t> out;
139 out.reserve(v.size());
140 for (int x : v) {
141 if (x < 0) throw InputError(where + ": entries must be nonnegative, got " + std::to_string(x));
142 out.push_back(static_cast<std::size_t>(x));
143 }
144 return out;
145}
146
147std::vector<unsigned> nonneg_unsigned(const std::vector<int>& v, const std::string& where) {
148 const std::vector<std::size_t> s = nonneg_sizes(v, where);
149 return std::vector<unsigned>(s.begin(), s.end());
150}
151
152/** MATLAB 1-based class labels to the port's 0-based ones. */
153std::vector<std::size_t> one_based_to_zero(const std::vector<int>& v, const std::string& where) {
154 std::vector<std::size_t> out;
155 out.reserve(v.size());
156 for (int x : v) {
157 if (x < 1) throw InputError(where + ": class labels are 1-based, got " + std::to_string(x));
158 out.push_back(static_cast<std::size_t>(x - 1));
159 }
160 return out;
161}
162
163std::vector<std::size_t> one_based_sizes(const std::vector<int>& v, const std::string& where) {
164 std::vector<std::size_t> out;
165 out.reserve(v.size());
166 for (int x : v) {
167 if (x < 1) throw InputError(where + ": class labels are 1-based, got " + std::to_string(x));
168 out.push_back(static_cast<std::size_t>(x));
169 }
170 return out;
171}
172
173/** A number where JSON null stands for `fill` (MATLAB's NaN or Inf placeholder). */
174template <class T>
175T number_or_fill(const Json& j, const std::string& where, const T& fill) {
176 return j.is_null() ? fill : number_from_json<T>(j, where);
177}
178
179/**
180 * A matrix like matrix_from_json, but with JSON null read as `fill`. The JSON
181 * encoder writes a double NaN as null, so this is how a NaN placeholder
182 * (MATLAB's 'not observed', 'not applicable') crosses the boundary.
183 */
184template <class T>
185Matrix<T> matrix_or_fill(const Json& j, const std::string& where, const T& fill) {
186 if (!j.is_array()) {
187 Matrix<T> m(1, 1);
188 m(0, 0) = number_or_fill<T>(j, where, fill);
189 return m;
190 }
191 if (j.empty()) return Matrix<T>();
192 if (j[0].is_array()) {
193 const std::size_t rows = j.size(), cols = j[0].size();
194 for (std::size_t i = 0; i < rows; ++i)
195 if (!j[i].is_array() || j[i].size() != cols)
196 throw InputError(where + ": row " + std::to_string(i) + " is not an array of " +
197 std::to_string(cols) + " entries");
198 if (cols == 0) return Matrix<T>();
199 Matrix<T> m(rows, cols);
200 for (std::size_t i = 0; i < rows; ++i)
201 for (std::size_t k = 0; k < cols; ++k) m(i, k) = number_or_fill<T>(j[i][k], where, fill);
202 return m;
203 }
204 Matrix<T> m(1, j.size());
205 for (std::size_t k = 0; k < j.size(); ++k) m(0, k) = number_or_fill<T>(j[k], where, fill);
206 return m;
207}
208
209/** MATLAB's reshape(X, 1, numel(X)): column-major flattening of any shape. */
210template <class T>
211std::vector<T> column_major(const Matrix<T>& m) {
212 std::vector<T> v;
213 v.reserve(m.rows() * m.cols());
214 for (std::size_t c = 0; c < m.cols(); ++c)
215 for (std::size_t r = 0; r < m.rows(); ++r) v.push_back(m(r, c));
216 return v;
217}
218
219std::string lower(std::string s) {
220 for (char& c : s) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
221 return s;
222}
223
224std::string string_of(const Json& j, const std::string& where) {
225 if (!j.is_string())
226 throw InputError(where + ": expected a string, got " + std::string(j.type_name()));
227 return j.get<std::string>();
228}
229
230std::vector<std::string> strings_of(const Json& j, const std::string& where) {
231 if (!j.is_array()) throw InputError(where + ": expected a list of strings");
232 std::vector<std::string> out;
233 for (const Json& e : j) out.push_back(string_of(e, where));
234 return out;
235}
236
237/** A flag stored as a JSON boolean or 0/1. */
238bool flag_of(const Json& j, const std::string& where) {
239 if (j.is_boolean()) return j.get<bool>();
240 const std::vector<int> v = int_vector_from_json(j, where);
241 if (v.size() != 1 || (v[0] != 0 && v[0] != 1))
242 throw InputError(where + ": expected true, false, 0 or 1");
243 return v[0] != 0;
244}
245
246/**
247 * The MATLAB 6 x (K+1) cell array `data` of the inference traces, of which the
248 * references read row 3 (arrival times, ms) and row 4 (response times) of the
249 * first K columns. Row-major JSON: data[row][column], each cell a vector.
250 */
251template <class T>
252void read_trace_cells(const Json& data, const std::string& where, std::vector<std::vector<T>>& at_ms,
253 std::vector<std::vector<T>>& rt) {
254 if (!data.is_array() || data.size() < 4)
255 throw InputError(where + ": expected the 6 x (K+1) cell array, as a list of rows; rows 3 "
256 "(arrival times, ms) and 4 (response times) are read");
257 const std::size_t cols = data[0].is_array() ? data[0].size() : 0;
258 for (std::size_t r = 0; r < data.size(); ++r)
259 if (!data[r].is_array() || data[r].size() != cols)
260 throw InputError(where + ": row " + std::to_string(r + 1) + " must have " +
261 std::to_string(cols) + " cells, like row 1");
262 if (cols < 2) throw InputError(where + ": at least one class column plus the trailing column");
263 const std::size_t K = cols - 1;
264 at_ms.assign(K, std::vector<T>());
265 rt.assign(K, std::vector<T>());
266 for (std::size_t k = 0; k < K; ++k) {
267 at_ms[k] = vector_from_json<T>(data[2][k], where + "{3," + std::to_string(k + 1) + "}");
268 rt[k] = vector_from_json<T>(data[3][k], where + "{4," + std::to_string(k + 1) + "}");
269 }
270}
271
272// ---------------------------------------------------------------------------
273// trace: one trace in, one scalar out
274// ---------------------------------------------------------------------------
275
276#define LINE_TRACE_SCALAR_OP(Name, fn, outkey) \
277 struct Name { \
278 template <class T> \
279 static Json run(Args& a) { \
280 const std::vector<T> S = a.vector<T>("S"); \
281 a.done(); \
282 Json out; \
283 out[outkey] = encode_scalar(line::trace::fn<T>(S)); \
284 return out; \
285 } \
286 };
287
288LINE_TRACE_SCALAR_OP(OpTraceMean, trace_mean, "m")
289LINE_TRACE_SCALAR_OP(OpTraceVar, trace_var, "v")
290LINE_TRACE_SCALAR_OP(OpTraceScv, trace_scv, "scv")
291LINE_TRACE_SCALAR_OP(OpTraceSkew, trace_skew, "skew")
292LINE_TRACE_SCALAR_OP(OpTraceIdc, trace_idc, "IDC")
293#undef LINE_TRACE_SCALAR_OP
294
295struct OpTraceAcf {
296 template <class T>
297 static Json run(Args& a) {
298 const std::vector<T> S = a.vector<T>("S");
299 const bool has_lags = a.has("lags");
300 const std::vector<int> lags = a.ints_or_empty("lags");
301 a.done();
302 Json out;
303 out["rho"] = encode_vector(has_lags ? line::trace::trace_acf<T>(S, lags)
305 return out;
306 }
307};
308
309struct OpTraceBicov {
310 template <class T>
311 static Json run(Args& a) {
312 const std::vector<T> S = a.vector<T>("S");
313 const std::vector<int> grid = a.ints("GRID");
314 a.done();
315 const line::trace::TraceBicovResult<T> r = line::trace::trace_bicov<T>(S, grid);
316 Json lags = Json::array();
317 for (const std::vector<int>& l : r.lags) lags.push_back(encode_ints(l));
318 Json out;
319 out["BiCov"] = encode_vector(r.bicov);
320 out["BiCovLags"] = lags;
321 return out;
322 }
323};
324
325struct OpTraceGamma {
326 template <class T>
327 static Json run(Args& a) {
328 const std::vector<T> S = a.vector<T>("T");
329 const bool has_limit = a.has("limit");
330 const int limit = has_limit ? a.required_integer("limit") : 0;
331 a.opt("limit");
332 a.done();
333 const line::trace::TraceGammaResult<T> r =
334 has_limit ? line::trace::trace_gamma<T>(S, static_cast<long>(limit))
335 : line::trace::trace_gamma<T>(S);
336 Json out;
337 out["GAMMA"] = encode_scalar(r.gamma);
338 out["RHO0"] = encode_scalar(r.rho0);
339 out["RESIDUALS"] = encode_scalar(r.residuals);
340 return out;
341 }
342};
343
344struct OpTraceIat2bins {
345 template <class T>
346 static Json run(Args& a) {
347 const std::vector<T> S = a.vector<T>("S");
348 const T scale = a.number<T>("scale");
349 a.done();
350 const line::trace::TraceBinsResult r = line::trace::trace_iat2bins<T>(S, scale);
351 Json out;
352 out["C"] = encode_longs(r.counts);
353 out["bC"] = encode_longs(r.membership);
354 return out;
355 }
356};
357
358struct OpTraceIat2counts {
359 template <class T>
360 static Json run(Args& a) {
361 const std::vector<T> S = a.vector<T>("S");
362 const T scale = a.number<T>("scale");
363 a.done();
364 Json out;
365 out["C"] = encode_longs(line::trace::trace_iat2counts<T>(S, scale));
366 return out;
367 }
368};
369
370/**
371 * trace_idi(S, kset, OPTION, n). OPTION 'aggregate' with n is the port's
372 * aggregate_n; 'aggregate-mix' is not ported (see trace_idi.h). The kset
373 * default of the reference (min(1000, ceil(n/30))) has no overload in the port,
374 * so kset is required rather than restated here.
375 */
376struct OpTraceIdi {
377 template <class T>
378 static Json run(Args& a) {
379 const std::vector<T> S = a.vector<T>("S");
380 const std::vector<long> kset = to_longs(a.ints("kset"));
381 const bool has_option = a.has("OPTION");
382 const std::string option = lower(a.text("OPTION", std::string()));
383 const bool has_n = a.has("n");
384 a.opt("n");
385 line::trace::TraceIdiResult<T> r;
386 if (!has_option) {
387 if (has_n)
388 throw InputError("trace_idi: 'n' is the aggregation count of OPTION 'aggregate'; "
389 "give OPTION too");
390 a.done();
391 r = line::trace::trace_idi<T>(S, kset);
392 } else if (option == "aggregate") {
393 if (!has_n) throw InputError("trace_idi: OPTION 'aggregate' needs 'n'");
394 const int n = a.required_integer("n");
395 if (n < 1) throw InputError("trace_idi: 'n' must be a positive integer");
396 a.done();
397 r = line::trace::trace_idi<T>(S, kset, static_cast<long>(n));
398 } else if (option == "aggregate-mix") {
399 throw UnsupportedError(ErrorKind{"JsonApi"},
400 "trace_idi: OPTION 'aggregate-mix' is not ported; the reference branch keeps "
401 "only the statistic of its last partition (see trace_idi.h)");
402 } else {
403 throw InputError("trace_idi: OPTION must be 'aggregate', got '" + option + "'");
404 }
405 Json out;
406 out["IDIk"] = encode_vector(r.idi);
407 out["support"] = encode_longs(r.support);
408 return out;
409 }
410};
411
412struct OpTraceJoint {
413 template <class T>
414 static Json run(Args& a) {
415 const std::vector<T> S = a.vector<T>("S");
416 const std::vector<int> lag = a.ints("lag");
417 const std::vector<unsigned> order = nonneg_unsigned(a.ints("order"), "trace_joint: order");
418 a.done();
419 Json out;
420 out["JM"] = encode_scalar(line::trace::trace_joint<T>(S, lag, order));
421 return out;
422 }
423};
424
425struct OpTracePmf {
426 template <class T>
427 static Json run(Args& a) {
428 const std::vector<int> X = a.ints("X");
429 a.done();
430 const line::trace::TracePmfResult<T> r = line::trace::trace_pmf<T>(X);
431 Json out;
432 out["pmf"] = encode_vector(r.pmf);
433 out["px"] = encode_ints(r.values);
434 return out;
435 }
436};
437
438/**
439 * trace_summary(m). The port's skewness is bias-corrected and its percentiles
440 * are type 7, its ACF covers lags 1..4 and its tail is the 95th percentile
441 * alone; those are the values reported (see trace_summary.h). KURT is MATLAB's
442 * kurtosis, i.e. the port's excess kurtosis plus 3.
443 */
444struct OpTraceSummary {
445 template <class T>
446 static Json run(Args& a) {
447 const std::vector<T> S = a.vector<T>("m");
448 a.unsupported("fid", "it is a MATLAB file identifier for the printed report; the API "
449 "returns the values instead of printing them");
450 a.done();
451 const line::trace::TraceSummary<T> r = line::trace::trace_summary<T>(S);
452 Json out;
453 out["MEAN"] = encode_scalar(r.mean);
454 out["SCV"] = encode_scalar(r.scv);
455 out["MAD"] = encode_scalar(r.mad);
456 out["SKEW"] = encode_scalar(r.skew);
457 out["KURT"] = encode_scalar(T(r.kurt_excess + num_traits<T>::from_int(3)));
458 out["QUART"] = encode_vector(std::vector<T>{r.q25, r.q50, r.q75});
459 out["TAILS1PERC"] = encode_vector(std::vector<T>{r.p95});
460 out["MINMAX"] = encode_vector(std::vector<T>{r.min, r.max});
461 out["IQR"] = encode_scalar(r.iqr);
462 out["ACF"] = encode_vector(r.acf);
463 out["IDC"] = encode_scalar(r.idc);
464 return out;
465 }
466};
467
468// ---------------------------------------------------------------------------
469// mtrace: marked traces
470// ---------------------------------------------------------------------------
471
472struct OpMtraceMoment {
473 template <class T>
474 static Json run(Args& a) {
475 const std::vector<T> Tv = a.vector<T>("T");
476 const std::vector<int> A = a.ints("A");
477 const std::vector<unsigned> orders = nonneg_unsigned(a.ints("ORDERS"), "mtrace_moment: ORDERS");
478 const bool has_after = a.has("AFTER"), has_norm = a.has("NORM");
479 if (has_norm && !has_after)
480 throw InputError("mtrace_moment: NORM follows AFTER positionally; give AFTER too");
481 const bool after = has_after ? a.boolean("AFTER", false) : false;
482 const bool norm = has_norm ? a.boolean("NORM", false) : false;
483 a.opt("AFTER");
484 a.opt("NORM");
485 a.done();
486 Matrix<T> M;
487 if (has_norm)
488 M = line::trace::mtrace_moment<T>(Tv, A, orders, after, norm);
489 else if (has_after)
490 M = line::trace::mtrace_moment<T>(Tv, A, orders, after);
491 else
492 M = line::trace::mtrace_moment<T>(Tv, A, orders);
493 Json out;
494 out["M"] = encode_matrix(M);
495 return out;
496 }
497};
498
499#define LINE_MTRACE_DIRECTED_MOMENT_OP(Name, fn) \
500 struct Name { \
501 template <class T> \
502 static Json run(Args& a) { \
503 const std::vector<T> Tv = a.vector<T>("T"); \
504 const std::vector<int> A = a.ints("A"); \
505 const std::vector<unsigned> orders = \
506 nonneg_unsigned(a.ints("ORDERS"), #fn ": ORDERS"); \
507 const bool has_norm = a.has("NORM"); \
508 const bool norm = has_norm ? a.boolean("NORM", false) : false; \
509 a.opt("NORM"); \
510 a.done(); \
511 Json out; \
512 out["M"] = encode_matrix(has_norm ? line::trace::fn<T>(Tv, A, orders, norm) \
513 : line::trace::fn<T>(Tv, A, orders)); \
514 return out; \
515 } \
516 };
517
518LINE_MTRACE_DIRECTED_MOMENT_OP(OpMtraceForwardMoment, mtrace_forward_moment)
519LINE_MTRACE_DIRECTED_MOMENT_OP(OpMtraceBackwardMoment, mtrace_backward_moment)
520#undef LINE_MTRACE_DIRECTED_MOMENT_OP
521
522#define LINE_MTRACE_CROSS_MOMENT_OP(Name, fn) \
523 struct Name { \
524 template <class T> \
525 static Json run(Args& a) { \
526 const std::vector<T> Tv = a.vector<T>("T"); \
527 const std::vector<int> L = a.ints("L"); \
528 const unsigned k = a.uinteger("k"); \
529 a.done(); \
530 const line::trace::MtraceCrossMomentResult<T> r = \
531 line::trace::fn<T>(Tv, L, k); \
532 Json out; \
533 out["MC"] = encode_matrix_defined(r.mc, r.count); \
534 return out; \
535 } \
536 };
537
538LINE_MTRACE_CROSS_MOMENT_OP(OpMtraceCrossMoment, mtrace_cross_moment)
539LINE_MTRACE_CROSS_MOMENT_OP(OpMtraceMomentSimple, mtrace_moment_simple)
540#undef LINE_MTRACE_CROSS_MOMENT_OP
541
542struct OpMtraceCount {
543 template <class T>
544 static Json run(Args& a) {
545 const std::vector<T> Tv = a.vector<T>("T");
546 const std::vector<int> A = a.ints("A");
547 const T t = a.number<T>("t");
548 a.done();
549 Json out;
550 out["N"] = encode_long_matrix(line::trace::mtrace_count<T>(Tv, A, t).counts);
551 return out;
552 }
553};
554
555struct OpMtraceCov {
556 template <class T>
557 static Json run(Args& a) {
558 const std::vector<T> Tv = a.vector<T>("T");
559 const std::vector<int> A = a.ints("A");
560 a.done();
561 const std::vector<std::vector<Matrix<T>>> cov = line::trace::mtrace_cov<T>(Tv, A);
562 Json c = Json::array();
563 for (const std::vector<Matrix<T>>& row : cov) c.push_back(encode_matrices(row));
564 Json out;
565 out["COV"] = c;
566 return out;
567 }
568};
569
570struct OpMtraceIat2counts {
571 template <class T>
572 static Json run(Args& a) {
573 const std::vector<T> Tv = a.vector<T>("T");
574 const std::vector<int> A = a.ints("A");
575 const T scale = a.number<T>("scale");
576 a.unsupported("no_mex", "it selects between MATLAB's MEX and script implementations of "
577 "the same counts; the port has one implementation");
578 a.done();
579 Json out;
580 out["C"] = encode_long_matrix(line::trace::mtrace_iat2counts<T>(Tv, A, scale).counts);
581 return out;
582 }
583};
584
585struct OpMtraceJoint {
586 template <class T>
587 static Json run(Args& a) {
588 const std::vector<T> Tv = a.vector<T>("T");
589 const std::vector<int> A = a.ints("A");
590 const std::vector<unsigned> i = nonneg_unsigned(a.ints("i"), "mtrace_joint: i");
591 if (i.size() != 2) throw InputError("mtrace_joint: 'i' must hold the two orders [i1, i2]");
592 a.done();
593 const line::trace::MtraceJointResult<T> r = line::trace::mtrace_joint<T>(Tv, A, i[0], i[1]);
594 Json out;
595 out["JM"] = encode_vector_defined(r.jm, r.count);
596 return out;
597 }
598};
599
600struct OpMtraceMean {
601 template <class T>
602 static Json run(Args& a) {
603 const std::vector<T> Tv = a.vector<T>("trace");
604 const int ntypes = a.required_integer("ntypes");
605 const std::vector<int> type = a.ints("type");
606 a.done();
607 const line::trace::MtraceMeanResult<T> r =
608 line::trace::mtrace_mean<T>(Tv, static_cast<long>(ntypes), type);
609 Json out;
610 out["m"] = encode_vector_defined(r.mean, r.count);
611 return out;
612 }
613};
614
615struct OpMtraceMerge {
616 template <class T>
617 static Json run(Args& a) {
618 const std::vector<T> t1 = a.vector<T>("t1");
619 const std::vector<T> t2 = a.vector<T>("t2");
620 a.done();
621 const line::trace::MtraceMergeResult<T> r = line::trace::mtrace_merge<T>(t1, t2);
622 Json out;
623 out["T"] = encode_vector(r.times);
624 out["L"] = encode_ints(r.labels);
625 return out;
626 }
627};
628
629/** The reference's first argument T is never read: the statistic is of the labels. */
630void refuse_unused_times(Args& a, const char* fn) {
631 if (a.has("T"))
632 throw InputError(std::string(fn) + ": 'T' is not read by the reference, whose result "
633 "depends on the labels L alone; omit it");
634 a.opt("T");
635}
636
637struct OpMtracePc {
638 template <class T>
639 static Json run(Args& a) {
640 const std::vector<int> C = a.ints("C");
641 a.done();
642 Json out;
644 return out;
645 }
646};
647
648struct OpMtraceSigma {
649 template <class T>
650 static Json run(Args& a) {
651 refuse_unused_times(a, "mtrace_sigma");
652 const std::vector<int> L = a.ints("L");
653 a.done();
654 Json out;
656 return out;
657 }
658};
659
660/** sigma(i,j,h) as a C x C x C nested array, from the port's (i, j*C+h) layout. */
661struct OpMtraceSigma2 {
662 template <class T>
663 static Json run(Args& a) {
664 refuse_unused_times(a, "mtrace_sigma2");
665 const std::vector<int> L = a.ints("L");
666 a.done();
667 const Matrix<T> s = line::trace::mtrace_sigma2<T>(L);
668 const std::size_t C = s.rows();
669 Json cube = Json::array();
670 for (std::size_t i = 0; i < C; ++i) {
671 Json plane = Json::array();
672 for (std::size_t j = 0; j < C; ++j) {
673 Json row = Json::array();
674 for (std::size_t h = 0; h < C; ++h) row.push_back(encode_scalar(s(i, j * C + h)));
675 plane.push_back(row);
676 }
677 cube.push_back(plane);
678 }
679 Json out;
680 out["sigma"] = cube;
681 return out;
682 }
683};
684
685struct OpMtraceSplit {
686 template <class T>
687 static Json run(Args& a) {
688 const std::vector<T> Tv = a.vector<T>("T");
689 const std::vector<int> L = a.ints("L");
690 a.done();
691 Json out;
692 out["TL"] = encode_vectors(line::trace::mtrace_split<T>(Tv, L).traces);
693 return out;
694 }
695};
696
697struct OpMtraceSummary {
698 template <class T>
699 static Json run(Args& a) {
700 const std::vector<T> Tv = a.vector<T>("T");
701 const std::vector<int> C = a.ints("C");
702 a.done();
703 const line::trace::MtraceSummary<T> r = line::trace::mtrace_summary<T>(Tv, C);
704 Json s;
705 s["M"] = encode_vector(r.M);
706 s["ACF"] = encode_vector(r.acf);
707 s["F1"] = encode_matrix(r.F1);
708 s["F2"] = encode_matrix(r.F2);
709 s["B1"] = encode_matrix(r.B1);
710 s["B2"] = encode_matrix(r.B2);
711 s["C1"] = encode_matrix(r.C1);
712 s["C2"] = encode_matrix(r.C2);
713 s["Pc"] = encode_vector(r.Pc);
714 s["Pab"] = encode_matrix(r.Pab);
715 Json out;
716 out["summary"] = s;
717 return out;
718 }
719};
720
721// ---------------------------------------------------------------------------
722// infer: queue lengths and demands from traces
723// ---------------------------------------------------------------------------
724
725struct OpInferComputeQlAtArrival {
726 template <class T>
727 static Json run(Args& a) {
728 const std::vector<T> at = a.vector<T>("at");
729 const std::vector<long> at_jobid = to_longs(a.ints("at_jobid"));
730 const std::vector<T> rt = a.vector<T>("rt");
731 const std::vector<long> rt_jobid = to_longs(a.ints("rt_jobid"));
732 const std::vector<std::size_t> cls =
733 one_based_to_zero(a.ints("class"), "infer_compute_ql_at_arrival: class");
734 const std::size_t R = a.count("R");
735 a.done();
736 Json out;
737 out["ql"] = encode_matrix(
738 line::infer::infer_compute_ql_at_arrival<T>(at, at_jobid, rt, rt_jobid, cls, R));
739 return out;
740 }
741};
742
743struct OpInferGetQlenArrival {
744 template <class T>
745 static Json run(Args& a) {
746 std::vector<std::vector<T>> at_ms, rt;
747 read_trace_cells<T>(a.get("data"), "infer_get_qlen_arrival: data", at_ms, rt);
748 a.done();
749 Json out;
751 return out;
752 }
753};
754
755struct OpInferLqnFindbyname {
756 template <class T>
757 static Json run(Args& a) {
758 const Json& container = a.get("container");
759 const std::string name = string_of(a.get("name"), "infer_lqn_findbyname: name");
760 a.done();
761 if (!container.is_array())
762 throw InputError("infer_lqn_findbyname: 'container' must be a list of elements");
763 std::vector<std::string> names;
764 for (std::size_t k = 0; k < container.size(); ++k) {
765 const Json& e = container[k];
766 if (!e.is_object() || e.find("name") == e.end())
767 throw InputError("infer_lqn_findbyname: container entry " + std::to_string(k + 1) +
768 " has no 'name' field");
769 names.push_back(string_of(e["name"], "infer_lqn_findbyname: container.name"));
770 }
771 const std::size_t idx = line::infer::infer_lqn_findbyname(names, name);
772 Json out;
773 out["el"] = idx == line::infer::INFER_LQN_NOT_FOUND ? Json(nullptr) : container[idx];
774 return out;
775 }
776};
777
778struct OpInferLqnGetobs {
779 template <class T>
780 static Json run(Args& a) {
781 const std::vector<std::string> names = strings_of(a.get("names"), "infer_lqn_getobs: names");
782 const Json& mj = a.get("metrics");
783 const Json& sj = a.get("obsSpec");
784 a.done();
785 line::infer::LqnMetrics<T> m;
786 {
787 Args ma(mj, "infer_lqn_getobs: metrics");
788 m.QLen = ma.vector_or_empty<T>("QLen");
789 m.Util = ma.vector_or_empty<T>("Util");
790 m.RespT = ma.vector_or_empty<T>("RespT");
791 m.Tput = ma.vector_or_empty<T>("Tput");
792 ma.done();
793 }
794 if (!sj.is_array()) throw InputError("infer_lqn_getobs: 'obsSpec' must be a list of {metric, name}");
795 std::vector<line::infer::LqnObsSpec> spec;
796 for (const Json& e : sj) {
797 Args ea(e, "infer_lqn_getobs: obsSpec");
798 const std::string metric = lower(string_of(ea.get("metric"), "infer_lqn_getobs: obsSpec.metric"));
799 line::infer::LqnObsSpec s;
800 s.name = string_of(ea.get("name"), "infer_lqn_getobs: obsSpec.name");
801 ea.done();
802 if (metric == "respt")
804 else if (metric == "util")
806 else if (metric == "tput")
808 else if (metric == "qlen")
810 else
811 throw InputError("infer_lqn_getobs: Unknown metric '" + metric + "'.");
812 spec.push_back(s);
813 }
814 Json out;
815 out["z"] = encode_vector(line::infer::infer_lqn_getobs<T>(names, m, spec));
816 return out;
817 }
818};
819
820struct OpInferQmle {
821 template <class T>
822 static Json run(Args& a) {
823 const Matrix<T> Q = a.matrix<T>("Q");
824 const std::vector<T> N = a.vector<T>("N");
825 const std::vector<T> Z = a.vector<T>("Z");
826 a.done();
827 Json out;
828 out["D"] = encode_matrix(line::infer::infer_qmle<T>(Q, N, Z));
829 return out;
830 }
831};
832
833struct OpInferRps {
834 template <class T>
835 static Json run(Args& a) {
836 const std::vector<T> rt = a.vector<T>("rt");
837 const std::vector<std::size_t> cls = one_based_to_zero(a.ints("class"), "infer_rps: class");
838 const Matrix<T> ql = a.matrix<T>("ql");
839 const int V = a.required_integer("V");
840 a.done();
841 Json out;
842 out["demandEst"] = encode_vector(line::infer::infer_rps<T>(rt, cls, ql, static_cast<long>(V)));
843 return out;
844 }
845};
846
847/**
848 * infer_quick_model(open, stations, classes, servers, jobs, routing). The
849 * routing of a closed model is one M x M matrix per class (MATLAB's cell
850 * form), or a single M x M matrix that every class uses.
851 */
852struct OpInferQuickModel {
853 template <class T>
854 static Json run(Args& a) {
855 const bool is_open = flag_of(a.get("open"), "infer_quick_model: open");
856 std::vector<lang::SchedStrategy> stations;
857 for (const std::string& s : strings_of(a.get("stations"), "infer_quick_model: stations"))
858 stations.push_back(io::detail::sched_from_json(s));
859 const Matrix<T> demands = a.matrix<T>("classes");
860 const bool has_servers = a.has("servers"), has_jobs = a.has("jobs"),
861 has_routing = a.has("routing");
862 if (has_jobs && !has_servers)
863 throw InputError("infer_quick_model: 'jobs' follows 'servers' positionally; give servers too");
864 if (has_routing && !(has_servers && has_jobs))
865 throw InputError("infer_quick_model: 'routing' follows 'servers' and 'jobs' "
866 "positionally; give both");
867 if (has_routing && is_open)
868 throw InputError("infer_quick_model: 'routing' applies to a closed model; an open "
869 "model is routed serially from the source to the sink");
870 const std::vector<double> servers = a.vector_or_empty<double>("servers");
871 const std::vector<double> jobs = a.vector_or_empty<double>("jobs");
872 std::vector<Matrix<T>> routing;
873 if (has_routing) {
874 const Json& rj = a.get("routing");
875 const bool cell_form = rj.is_array() && !rj.empty() && rj[0].is_array() &&
876 !rj[0].empty() && rj[0][0].is_array();
877 if (cell_form) {
878 routing = a.matrices<T>("routing");
879 } else {
880 const Matrix<T> P = a.matrix<T>("routing");
881 routing.assign(demands.rows(), P);
882 }
883 }
884 a.done();
885 qn::Network<T> model =
886 has_routing ? line::infer::infer_quick_model<T>(is_open, stations, demands, servers, jobs, routing)
887 : has_jobs ? line::infer::infer_quick_model<T>(is_open, stations, demands, servers, jobs)
888 : has_servers ? line::infer::infer_quick_model<T>(is_open, stations, demands, servers)
889 : line::infer::infer_quick_model<T>(is_open, stations, demands);
890 Json out;
891 out["model"] = io::network_json_envelope(model.get_struct());
892 return out;
893 }
894};
895
896/**
897 * infer_nhpp_ks(times, T, 'cumRate', f, 'method', m, 'T0', t0). The port's
898 * options are positional (cumRate, method, T0); an absent cumRate is the
899 * constant rate in both, so method is reached through the empty callable.
900 */
901struct OpInferNhppKs {
902 template <class T>
903 static Json run(Args& a) {
904 const std::vector<T> times = a.vector<T>("times");
905 const T Tend = a.number<T>("T");
906 a.unsupported("cumRate", "it is a function handle; without it the rate is taken "
907 "constant over the interval");
908 const bool has_method = a.has("method"), has_t0 = a.has("T0");
909 const std::string m = lower(a.text("method", std::string()));
910 if (has_t0 && !has_method)
911 throw InputError("infer_nhpp_ks: the port takes T0 after method; give 'method' too");
912 line::infer::NhppKsResult<T> r;
913 if (!has_method) {
914 a.done();
915 r = line::infer::infer_nhpp_ks<T>(times, Tend);
916 } else {
918 if (m == "cu")
920 else if (m == "lewis")
922 else
923 throw InputError("infer_nhpp_ks: method must be 'cu' or 'lewis'");
924 const std::function<T(const T&)> constant;
925 if (has_t0) {
926 const T t0 = a.number<T>("T0");
927 a.done();
928 r = line::infer::infer_nhpp_ks<T>(times, Tend, constant, method, t0);
929 } else {
930 a.opt("T0");
931 a.done();
932 r = line::infer::infer_nhpp_ks<T>(times, Tend, constant, method);
933 }
934 }
935 Json res;
936 res["statistic"] = encode_scalar(r.statistic);
937 res["pvalue"] = encode_scalar(r.pvalue);
938 res["n"] = encode_count(r.n);
939 res["uniforms"] = encode_vector(r.uniforms);
940 res["transformed"] = encode_vector(r.transformed);
941 Json out;
942 out["result"] = res;
943 return out;
944 }
945};
946
947// ---- semi-Markov inference (double only) ----------------------------------
948
949Json encode_smp_fit(const line::infer::SmpFit& f) {
950 Json arcs = Json::array();
951 for (const line::infer::SmpArc& c : f.arcs) {
952 Json j;
953 j["from"] = encode_count(c.from);
954 j["to"] = encode_count(c.to);
955 j["family"] = c.family;
956 j["theta"] = encode_vector(c.theta);
957 j["params"] = encode_vector(c.params);
958 j["beta"] = encode_vector(c.beta);
959 j["se"] = encode_vector(c.se);
960 j["prob"] = c.prob;
961 j["nevents"] = encode_count(c.nevents);
962 arcs.push_back(j);
963 }
964 Json j;
965 j["approach"] = f.approach;
966 j["nstates"] = encode_count(f.nstates);
967 j["ncov"] = encode_count(f.ncov);
968 j["arcs"] = arcs;
969 j["loglik"] = f.loglik;
970 j["nparams"] = encode_count(f.nparams);
971 j["aic"] = f.aic;
972 j["P"] = encode_matrix(f.P);
973 return j;
974}
975
976std::vector<double> doubles_nan(const Json& j, const std::string& where) {
977 const Matrix<double> m = matrix_or_fill<double>(j, where, std::numeric_limits<double>::quiet_NaN());
978 return column_major(m);
979}
980
981/** The `fit` struct infer_smp returns, read back field for field. */
982line::infer::SmpFit read_smp_fit(const Json& j) {
983 const std::string w = "infer_smp_hazard: fit";
984 Args f(j, w);
985 line::infer::SmpFit fit;
986 fit.approach = string_of(f.get("approach"), w + ".approach");
987 fit.nstates = f.count("nstates");
988 fit.ncov = f.count("ncov");
989 fit.loglik = number_or_fill<double>(f.get("loglik"), w + ".loglik",
990 std::numeric_limits<double>::quiet_NaN());
991 fit.nparams = f.count("nparams");
992 fit.aic = number_or_fill<double>(f.get("aic"), w + ".aic", std::numeric_limits<double>::quiet_NaN());
993 fit.P = matrix_or_fill<double>(f.get("P"), w + ".P", std::numeric_limits<double>::quiet_NaN());
994 const Json& arcs = f.get("arcs");
995 f.done();
996 if (!arcs.is_array()) throw InputError(w + ".arcs: expected a list of arcs");
997 for (const Json& e : arcs) {
998 Args c(e, w + ".arcs");
999 line::infer::SmpArc arc;
1000 arc.from = c.count("from");
1001 arc.to = c.count("to");
1002 arc.family = string_of(c.get("family"), w + ".arcs.family");
1003 arc.theta = doubles_nan(c.get("theta"), w + ".arcs.theta");
1004 arc.params = doubles_nan(c.get("params"), w + ".arcs.params");
1005 arc.beta = doubles_nan(c.get("beta"), w + ".arcs.beta");
1006 arc.se = doubles_nan(c.get("se"), w + ".arcs.se");
1007 arc.prob = number_or_fill<double>(c.get("prob"), w + ".arcs.prob",
1008 std::numeric_limits<double>::quiet_NaN());
1009 arc.nevents = c.count("nevents");
1010 c.done();
1011 fit.arcs.push_back(arc);
1012 }
1013 return fit;
1014}
1015
1016struct OpInferSmp {
1017 static Json run(Args& a) {
1018 const std::string w = "infer_smp";
1019 const std::vector<std::size_t> from = nonneg_sizes(a.ints("from"), w + ": from");
1020 const std::vector<std::size_t> to = nonneg_sizes(a.ints("to"), w + ": to");
1021 const std::vector<double> dur = a.vector<double>("dur");
1022 line::infer::SmpOptions opt;
1023 if (a.has("approach")) opt.approach = lower(a.text("approach", std::string()));
1024 if (const Json* d = a.opt("dist")) {
1025 if (d->is_string()) {
1026 opt.dist = lower(d->get<std::string>());
1027 } else {
1028 if (!d->is_array()) throw InputError(w + ": 'dist' must be a family name or an E x E table of them");
1029 for (const Json& row : *d) {
1030 if (!row.is_array()) throw InputError(w + ": 'dist' table rows must be lists");
1031 std::vector<std::string> r;
1032 for (const Json& e : row) r.push_back(e.is_null() ? std::string() : lower(string_of(e, w + ": dist")));
1033 opt.dist_table.push_back(r);
1034 }
1035 }
1036 }
1037 if (a.has("covariates")) opt.covariates = a.matrix<double>("covariates");
1038 if (const Json* cm = a.opt("covmask")) {
1039 if (!cm->is_array()) throw InputError(w + ": 'covmask' must be an E x E x p array");
1040 for (const Json& plane : *cm) {
1041 if (!plane.is_array()) throw InputError(w + ": 'covmask' must be an E x E x p array");
1042 std::vector<std::vector<bool>> p;
1043 for (const Json& row : plane) {
1044 if (!row.is_array()) throw InputError(w + ": 'covmask' must be an E x E x p array");
1045 std::vector<bool> r;
1046 for (const Json& e : row) r.push_back(flag_of(e, w + ": covmask"));
1047 p.push_back(r);
1048 }
1049 opt.covmask.push_back(p);
1050 }
1051 }
1052 if (a.has("nstates")) opt.nstates = a.count("nstates");
1053 if (a.has("tolx")) opt.tolx = a.number<double>("tolx");
1054 if (a.has("tolf")) opt.tolf = a.number<double>("tolf");
1055 if (a.has("maxiter")) opt.maxiter = a.count("maxiter");
1056 a.opt("approach");
1057 a.opt("covariates");
1058 a.opt("nstates");
1059 a.opt("tolx");
1060 a.opt("tolf");
1061 a.opt("maxiter");
1062 a.done();
1063 Json out;
1064 out["fit"] = encode_smp_fit(line::infer::infer_smp(from, to, dur, opt));
1065 return out;
1066 }
1067};
1068
1069struct OpInferSmpHazard {
1070 static Json run(Args& a) {
1071 const line::infer::SmpFit fit = read_smp_fit(a.get("fit"));
1072 const std::vector<double> t = a.vector<double>("t");
1073 const bool has_z = a.has("z");
1074 const std::vector<double> z = a.vector_or_empty<double>("z");
1075 a.done();
1076 const std::vector<line::infer::SmpHazard> h =
1077 has_z ? line::infer::infer_smp_hazard(fit, t, z) : line::infer::infer_smp_hazard(fit, t);
1078 Json hz = Json::array();
1079 for (const line::infer::SmpHazard& e : h) {
1080 Json j;
1081 j["from"] = encode_count(e.from);
1082 j["to"] = encode_count(e.to);
1083 j["intensity"] = encode_vector(e.intensity);
1084 j["sojourn"] = encode_vector(e.sojourn);
1085 j["cumulative"] = encode_vector(e.cumulative);
1086 j["cif"] = encode_vector(e.cif);
1087 hz.push_back(j);
1088 }
1089 Json out;
1090 out["hz"] = hz;
1091 return out;
1092 }
1093};
1094
1095struct OpInferSmpNelsonAalen {
1096 static Json run(Args& a) {
1097 const std::string w = "infer_smp_nelson_aalen";
1098 const std::vector<std::size_t> from = nonneg_sizes(a.ints("from"), w + ": from");
1099 const std::vector<std::size_t> to = nonneg_sizes(a.ints("to"), w + ": to");
1100 const std::vector<double> dur = a.vector<double>("dur");
1101 const bool has_e = a.has("nstates");
1102 const std::size_t E = has_e ? a.count("nstates") : 0;
1103 a.opt("nstates");
1104 a.done();
1105 const std::vector<line::infer::SmpNelsonAalen> r =
1106 has_e ? line::infer::infer_smp_nelson_aalen(from, to, dur, E)
1107 : line::infer::infer_smp_nelson_aalen(from, to, dur);
1108 Json na = Json::array();
1109 for (const line::infer::SmpNelsonAalen& e : r) {
1110 Json j;
1111 j["from"] = encode_count(e.from);
1112 j["to"] = encode_count(e.to);
1113 j["time"] = encode_vector(e.time);
1114 j["A"] = encode_vector(e.A);
1115 j["var"] = encode_vector(e.var);
1116 na.push_back(j);
1117 }
1118 Json out;
1119 out["na"] = na;
1120 return out;
1121 }
1122};
1123
1124/**
1125 * infer_smp_family(family, mode, ...), one entry for the five modes, with the
1126 * mode's positional arguments under the names of the reference's help text.
1127 */
1128struct OpInferSmpFamily {
1129 static Json run(Args& a) {
1130 const std::string w = "infer_smp_family";
1131 const std::string family = string_of(a.get("family"), w + ": family");
1132 const std::string mode = string_of(a.get("mode"), w + ": mode");
1133 // Validates the family name before any parameter vector is indexed.
1134 const std::size_t npar = line::infer::infer_smp_family_npar(family);
1135 auto theta = [&]() {
1136 const std::vector<double> th = a.vector<double>("THETA");
1137 if (th.size() != npar)
1138 throw InputError(w + ": THETA must have " + std::to_string(npar) + " entries for '" +
1139 family + "'");
1140 return th;
1141 };
1142 Json out;
1143 if (mode == "npar") {
1144 a.done();
1145 out["N"] = encode_count(npar);
1146 } else if (mode == "logfs") {
1147 const std::vector<double> th = theta();
1148 const std::vector<double> t = a.vector<double>("T");
1149 const std::vector<double> eta = a.vector_or_empty<double>("ETA");
1150 if (eta.size() > 1 && eta.size() != t.size())
1151 throw InputError(w + ": ETA must be a scalar or have one entry per instant of T");
1152 a.done();
1153 std::vector<double> logf, logS;
1154 line::infer::infer_smp_family_logfs(family, th, t, eta, logf, logS);
1155 out["LOGF"] = encode_vector(logf);
1156 out["LOGS"] = encode_vector(logS);
1157 } else if (mode == "natural") {
1158 const std::vector<double> th = theta();
1159 a.done();
1161 } else if (mode == "theta0") {
1162 const std::vector<double> tev = a.vector<double>("TEV");
1163 const double texp = a.number<double>("TEXP");
1164 a.done();
1165 out["THETA"] = encode_vector(line::infer::infer_smp_family_theta0(family, tev, texp));
1166 } else if (mode == "dist") {
1167 const std::vector<double> th = theta();
1168 const double eta = a.number<double>("ETA");
1169 a.done();
1170 out["D"] = io::detail::dist_to_json(line::infer::infer_smp_family_dist(family, th, eta));
1171 } else {
1172 throw InputError(w + ": Unknown mode '" + mode +
1173 "': use 'npar', 'logfs', 'natural', 'theta0' or 'dist'.");
1174 }
1175 return out;
1176 }
1177};
1178
1179// ---- variational inference --------------------------------------------------
1180
1181/**
1182 * infer_variational(spec, options). The spec is MATLAB's struct; NaN entries
1183 * (arcrate where the arc is governed by a parameter, obsData where nothing was
1184 * observed) are written as null, and so is an Inf capacity. obsRange and
1185 * capacity are flattened column-major, as the reference's reshape does.
1186 */
1187struct OpInferVariational {
1188 template <class T>
1189 static Json run(Args& a) {
1190 const std::string w = "infer_variational";
1191 const Json& sj = a.get("spec");
1192 const Json* oj = a.opt("options");
1193 a.done();
1194 const T nan = num_traits<T>::from_double(std::numeric_limits<double>::quiet_NaN());
1195 const T inf = num_traits<T>::from_double(std::numeric_limits<double>::infinity());
1196
1197 line::infer::VariationalSpec<T> spec;
1198 {
1199 Args s(sj, w + ": spec");
1200 const Matrix<T> arcs = s.matrix<T>("arcs");
1201 if (!arcs.empty() && arcs.cols() != 3)
1202 throw InputError(w + ": spec.arcs must have three columns [i j c].");
1203 const std::vector<int> arcint = int_vector_from_json(s.get("arcs"), w + ": spec.arcs");
1204 const std::vector<std::size_t> arcsz = nonneg_sizes(arcint, w + ": spec.arcs");
1205 for (std::size_t e = 0; e < arcs.rows(); ++e)
1206 spec.arcs.push_back(std::array<std::size_t, 3>{arcsz[3 * e], arcsz[3 * e + 1], arcsz[3 * e + 2]});
1207 spec.x0 = s.matrix<T>("x0");
1208 spec.sched = s.ints("sched");
1209 spec.nservers = s.vector<T>("nservers");
1210 spec.routeprob = s.vector<T>("routeprob");
1211 spec.arcparam = nonneg_sizes(s.ints("arcparam"), w + ": spec.arcparam");
1212 spec.arcrate = column_major(matrix_or_fill<T>(s.get("arcrate"), w + ": spec.arcrate", nan));
1213 spec.alpha0 = s.vector<T>("alpha0");
1214 spec.beta0 = s.vector<T>("beta0");
1215 spec.obsTimes = s.vector<T>("obsTimes");
1216 spec.obsData = matrix_or_fill<T>(s.get("obsData"), w + ": spec.obsData",
1218 spec.obsRange = column_major(s.matrix<T>("obsRange"));
1219 spec.epsilon = s.number<T>("epsilon");
1220 if (const Json* c = s.opt("capacity"))
1221 spec.capacity = column_major(matrix_or_fill<T>(*c, w + ": spec.capacity", inf));
1222 s.done();
1223 }
1224 line::infer::VariationalOptions<T> opt;
1225 if (oj) {
1226 Args o(*oj, w + ": options");
1227 if (o.has("verbose")) opt.verbose = o.required_integer("verbose");
1228 if (o.has("iter_max")) opt.iter_max = o.count("iter_max");
1229 if (o.has("tol")) opt.tol = o.number<double>("tol");
1230 if (o.has("nsamples")) opt.nsamples = o.count("nsamples");
1231 if (o.has("delta")) opt.delta = o.number<double>("delta");
1232 if (o.has("floor")) opt.floor = o.number<double>("floor");
1233 if (o.has("rate_max")) opt.rate_max = o.number<double>("rate_max");
1234 if (o.has("rate_cap_factor")) opt.rate_cap_factor = o.number<double>("rate_cap_factor");
1235 if (o.has("unifmax")) opt.unifmax = o.number<double>("unifmax");
1236 if (o.has("unif_tol")) opt.unif_tol = o.number<double>("unif_tol");
1237 if (o.has("unif_max_terms")) opt.unif_max_terms = o.count("unif_max_terms");
1238 if (o.has("tmax")) opt.tmax = o.number<double>("tmax");
1239 if (o.has("dt")) opt.dt = o.number<double>("dt");
1240 if (o.has("ngrid")) opt.ngrid = static_cast<long>(o.count("ngrid"));
1241 if (o.has("ymax")) opt.ymax = static_cast<long>(o.count("ymax"));
1242 for (const char* k : {"verbose", "iter_max", "tol", "nsamples", "delta", "floor",
1243 "rate_max", "rate_cap_factor", "unifmax", "unif_tol",
1244 "unif_max_terms", "tmax", "dt", "ngrid", "ymax"})
1245 o.opt(k);
1246 o.done();
1247 }
1248 const line::infer::VariationalResult<T> r =
1249 oj ? line::infer::infer_variational<T>(spec, opt) : line::infer::infer_variational<T>(spec);
1250 Json res;
1251 res["alpha"] = encode_vector(r.alpha);
1252 res["beta"] = encode_vector(r.beta);
1253 res["rates"] = encode_vector(r.rates);
1254 res["meanServiceTime"] = encode_vector(r.mean_service_time);
1255 res["bound"] = encode_vector(r.bound);
1256 res["alphaTrace"] = encode_matrix(r.alpha_trace);
1257 res["betaTrace"] = encode_matrix(r.beta_trace);
1258 res["Y"] = encode_matrices(r.Y);
1259 res["nu"] = encode_matrices(r.nu);
1260 res["tgrid"] = encode_vector(r.tgrid);
1261 res["qlen"] = encode_matrix(r.qlen);
1262 res["iter"] = encode_count(r.iter);
1263 res["converged"] = r.converged;
1264 res["tailmass"] = encode_scalar(r.tailmass);
1265 Json out;
1266 out["out"] = res;
1267 return out;
1268 }
1269};
1270
1271// ---- MLPS / MINPS (double only) --------------------------------------------
1272
1273const char* const kMlpsModelWhy =
1274 "the reference reads the delay rates and the server count off the model; pass them as "
1275 "'muZ' (per-class think rates) and 'nCores' instead";
1276
1277std::vector<line::api::MlpsSample> read_mlps_samples(Args& a, const std::string& w) {
1278 const std::vector<double> rt = a.vector<double>("rt");
1279 const std::vector<std::size_t> cls = one_based_sizes(a.ints("class"), w + ": class");
1280 const Matrix<double> ql = a.matrix<double>("ql");
1281 if (cls.size() != rt.size() || ql.rows() != rt.size())
1282 throw InputError(w + ": rt, class and ql must have one row per sample");
1283 std::vector<line::api::MlpsSample> samples(rt.size());
1284 for (std::size_t i = 0; i < rt.size(); ++i) {
1285 samples[i].rt = rt[i];
1286 samples[i].cls = cls[i];
1287 samples[i].ql.resize(ql.cols());
1288 for (std::size_t c = 0; c < ql.cols(); ++c) samples[i].ql[c] = ql(i, c);
1289 }
1290 return samples;
1291}
1292
1293struct OpInferMlps {
1294 static Json run(Args& a) {
1295 a.unsupported("model", kMlpsModelWhy);
1296 a.unsupported("node", kMlpsModelWhy);
1297 const std::vector<double> muZ = a.vector<double>("muZ");
1298 const double nCores = a.number<double>("nCores");
1299 const std::vector<line::api::MlpsSample> samples = read_mlps_samples(a, "infer_mlps");
1300 a.done();
1301 Json out;
1302 out["demandEst"] = encode_vector(line::api::infer_mlps(muZ, nCores, samples));
1303 return out;
1304 }
1305};
1306
1307/**
1308 * infer_minps_setup(data, initSample, sampleSize, V, model, node): prepare the
1309 * window, then run MINPS on it, as the reference's last line does. The port
1310 * also reports what the preparation decided (lambda, Wexp, classMap).
1311 */
1312struct OpInferMinpsSetup {
1313 static Json run(Args& a) {
1314 const std::string w = "infer_minps_setup";
1315 a.unsupported("model", kMlpsModelWhy);
1316 a.unsupported("node", kMlpsModelWhy);
1317 if (a.has("V"))
1318 throw InputError(w + ": 'V' is not read by the reference, which takes the server count "
1319 "from the node; pass it as 'nCores'");
1320 a.opt("V");
1321 std::vector<std::vector<double>> at_ms, rt;
1322 read_trace_cells<double>(a.get("data"), w + ": data", at_ms, rt);
1323 std::vector<line::api::MinpsClassTrace> traces(at_ms.size());
1324 for (std::size_t k = 0; k < at_ms.size(); ++k) {
1325 traces[k].arrival_ms = at_ms[k];
1326 traces[k].rt = rt[k];
1327 }
1328 const std::size_t initSample = a.count("initSample");
1329 const std::size_t sampleSize = a.count("sampleSize");
1330 const std::vector<double> muZ = a.vector<double>("muZ");
1331 const double nCores = a.number<double>("nCores");
1332 a.done();
1333 const line::api::MinpsSetup st = line::api::infer_minps_setup(traces, initSample, sampleSize);
1334 if (muZ.size() != st.classMap.size())
1335 throw InputError(w + ": 'muZ' must have one entry per class with samples (" +
1336 std::to_string(st.classMap.size()) + "), in the order of classMap");
1337 Json out;
1338 out["demandEst"] = encode_vector(line::api::infer_minps(muZ, nCores, st.samples));
1339 out["lambda"] = encode_vector(st.lambda);
1340 out["Wexp"] = st.threads;
1341 out["classMap"] = encode_sizes(st.classMap);
1342 return out;
1343 }
1344};
1345
1346
1347/**
1348 * demand = infer_gibbs(data, nbCores, tol, seed). data is the 6 x (K+1) cell
1349 * array, of which rows 3 (arrival times, ms), 4 (response times, s) and 6
1350 * (the think-time observations) of the first K columns are read; tol defaults
1351 * to 1e-3 as in the reference. The test-set draw and the slice sampler take a
1352 * pfqn::McRng named by the required seed: MATLAB uses the session RNG, so its
1353 * estimates agree with these in law, not draw for draw.
1354 */
1355struct OpInferGibbs {
1356 static Json run(Args& a) {
1357 const std::string w = "infer_gibbs";
1358 const Json& data = a.get("data");
1359 std::vector<std::vector<double>> at_ms, rt;
1360 read_trace_cells<double>(data, w + ": data", at_ms, rt);
1361 if (data.size() < 6)
1362 throw InputError(w + ": data needs row 6 (the think-time observations) as well as "
1363 "rows 3 and 4");
1364 std::vector<line::infer::GibbsTrace<double>> traces(at_ms.size());
1365 for (std::size_t k = 0; k < at_ms.size(); ++k) {
1366 traces[k].arrival_ms = at_ms[k];
1367 traces[k].respt_s = rt[k];
1368 traces[k].think_obs =
1369 vector_from_json<double>(data[5][k], w + ": data{6," + std::to_string(k + 1) + "}");
1370 }
1371 const double nbCores = a.number<double>("nbCores");
1372 const double tol = a.scalar<double>("tol", 1e-3);
1373 pfqn::McRng rng(required_seed(a, w));
1374 a.done();
1375 Json out;
1376 out["demand"] = encode_vector(line::infer::infer_gibbs<double>(traces, nbCores, tol, rng));
1377 return out;
1378 }
1379};
1380
1381} // namespace
1382
1384 // trace
1385 t["trace_mean"] = make<OpTraceMean>();
1386 t["trace_var"] = make<OpTraceVar>();
1387 t["trace_scv"] = make<OpTraceScv>();
1388 t["trace_skew"] = make<NeedsTranscendental<OpTraceSkew>>();
1389 t["trace_idc"] = make<OpTraceIdc>();
1390 t["trace_acf"] = make<OpTraceAcf>();
1391 t["trace_bicov"] = make<OpTraceBicov>();
1392 t["trace_gamma"] = make<OpTraceGamma>();
1393 t["trace_iat2bins"] = make<OpTraceIat2bins>();
1394 t["trace_iat2counts"] = make<OpTraceIat2counts>();
1395 t["trace_idi"] = make<OpTraceIdi>();
1396 t["trace_joint"] = make<OpTraceJoint>();
1397 t["trace_pmf"] = make<OpTracePmf>();
1398 t["trace_summary"] = make<NeedsTranscendental<OpTraceSummary>>();
1399 // mtrace
1400 t["mtrace_moment"] = make<OpMtraceMoment>();
1401 t["mtrace_forward_moment"] = make<OpMtraceForwardMoment>();
1402 t["mtrace_backward_moment"] = make<OpMtraceBackwardMoment>();
1403 t["mtrace_cross_moment"] = make<OpMtraceCrossMoment>();
1404 t["mtrace_moment_simple"] = make<OpMtraceMomentSimple>();
1405 t["mtrace_count"] = make<OpMtraceCount>();
1406 t["mtrace_cov"] = make<OpMtraceCov>();
1407 t["mtrace_iat2counts"] = make<OpMtraceIat2counts>();
1408 t["mtrace_joint"] = make<OpMtraceJoint>();
1409 t["mtrace_mean"] = make<OpMtraceMean>();
1410 t["mtrace_merge"] = make<OpMtraceMerge>();
1411 t["mtrace_pc"] = make<OpMtracePc>();
1412 t["mtrace_sigma"] = make<OpMtraceSigma>();
1413 t["mtrace_sigma2"] = make<OpMtraceSigma2>();
1414 t["mtrace_split"] = make<OpMtraceSplit>();
1415 t["mtrace_summary"] = make<OpMtraceSummary>();
1416 // infer
1417 t["infer_compute_ql_at_arrival"] = make<OpInferComputeQlAtArrival>();
1418 t["infer_get_qlen_arrival"] = make<OpInferGetQlenArrival>();
1419 t["infer_lqn_findbyname"] = make<OpInferLqnFindbyname>();
1420 t["infer_lqn_getobs"] = make<OpInferLqnGetobs>();
1421 t["infer_qmle"] = make<OpInferQmle>();
1422 t["infer_rps"] = make<OpInferRps>();
1423 t["infer_quick_model"] = make<OpInferQuickModel>();
1424 t["infer_nhpp_ks"] = make<NeedsTranscendental<OpInferNhppKs>>();
1425 t["infer_variational"] = make<NeedsTranscendental<OpInferVariational>>();
1426 t["infer_smp"] = make<DoubleOnly<OpInferSmp>>();
1427 t["infer_smp_hazard"] = make<DoubleOnly<OpInferSmpHazard>>();
1428 t["infer_smp_nelson_aalen"] = make<DoubleOnly<OpInferSmpNelsonAalen>>();
1429 t["infer_smp_family"] = make<DoubleOnly<OpInferSmpFamily>>();
1430 t["infer_mlps"] = make<DoubleOnly<OpInferMlps>>();
1431 t["infer_minps_setup"] = make<DoubleOnly<OpInferMinpsSetup>>();
1432 t["infer_gibbs"] = make<DoubleOnly<OpInferGibbs>>();
1433}
1434
1436 t["infer_lqn_jacobian"] =
1437 "its first argument hfun is a function handle (the LQN observation map), which JSON "
1438 "cannot carry.";
1439 t["infer_lqn_ekf"] =
1440 "its first argument hfun is a function handle (the LQN observation map), which JSON "
1441 "cannot carry.";
1442 t["infer_lqn"] =
1443 "it calibrates a LayeredNetwork model in place through a solver callable; no line-cli "
1444 "analysis serves LQN inference yet.";
1445 t["infer_lqn_setparams"] =
1446 "it writes parameters into a LayeredNetwork model object in place; there is no data "
1447 "result to return.";
1448 t["infer_fmlps"] =
1449 "it reads the Network model and the station node (NetworkStruct), not a data argument; "
1450 "no line-cli analysis serves demand inference yet.";
1451 t["infer_smp_env"] =
1452 "it takes the per-stage Network models and returns an Environment model object; solve "
1453 "the resulting environment with line-cli -s env.";
1454}
1455
1456} // namespace reg
1457} // namespace line
Internal to the –api dispatcher: what a per-domain translation unit needs to add entries to the table...
#define LINE_MTRACE_DIRECTED_MOMENT_OP(Name, fn)
#define LINE_MTRACE_CROSS_MOMENT_OP(Name, fn)
#define LINE_TRACE_SCALAR_OP(Name, fn, outkey)
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
Per-class queue lengths seen by each arriving job, reconstructed from arrival and response time sampl...
Per-class queue lengths at arrival for the per-class sample format.
Gibbs sampling demand estimator for a closed delay-plus-queue model.
First element of a named LQN container matching a name.
Observation vector of a solved LQN, z = h(a).
Turn a raw per-class trace into the sample set MINPS estimates from.
Maximum-likelihood service-demand estimation at a processor-sharing queue.
Kolmogorov-Smirnov tests for a non-homogeneous Poisson arrival process.
Queue-length-based maximum-likelihood estimator of the service demands of a closed queueing network.
Convenience factory for the single-layer networks the inference estimators fit.
Regression for Processor Sharing (RPS) demand estimator.
Maximum-likelihood fit of a parametric semi-Markov multi-state model.
Variational inference for Markovian queueing networks.
Backward moments of a marked trace: the moments of the inter-arrival time that PRECEDES an event of e...
Per-class count process of a marked trace on a fixed resolution: the number of events of each class i...
Class-pair covariance matrices of a marked trace.
Class-pair cross moments of a marked trace: the k-th moment of the interval that separates an event o...
Forward moments of a marked trace: the moments of the inter-arrival time that FOLLOWS an event of eac...
Per-class counting processes of a marked trace: for each arrival, how many events of each class fall ...
Class-dependent joint moments of a marked trace,.
Per-type sample mean of a trace.
Superposes two single-class traces into one marked trace, labelling the events of the first stream 1 ...
Empirical class-dependent moments of a marked trace.
Class-pair cross moments of a marked trace.
Class probabilities of a marked trace, p_c = count_c / N.
Two-step class transition frequencies of a marked trace,.
One-step class transition frequencies of a marked trace,.
Splits a marked trace into its per-class traces: for each class, the inter-arrival times BETWEEN CONS...
Descriptor set of a marked trace: the first five raw moments of the inter-arrival times,...
std::vector< double > infer_minps(const std::vector< double > &muZ, double nCores, const std::vector< MlpsSample > &samples)
MINPS: run MLPS and RPS and keep whichever gives the smaller mean demand.
Definition infer_mlps.h:322
std::vector< double > infer_mlps(const std::vector< double > &muZ, double nCores, const std::vector< MlpsSample > &samples)
MLPS demand estimation at a PS queue.
Definition infer_mlps.h:117
MinpsSetup infer_minps_setup(const std::vector< MinpsClassTrace > &traces, std::size_t initSample, std::size_t sampleSize)
Turn a raw per-class trace into the sample set MINPS estimates from.
NhppKsResult< Tv > infer_nhpp_ks(const std::vector< Tv > &times, const Tv &T, const std::function< Tv(const Tv &)> &cumRate=std::function< Tv(const Tv &)>(), NhppKsMethod method=NhppKsMethod::Lewis, const Tv &T0=num_traits< Tv >::from_int(0))
Kolmogorov-Smirnov tests for a non-homogeneous Poisson arrival process.
static const std::size_t INFER_LQN_NOT_FOUND
Returned when no element carries the requested name, MATLAB's [].
std::size_t infer_smp_family_npar(const std::string &family)
infer_smp_family(F, 'npar'): the number of parameters, validating the name.
Definition infer_smp.h:80
VariationalResult< T > infer_variational(VariationalSpec< T > spec, VariationalOptions< T > opt=VariationalOptions< T >())
Run the variational inference procedure.
std::vector< SmpHazard > infer_smp_hazard(const SmpFit &fit, const std::vector< double > &t, const std::vector< double > &zin=std::vector< double >())
infer_smp_hazard(fit, t, 'z', z): both hazard functions of every arc of a fit on the ascending instan...
Definition infer_smp.h:887
std::vector< T > infer_rps(const std::vector< T > &rt, const std::vector< std::size_t > &cls, const Matrix< T > &ql, long V)
Regression for Processor Sharing (RPS) demand estimator.
Definition infer_rps.h:57
std::vector< double > infer_smp_family_natural(const std::string &family, const std::vector< double > &th)
infer_smp_family(F, 'natural', THETA): rate | shape, scale | mu, sigma.
Definition infer_smp.h:140
qn::Network< T > infer_quick_model(bool is_open, const std::vector< lang::SchedStrategy > &stations, const Matrix< T > &demands, const std::vector< double > &servers=std::vector< double >(), const std::vector< double > &jobs=std::vector< double >(), const std::vector< Matrix< T > > &routing=std::vector< Matrix< T > >())
Build a simple open or closed queueing network.
void infer_smp_family_logfs(const std::string &family, const std::vector< double > &th, const std::vector< double > &t, const std::vector< double > &eta, std::vector< double > &logf, std::vector< double > &logS)
infer_smp_family(F, 'logfs', THETA, T, ETA): log density and log survival at t under the proportional...
Definition infer_smp.h:95
std::size_t infer_lqn_findbyname(const std::vector< std::string > &names, const std::string &name)
First element of a named LQN container matching a name.
std::vector< T > infer_gibbs(const std::vector< GibbsTrace< T > > &data, const T &nbCores, const GibbsOptions &opts, pfqn::McRng &rng)
Estimated per-class mean demands.
Matrix< T > infer_qmle(const Matrix< T > &Q, const std::vector< T > &N, const std::vector< T > &Z)
Queue-length-based maximum-likelihood estimator of the service demands of a closed queueing network.
Definition infer_qmle.h:50
lang::Distrib< double > infer_smp_family_dist(const std::string &family, const std::vector< double > &th, double eta)
infer_smp_family(F, 'dist', THETA, ETA): the LINE distribution of the law with hazard h0(t) exp(eta).
Definition infer_smp.h:187
std::vector< double > infer_smp_family_theta0(const std::string &family, const std::vector< double > &tev, double texp)
infer_smp_family(F, 'theta0', TEV, TEXP): the starting point from the event times tev and the total e...
Definition infer_smp.h:154
std::vector< Matrix< T > > infer_get_qlen_arrival(const std::vector< std::vector< T > > &at_ms, const std::vector< std::vector< T > > &rt)
Per-class queue lengths at arrival for the per-class sample format.
NhppKsMethod
Which test to run on the conditional-uniform data.
std::vector< T > infer_lqn_getobs(const std::vector< std::string > &names, const LqnMetrics< T > &metrics, const std::vector< LqnObsSpec > &spec)
Observation vector of a solved LQN, z = h(a).
Matrix< T > infer_compute_ql_at_arrival(const std::vector< T > &at, const std::vector< long > &at_jobid, const std::vector< T > &rt, const std::vector< long > &rt_jobid, const std::vector< std::size_t > &cls, std::size_t R)
Per-class queue lengths seen by each arriving job, reconstructed from arrival and response time sampl...
SmpFit infer_smp(const std::vector< std::size_t > &from, const std::vector< std::size_t > &to, const std::vector< double > &dur, const SmpOptions &opt=SmpOptions())
infer_smp(from, to, dur, ...): the fit, under opt.approach.
Definition infer_smp.h:671
std::vector< SmpNelsonAalen > infer_smp_nelson_aalen(const std::vector< std::size_t > &from, const std::vector< std::size_t > &to, const std::vector< double > &dur, std::size_t nstates=0)
infer_smp_nelson_aalen(from, to, dur, 'nstates', E): the Nelson-Aalen estimate of the cumulative tran...
Definition infer_smp.h:1028
detail::json network_json_envelope(const qn::NetworkStruct< T > &sn)
The complete model.json envelope: {format, version, model}.
double z(double v)
Definition lqn_focus.h:261
std::string name(const Element *el, const std::string &key="name")
std::mt19937_64 McRng
The generator type every Monte Carlo entry point in this tree accepts.
nlohmann::json Json
Definition api_json.h:63
void register_library_only_trace(LibraryOnlyTable &t)
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
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
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
void register_api_trace(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.
Json encode_ints(const std::vector< int > &v)
Definition api_json.h:350
std::map< std::string, Invoker > DispatchTable
MtraceCountResult< T > mtrace_count(const std::vector< T > &Tv, const std::vector< int > &A, const T &t)
Per-class count process of a marked trace on a fixed resolution: the number of events of each class i...
MtraceJointResult< T > mtrace_joint(const std::vector< T > &Tv, const std::vector< int > &A, unsigned i1, unsigned i2)
Class-dependent joint moments of a marked trace, JM(a) = (1/N_a) sum_{j : A_{j+1} = a}...
MtraceMeanResult< T > mtrace_mean(const std::vector< T > &Tv, long ntypes, const std::vector< int > &type)
Per-type sample mean of a trace.
Definition mtrace_mean.h:50
TraceBicovResult< T > trace_bicov(const std::vector< T > &S, const std::vector< int > &grid)
Bicovariance of a trace on a lag grid.
Definition trace_bicov.h:50
TraceSummary< T > trace_summary(const std::vector< T > &S)
Descriptive summary of a trace: moments, shape, order statistics, autocorrelation and burstiness.
std::vector< std::vector< Matrix< T > > > mtrace_cov(const std::vector< T > &Tv, const std::vector< int > &A)
Class-pair covariance matrices of a marked trace.
Definition mtrace_cov.h:48
TracePmfResult< T > trace_pmf(const std::vector< int > &X)
Empirical probability mass function of a discrete trace (counts, batch sizes, queue-length samples).
Definition trace_pmf.h:54
TraceBinsResult trace_iat2bins(const std::vector< T > &S, const T &scale)
Bins a trace on a fixed time grid: the number of arrivals falling in each interval ((i-1)*scale,...
Matrix< T > mtrace_sigma2(const std::vector< int > &L)
Two-step class transition frequencies of a marked trace, sigma(i,j,h) = #{t : A_t = i,...
MtraceMergeResult< T > mtrace_merge(const std::vector< T > &t1, const std::vector< T > &t2)
Superposes two single-class traces into one marked trace, labelling the events of the first stream 1 ...
MtraceSummary< T > mtrace_summary(const std::vector< T > &Tv, const std::vector< int > &A, int max_lag=100)
Descriptor set of a marked trace: the first five raw moments of the inter-arrival times,...
MtraceCountsResult< T > mtrace_iat2counts(const std::vector< T > &Tv, const std::vector< int > &A, const T &scale)
Per-class counting processes of a marked trace: for each arrival, how many events of each class fall ...
TraceGammaResult< T > trace_gamma(const std::vector< T > &S, long limit=1000, const std::vector< T > &grid=std::vector< T >())
Autocorrelation decay rate of a trace: the gamma of the geometric model rho(k) = rho0 * gamma^k,...
Definition trace_gamma.h:73
MtraceSplitResult< T > mtrace_split(const std::vector< T > &Tv, const std::vector< int > &L)
Splits a marked trace into its per-class traces: for each class, the inter-arrival times BETWEEN CONS...
std::vector< T > mtrace_pc(const std::vector< int > &A)
Class probabilities of a marked trace, p_c = count_c / N.
Definition mtrace_pc.h:42
Matrix< T > mtrace_moment(const std::vector< T > &Tv, const std::vector< int > &A, const std::vector< unsigned > &orders, bool after=false, bool norm=false)
Empirical class-dependent moments of a marked trace.
TraceIdiResult< T > trace_idi(const std::vector< T > &S, const std::vector< long > &k_set, long aggregate_n=0, bool drop_trailing_zero=false)
Index of dispersion for intervals, IDI(k) = k * var(S_t + ... + S_{t+k-1}) / mean(S_t + ....
Definition trace_idi.h:71
T trace_joint(const std::vector< T > &S, const std::vector< int > &lag, const std::vector< unsigned > &order)
Joint moments of a trace, E[X_i^{k_1} X_{i+l_2}^{k_2} ...].
Definition trace_joint.h:53
Matrix< T > mtrace_sigma(const std::vector< int > &L)
One-step class transition frequencies of a marked trace, sigma(i,j) = #{t : A_t = i,...
std::vector< T > trace_acf(const std::vector< T > &S, const std::vector< int > &lags)
Autocorrelation coefficients of a trace at the requested lags.
Definition trace_acf.h:57
std::vector< long > trace_iat2counts(const std::vector< T > &S, const T &scale)
Counting process of a trace: the number of arrivals in the window of length scale that starts at each...
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Reader for the LINE model.json interchange (a Network model) into a qn::Network<T> built through the ...
qn::NetworkStruct -> model.json, the inverse of network_reader.h.
std::vector< double > lambda
per-surviving-class think-time rate
std::vector< MlpsSample > samples
sorted by arrival time, window applied
std::vector< std::size_t > classMap
Original 1-based class label of each surviving class, in the new order.
double threads
Wexp, the largest total queue length seen.
T statistic
the KS distance
std::vector< T > uniforms
after the conditional-uniform transformation
std::size_t n
number of arrivals used
T pvalue
asymptotic p-value
std::vector< T > transformed
after the Durbin step, for the Lewis test
std::size_t from
Definition infer_smp.h:233
std::vector< double > beta
p covariate effects, zero where masked out
Definition infer_smp.h:237
std::string family
Definition infer_smp.h:234
std::vector< double > theta
unconstrained parameters
Definition infer_smp.h:235
std::vector< double > se
standard errors of [theta; beta]
Definition infer_smp.h:238
std::size_t nevents
Definition infer_smp.h:241
std::vector< double > params
natural parameters
Definition infer_smp.h:236
double prob
Approach I: p_ij; Approach II: the embedded probability at z = 0.
Definition infer_smp.h:240
std::size_t nstates
Definition infer_smp.h:247
std::size_t nparams
Definition infer_smp.h:251
std::string approach
Definition infer_smp.h:246
Matrix< double > P
E x E embedded jump chain at z = 0.
Definition infer_smp.h:253
std::vector< SmpArc > arcs
Definition infer_smp.h:249
std::size_t ncov
Definition infer_smp.h:248
double delta
rate added to every feasible transition by the space expansion.
double rate_max
cap on the variational rates; derived from ymax and tmax when negative.
std::vector< Matrix< T > > nu
std::vector< Matrix< T > > Y
std::vector< T > obsRange
(M*R) support size of the uniform contamination.
std::vector< T > arcrate
(T) known rate for transitions with arcparam == 0.
std::vector< T > capacity
(M*R) upper bound on the queue length, empty for none.
std::vector< std::size_t > arcparam
(T) index in 1..P of the rate governing the transition, 0 when known.
Matrix< T > x0
(M x R) initial queue lengths.
Matrix< T > obsData
(K x M*R) observed queue lengths; an unobserved entry is unobserved.
static T unobserved()
Sentinel marking an unobserved entry of obsData.
std::vector< std::array< std::size_t, 3 > > arcs
(T x 3) transitions [i j c]; i==0 external source, j==0 sink.
std::vector< int > sched
(M) discipline codes.
std::vector< T > obsTimes
(K) observation epochs.
std::vector< T > alpha0
(P) Gamma prior shapes and rates.
std::vector< T > nservers
(M) number of servers.
std::vector< T > routeprob
(T) routing probability of each transition.
T epsilon
probability that a reading is faulty.
std::vector< long > count
interior events of each label; 0 = undefined
std::vector< T > jm
one moment per label 1..max(A)
std::vector< T > mean
one mean per type; undefined where count = 0
Definition mtrace_mean.h:38
std::vector< long > count
samples of each type
Definition mtrace_mean.h:39
std::vector< int > labels
1 for the first stream, 2 for the second
std::vector< T > times
inter-arrival times of the merged process
Matrix< T > Pab
one-step class transition frequencies
std::vector< T > Pc
class probabilities
std::vector< T > M
raw moments of order 1..5
Matrix< T > C2
class-pair cross moments of order 1 and 2
Matrix< T > B2
backward moments of order 1 and 2
std::vector< T > acf
autocorrelation at the requested lags
Matrix< T > F2
forward moments of order 1 and 2
std::vector< std::vector< int > > lags
the [1,i,j] increment triples
Definition trace_bicov.h:40
std::vector< T > bicov
one moment per grid pair
Definition trace_bicov.h:39
std::vector< long > counts
arrivals per bin
std::vector< long > membership
1-based bin index of each binned arrival
T rho0
(1 - 1/scv)/2, the GE-type lag-0 amplitude
Definition trace_gamma.h:58
T gamma
best decay rate on the grid
Definition trace_gamma.h:57
T residuals
sum of squared deviations from the fitted model
Definition trace_gamma.h:59
std::vector< T > idi
one value per requested k
Definition trace_idi.h:53
std::vector< long > support
the number of points each value rests on
Definition trace_idi.h:54
std::vector< T > pmf
relative frequency of each distinct value
Definition trace_pmf.h:43
std::vector< int > values
the distinct values, in increasing order
Definition trace_pmf.h:44
T mad
median absolute deviation about the median
T kurt_excess
population kurtosis minus 3
std::vector< T > acf
lags 1..4
T skew
bias-corrected skewness, see trace_skew
Autocorrelation coefficients of a trace at the requested lags.
Bicovariance of a trace on a lag grid.
Autocorrelation decay rate of a trace: the gamma of the geometric model rho(k) = rho0 * gamma^k,...
Bins a trace on a fixed time grid: the number of arrivals falling in each interval ((i-1)*scale,...
Counting process of a trace: the number of arrivals in the window of length scale that starts at each...
Index of dispersion for counts, estimated by its asymptotic equality with the index of dispersion for...
Index of dispersion for intervals,.
Joint moments of a trace, E[X_i^{k_1} X_{i+l_2}^{k_2} ...].
Sample mean of a trace.
Empirical probability mass function of a discrete trace (counts, batch sizes, queue-length samples).
Squared coefficient of variation of a trace, var/mean^2.
Bias-corrected sample skewness (MATLAB's skewness(S,0), equivalently the G1 estimator).
Descriptive summary of a trace: moments, shape, order statistics, autocorrelation and burstiness.
Sample variance of a trace.