LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_facade.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
6/**
7 * @file
8 * The one translation unit that instantiates the solver stack for the facade.
9 *
10 * `line/solvers/solver.h` declares a `double`-only, non-template solver API so
11 * that this file can carry every body: a caller including that header pays for
12 * none of the template instantiation below, and `line_mp_api` pays for it once
13 * for the CLI, the tests and the examples together.
14 *
15 * Every entry point forwards to the runner the CLI calls, so a program written
16 * against the facade and `line-cli -s <solver>` on the same model produce the
17 * same numbers by construction. Nothing is computed here beyond the reshaping
18 * the tables need.
19 */
20
21#include "line/solvers/solver.h"
22
23#include <algorithm>
24#include <cctype>
25#include <cstdio>
26#include <iomanip>
27#include <iostream>
28#include <limits>
29#include <map>
30#include <sstream>
31#include <utility>
32
35#include "line/lang/qn/nodes.h"
54
55namespace line {
56
57// ---------------------------------------------------------------------------
58// AvgTable
59// ---------------------------------------------------------------------------
60
61double AvgTable::get(const std::string& col, const std::string& station,
62 const std::string& jobclass) const {
63 for (std::size_t i = 0; i < Station.size(); ++i) {
64 if (Station[i] != station || JobClass[i] != jobclass) continue;
65 if (col == "QLen") return QLen[i];
66 if (col == "Util") return Util[i];
67 if (col == "RespT") return RespT[i];
68 if (col == "ResidT") return ResidT[i];
69 if (col == "ArvR") return ArvR[i];
70 if (col == "Tput") return Tput[i];
71 throw InputError("AvgTable::get: unknown column '" + col + "'");
72 }
73 return std::numeric_limits<double>::quiet_NaN();
74}
75
76std::vector<double> AvgTable::column(const std::string& col) const {
77 if (col == "QLen") return QLen;
78 if (col == "Util") return Util;
79 if (col == "RespT") return RespT;
80 if (col == "ResidT") return ResidT;
81 if (col == "ArvR") return ArvR;
82 if (col == "Tput") return Tput;
83 throw InputError("AvgTable::column: unknown column '" + col + "'");
84}
85
86namespace {
87
88template <class Keep>
89AvgTable filter_avg_table(const AvgTable& source, Keep keep) {
90 AvgTable out;
91 out.solver = source.solver;
92 out.method = source.method;
93 out.iter = source.iter;
95 out.lognormconst = source.lognormconst;
96 out.ListCost = source.ListCost;
97 out.warning = source.warning;
98 out.SysClass = source.SysClass;
99 out.SysRespT = source.SysRespT;
100 out.SysTput = source.SysTput;
101 for (std::size_t i = 0; i < source.Station.size(); ++i) {
102 if (!keep(i)) continue;
103 out.Station.push_back(source.Station[i]);
104 out.JobClass.push_back(source.JobClass[i]);
105 out.QLen.push_back(source.QLen[i]);
106 out.Util.push_back(source.Util[i]);
107 out.RespT.push_back(source.RespT[i]);
108 out.ResidT.push_back(source.ResidT[i]);
109 out.ArvR.push_back(source.ArvR[i]);
110 out.Tput.push_back(source.Tput[i]);
111 }
112 return out;
113}
114
115} // namespace
116
117AvgTable AvgTable::filter_by(const std::string& name) const {
118 return filter_avg_table(*this, [&](std::size_t i) {
119 return Station[i] == name || JobClass[i] == name;
120 });
121}
122
123AvgTable AvgTable::filter_by(const std::string& station, const std::string& jobclass) const {
124 return filter_avg_table(*this, [&](std::size_t i) {
125 return Station[i] == station && JobClass[i] == jobclass;
126 });
127}
128
130 return filter_avg_table(*this, [&](std::size_t i) {
131 return Station[i] == node.get_name();
132 });
133}
134
135AvgTable AvgTable::filter_by(const ::line::JobClass& jobclass) const {
136 return filter_avg_table(*this, [&](std::size_t i) {
137 return JobClass[i] == jobclass.get_name();
138 });
139}
140
141AvgTable AvgTable::filter_by(const Node& node, const ::line::JobClass& jobclass) const {
142 return filter_by(node.get_name(), jobclass.get_name());
143}
144
145AvgTable AvgTable::filter_by(const ::line::JobClass& jobclass, const Node& node) const {
146 return filter_by(node, jobclass);
147}
148
149AvgTable AvgTable::get(const Node& node) const { return filter_by(node); }
150AvgTable AvgTable::get(const ::line::JobClass& jobclass) const { return filter_by(jobclass); }
151AvgTable AvgTable::get(const Node& node, const ::line::JobClass& jobclass) const {
152 return filter_by(node, jobclass);
153}
154AvgTable AvgTable::get(const ::line::JobClass& jobclass, const Node& node) const {
155 return filter_by(node, jobclass);
156}
157AvgTable AvgTable::tget(const Node& node) const { return filter_by(node); }
158AvgTable AvgTable::tget(const ::line::JobClass& jobclass) const { return filter_by(jobclass); }
159AvgTable AvgTable::tget(const Node& node, const ::line::JobClass& jobclass) const {
160 return filter_by(node, jobclass);
161}
162AvgTable AvgTable::tget(const ::line::JobClass& jobclass, const Node& node) const {
163 return filter_by(node, jobclass);
164}
165AvgTable AvgTable::operator()(const Node& node) const { return filter_by(node); }
166AvgTable AvgTable::operator()(const ::line::JobClass& jobclass) const {
167 return filter_by(jobclass);
168}
169AvgTable AvgTable::operator()(const Node& node, const ::line::JobClass& jobclass) const {
170 return filter_by(node, jobclass);
171}
172AvgTable AvgTable::operator()(const ::line::JobClass& jobclass, const Node& node) const {
173 return filter_by(node, jobclass);
174}
175
176void AvgTable::print(std::ostream& out) const {
177 const std::ios::fmtflags flags = out.flags();
178 const std::streamsize precision = out.precision();
179 out << std::left << std::setw(16) << "Station" << std::setw(14) << "JobClass"
180 << std::right << std::setw(12) << "QLen" << std::setw(12) << "Util"
181 << std::setw(12) << "RespT" << std::setw(12) << "ResidT"
182 << std::setw(12) << "ArvR" << std::setw(12) << "Tput" << '\n';
183 out << std::setprecision(6);
184 for (std::size_t i = 0; i < Station.size(); ++i)
185 out << std::left << std::setw(16) << Station[i] << std::setw(14) << JobClass[i]
186 << std::right << std::setw(12) << QLen[i] << std::setw(12) << Util[i]
187 << std::setw(12) << RespT[i] << std::setw(12) << ResidT[i]
188 << std::setw(12) << ArvR[i] << std::setw(12) << Tput[i] << '\n';
189 out.flags(flags);
190 out.precision(precision);
191}
192
193void AvgTable::print() const { print(std::cout); }
194
195std::ostream& operator<<(std::ostream& out, const AvgTable& table) {
196 table.print(out);
197 return out;
198}
199
200namespace {
201
202/** Fill the label columns and the per-class system metrics of a table. */
203void fill_table(AvgTable& t, const qn::NetworkStruct<double>& sn, const mva::AvgResult<double>& r) {
204 for (std::size_t i = 0; i < sn.nstations; ++i)
205 for (std::size_t c = 0; c < sn.nclasses; ++c) {
206 const double q = r.QN(i, c), u = r.UN(i, c), rt = r.RN(i, c);
207 const double w = r.WN(i, c), a = r.AN(i, c), x = r.TN(i, c);
208 if (q == 0.0 && u == 0.0 && rt == 0.0 && w == 0.0 && a == 0.0 && x == 0.0) continue;
209 t.Station.push_back(sn.stations[i].name);
210 t.JobClass.push_back(sn.classes[c].name);
211 t.QLen.push_back(q);
212 t.Util.push_back(u);
213 t.RespT.push_back(rt);
214 t.ResidT.push_back(w);
215 t.ArvR.push_back(a);
216 t.Tput.push_back(x);
217 }
218 for (std::size_t c = 0; c < sn.nclasses && c < r.CN.size(); ++c) {
219 t.SysClass.push_back(sn.classes[c].name);
220 t.SysRespT.push_back(r.CN[c]);
221 t.SysTput.push_back(c < r.XN.size() ? r.XN[c] : 0.0);
222 }
223 t.iter = r.iter;
224 t.warning = r.warning;
225 t.ListCost = r.listcost;
226 if (r.lognormconst.has_value()) {
227 t.has_lognormconst = true;
228 t.lognormconst = r.lognormconst.value();
229 }
230}
231
232/**
233 * The same fill for a solver whose solution type is not `mva::AvgResult`.
234 *
235 * THE SSA AND FLUID ARMS OWE BOTH DERIVED COLUMNS. Neither analyzer reports a
236 * residence time or an arrival rate of its own, and neither is a copy of its
237 * neighbour: ResidT is the per-JOB time and RespT the per-VISIT one, and they
238 * agree only where every station is visited once per cycle. ArvR is a FLOW and
239 * parts from the throughput at any station a job leaves by another route.
240 * `sn_get_residt_from_respt` and `sn_get_arvr_from_tput` are the reference's own
241 * conversions and are what the CLI's `-s fluid` and `-s ssa` arms apply. A
242 * Source's ArvR stays zero: nothing arrives TO it.
243 */
244template <class Sol>
245void fill_table_sim(AvgTable& t, const qn::NetworkStruct<double>& sn, const Sol& r) {
246 const std::size_t M = sn.nstations, K = sn.nclasses;
247 Matrix<double> RN(M, K), TN(M, K);
248 for (std::size_t i = 0; i < M; ++i)
249 for (std::size_t c = 0; c < K; ++c) {
250 RN(i, c) = r.RN(i, c);
251 TN(i, c) = r.TN(i, c);
252 }
255 for (std::size_t i = 0; i < M; ++i)
256 for (std::size_t c = 0; c < K; ++c) {
257 const double q = r.QN(i, c), u = r.UN(i, c), rt = r.RN(i, c), x = r.TN(i, c);
258 const bool is_source = sn.stations[i].sched == lang::SchedStrategy::EXT;
259 const double w = WN(i, c), a = is_source ? 0.0 : AN(i, c);
260 // The all-zero row test over ALL SIX columns, as `avg_rows` and the
261 // reference make it: a station only ARRIVALS reach still has a row.
262 if (q == 0.0 && u == 0.0 && rt == 0.0 && w == 0.0 && a == 0.0 && x == 0.0) continue;
263 t.Station.push_back(sn.stations[i].name);
264 t.JobClass.push_back(sn.classes[c].name);
265 t.QLen.push_back(q);
266 t.Util.push_back(u);
267 t.RespT.push_back(rt);
268 t.ResidT.push_back(w);
269 t.ArvR.push_back(a);
270 t.Tput.push_back(x);
271 }
272 for (std::size_t c = 0; c < sn.nclasses && c < r.CN.size(); ++c) {
273 t.SysClass.push_back(sn.classes[c].name);
274 t.SysRespT.push_back(r.CN[c]);
275 t.SysTput.push_back(c < r.XN.size() ? r.XN[c] : 0.0);
276 }
277}
278
279/**
280 * The LDES fill. Its solution type carries the six metrics separately and its
281 * per-class CN/XN are (1 x nclasses) MATRICES rather than vectors, so neither
282 * `fill_table` nor `fill_table_sim` can read it.
283 *
284 * ResidT IS RECOMPUTED RATHER THAN TAKEN, which is what the CLI's `-a avg` LDES
285 * arm does and for the same reason: the engine counts ONE VISIT PER STATION, so
286 * the residence time it reports is its response time wherever a visit ratio is
287 * not 1.
288 */
289void fill_table_ldes(AvgTable& t, const qn::NetworkStruct<double>& sn,
290 const ldes::LdesResult& r) {
292 for (std::size_t i = 0; i < sn.nstations; ++i)
293 for (std::size_t c = 0; c < sn.nclasses; ++c) {
294 const double q = r.QN(i, c), u = r.UN(i, c), rt = r.RN(i, c);
295 const double w = WNfix(i, c), a = r.AN(i, c), x = r.TN(i, c);
296 if (q == 0.0 && u == 0.0 && rt == 0.0 && w == 0.0 && a == 0.0 && x == 0.0) continue;
297 t.Station.push_back(sn.stations[i].name);
298 t.JobClass.push_back(sn.classes[c].name);
299 t.QLen.push_back(q);
300 t.Util.push_back(u);
301 t.RespT.push_back(rt);
302 t.ResidT.push_back(w);
303 t.ArvR.push_back(a);
304 t.Tput.push_back(x);
305 }
306 for (std::size_t c = 0; c < sn.nclasses && c < r.CN.cols(); ++c) {
307 t.SysClass.push_back(sn.classes[c].name);
308 t.SysRespT.push_back(r.CN(0, c));
309 t.SysTput.push_back(c < r.XN.cols() ? r.XN(0, c) : 0.0);
310 }
311}
312
313/**
314 * The caller-facing `map_env` knobs, as the gate wants them.
315 *
316 * They live on `SolverOptions` and NOT on `MvaOptions`/`NcSolverOptions`/
317 * `FluidOptions`, because no `runAnalyzer` in the reference reads them: the
318 * fallback is decided one level ABOVE the runner, in `getAvg`, which is what
319 * this facade's `avg_table` plays the part of here.
320 */
321solvers::MapEnvConfig map_env_config(const SolverOptions& o) {
323 if (!o.map_env.empty()) c.mode = o.map_env;
324 if (!o.map_env_method.empty()) c.method = o.map_env_method;
325 if (o.map_env_maxstages) c.max_stages = o.map_env_maxstages;
326 return c;
327}
328
329ctmc::CtmcOptions ctmc_options(const SolverOptions& o) {
331 if (!o.method.empty()) c.method = o.method;
332 if (o.cutoff > 0.0) c.cutoff = o.cutoff;
333 if (!o.cutoff_vec.empty()) c.cutoff_vec = o.cutoff_vec;
334 if (o.state_max) c.state_max = o.state_max;
335 return c;
336}
337
338/**
339 * The symbolic knobs, which live on their own struct rather than on
340 * CtmcOptions: the backend and its timeout configure the computer-algebra
341 * request, not the generator build. An empty or non-positive field keeps the
342 * engine default, as everywhere else here.
343 */
344ctmc::CtmcSymbolicOptions ctmc_symbolic_options(const SolverOptions& o) {
346 if (!o.symbolic.empty()) s.backend = o.symbolic;
347 if (o.symbolic_timeout > 0) s.timeout_s = o.symbolic_timeout;
348 return s;
349}
350
351fluid::FluidOptions fluid_options(const SolverOptions& o) {
353 if (!o.method.empty()) f.method = o.method;
354 if (o.tol >= 0.0) f.tol = o.tol;
355 if (o.iter_tol >= 0.0) f.iter_tol = o.iter_tol;
356 if (o.iter_max >= 0) f.iter_max = static_cast<std::size_t>(o.iter_max);
357 if (o.timespan_end > 0.0) f.timespan_end = o.timespan_end;
358 if (!o.init_sol.empty()) f.init_sol = o.init_sol;
359 if (o.seed) f.seed = o.seed;
360 if (!o.highvar.empty()) f.highvar = o.highvar;
361 f.stiff = o.stiff;
362 return f;
363}
364
365/**
366 * The JMT client's options.
367 *
368 * `timespan_end` becomes `max_simulated_time`, which is the finite horizon the
369 * replicated arms REQUIRE, and `replications` is `options.config.replications`, the
370 * size of their ensemble. `iter_max` means nothing to JMT and is not read.
371 */
372jmt::JmtOptions jmt_options(const SolverOptions& o) {
374 if (!o.method.empty()) j.method = o.method;
375 if (o.samples) j.samples = static_cast<double>(o.samples);
376 if (o.seed) j.seed = static_cast<long>(o.seed);
377 if (o.replications > 0) j.replications = o.replications;
378 if (o.timespan_end > 0.0) j.max_simulated_time = o.timespan_end;
379 j.keep = o.keep;
380 j.verbose = o.verbose;
381 return j;
382}
383
384/** `AUTO` resolves to the solver its feature set picks, as the CLI's auto arm does. */
385std::string resolve_auto(const std::string& name, const qn::NetworkStruct<double>& sn) {
386 if (name != "AUTO") return name;
388 std::transform(picked.begin(), picked.end(), picked.begin(), ::toupper);
389 if (picked == "FLUID") picked = "FLD";
390 return picked;
391}
392
393} // namespace
394
395// ---------------------------------------------------------------------------
396// NetworkSolver
397// ---------------------------------------------------------------------------
398
400 if (solved_) return table_;
401 AvgTable t;
402 t.solver = name_;
404 const SolverOptions& o = opts_;
405
406 if (name_ == "LQNS")
407 throw UnsupportedError("avg_table: '" + name_ +
408 "' is not wrapped by this facade; its Network path is "
409 "lqns::solve_network_run_analyzer");
410
411 const std::string name = resolve_auto(name_, sn);
412
413 if (name == "MVA") {
415 if (!o.method.empty()) opt.method = o.method;
416 if (o.tol >= 0.0) opt.tol = o.tol;
417 if (o.iter_tol >= 0.0) opt.iter_tol = o.iter_tol;
418 if (o.iter_max >= 0) opt.iter_max = o.iter_max;
419 if (!o.multiserver.empty()) opt.multiserver = o.multiserver;
420 if (!o.highvar.empty()) opt.highvar = o.highvar;
421 if (!o.np_priority.empty()) opt.np_priority = o.np_priority;
422 if (!o.fork_join.empty()) opt.fork_join = o.fork_join;
423 Matrix<double> init;
425 sn, "SolverMVA", qn::mva_feature_set(mva::resolve_method(sn, opt.method), sn),
426 map_env_config(o),
427 [&opt, &init](const qn::NetworkStruct<double>& m) {
428 return mva::solver_mva_run_analyzer(m, opt, init);
429 },
430 solvers::mva_stage_fn(opt), opt.method);
431 t.method = r.actualmethod;
432 fill_table(t, sn, r);
433 } else if (name == "NC") {
435 if (!o.method.empty()) opt.method = o.method;
436 if (o.tol >= 0.0) opt.tol = o.tol;
437 if (o.iter_tol >= 0.0) opt.iter_tol = o.iter_tol;
438 if (o.iter_max >= 0) opt.iter_max = o.iter_max;
439 if (!o.highvar.empty()) opt.highvar = o.highvar;
440 if (!o.multiserver.empty()) opt.multiserver = o.multiserver;
441 if (!o.fork_join.empty()) opt.fork_join = o.fork_join;
442 if (o.samples) opt.samples = o.samples;
443 if (o.seed) opt.seed = o.seed;
445 sn, "SolverNC", qn::nc_feature_set(opt.method), map_env_config(o),
446 [&opt](const qn::NetworkStruct<double>& m) { return nc::solver_nc_run_analyzer(m, opt); },
447 solvers::nc_stage_fn(opt), opt.method);
448 t.method = r.actualmethod;
449 fill_table(t, sn, r);
450 } else if (name == "CTMC") {
451 const ctmc::CtmcOptions opt = ctmc_options(o);
453 t.method = r.actualmethod;
454 fill_table(t, sn, r);
455 } else if (name == "MAM") {
457 if (!o.method.empty()) opt.method = o.method;
458 if (o.tol >= 0.0) opt.tol = o.tol;
459 if (o.iter_max >= 0) opt.iter_max = o.iter_max;
461 t.method = r.actualmethod;
462 fill_table(t, sn, r);
463 } else if (name == "BA") {
464 // DELIBERATELY NOT WRAPPED IN `run_avg`. A bound request must be answered
465 // with a bound, and the environment image is an APPROXIMATION of the
466 // model, so bounds computed on it do not bracket the original. The
467 // reference refuses SolverBA inside `needsMapEnv` itself; here the
468 // exclusion is the absence of the wrapper, so do not "fix" the
469 // inconsistency with the arms above.
471 if (!o.method.empty()) opt.method = o.method;
472 opt.level = o.level;
474 t.method = r.actualmethod;
475 fill_table(t, sn, r);
476 } else if (name == "SSA") {
478 if (!o.method.empty()) opt.method = o.method;
479 if (o.samples) opt.samples = o.samples;
480 if (o.seed) opt.seed = o.seed;
481 if (!o.state_space_gen.empty()) opt.state_space_gen = o.state_space_gen;
482 opt.verbose = o.verbose;
484 t.method = r.method;
485 fill_table_sim(t, sn, r);
486 } else if (name == "FLD" || name == "FLUID") {
487 const fluid::FluidOptions opt = fluid_options(o);
488 // The fluid arm cannot go through `run_avg`: its runner reports a
489 // `FluidSolution` and the driver an `AvgResult`, so the two halves fill
490 // the table through different helpers. The DECISION is the same one
491 // `run_avg` makes, asked here directly rather than duplicated.
492 const solvers::MapEnvConfig mecfg = map_env_config(o);
495 if (d.needed) {
497 sn, "SolverFLD", mecfg, solvers::fluid_stage_fn(opt), opt.method);
498 t.method = r.actualmethod;
499 fill_table(t, sn, r);
500 } else {
502 t.method = r.method;
503 t.iter = static_cast<int>(r.iters);
504 fill_table_sim(t, sn, r);
505 }
506 } else if (name == "JMT") {
507 const jmt::JmtOptions opt = jmt_options(o);
509 t.method = r.avg.actualmethod;
510 fill_table(t, sn, r.avg);
511 } else if (name == "LDES") {
513 if (!o.method.empty()) opt.method = o.method;
514 if (o.samples) opt.samples = o.samples;
515 if (o.seed) opt.seed = static_cast<long>(o.seed);
516 opt.verbose = o.verbose;
518 t.method = opt.method;
519 fill_table_ldes(t, sn, r);
520 } else {
521 throw UnsupportedError("avg_table: unknown solver '" + name_ + "'");
522 }
523 table_ = t;
524 solved_ = true;
525 return table_;
526}
527
528std::string NetworkSolver::method_used() { return avg_table().method; }
529
530std::vector<std::string> NetworkSolver::list_valid_methods() const {
532 if (name_ == "MVA") return mva::list_valid_methods(sn);
533 if (name_ == "NC") return nc::list_valid_methods();
534 if (name_ == "CTMC") return ctmc::list_valid_methods();
535 if (name_ == "MAM") return mam::list_valid_methods();
536 // The model-aware overload, not the bare one: the reduction bounds are
537 // derived for a single-class closed network of single servers and the three
538 // open-network bounds for its mirror image, so the list a caller may act on
539 // depends on the model. `SolverBA` gates on exactly this list.
540 if (name_ == "BA") return ba::list_valid_methods(sn);
541 if (name_ == "FLD") return fluid::fluid_list_valid_methods();
542 if (name_ == "SSA") return ssa::list_valid_methods();
543 if (name_ == "JMT") return jmt::jmt_list_valid_methods();
544 if (name_ == "LDES") return ldes::list_valid_methods();
545 // AUTO answers about every family it can delegate to, gated by each one's
546 // feature set on this model; see `auto_methods.h`.
547 if (name_ == "AUTO") return autosolver::auto_list_valid_methods(sn);
548 throw UnsupportedError("list_valid_methods: unknown solver '" + name_ + "'");
549}
550
551// ---------------------------------------------------------------------------
552// SolverCTMC
553// ---------------------------------------------------------------------------
554
560
568
569double SolverCTMC::prob_aggr(std::size_t node, const std::vector<double>& state) {
573 const std::size_t ist = sn.nodes[node - 1].station;
574 if (ist == 0) throw InputError("prob_aggr: the node is not a station");
575 const std::size_t K = sn.nclasses;
576 if (state.size() != K) throw InputError("prob_aggr: the state must have one entry per class");
577 double p = 0.0;
578 for (std::size_t s = 0; s < A.rows(); ++s) {
579 bool hit = true;
580 for (std::size_t k = 0; k < K && hit; ++k) hit = A(s, (ist - 1) * K + k) == state[k];
581 if (hit) p += d.pi[s];
582 }
583 return p;
584}
585
586std::vector<double> SolverCTMC::marg_aggr(std::size_t node) {
590 const std::size_t ist = sn.nodes[node - 1].station;
591 if (ist == 0) throw InputError("marg_aggr: the node is not a station");
592 const std::size_t K = sn.nclasses;
593 std::size_t nmax = 0;
594 for (std::size_t s = 0; s < A.rows(); ++s) {
595 double tot = 0.0;
596 for (std::size_t k = 0; k < K; ++k) tot += A(s, (ist - 1) * K + k);
597 nmax = std::max(nmax, static_cast<std::size_t>(tot));
598 }
599 std::vector<double> pmf(nmax + 1, 0.0);
600 for (std::size_t s = 0; s < A.rows(); ++s) {
601 double tot = 0.0;
602 for (std::size_t k = 0; k < K; ++k) tot += A(s, (ist - 1) * K + k);
603 pmf[static_cast<std::size_t>(tot)] += d.pi[s];
604 }
605 return pmf;
606}
607
608std::vector<std::vector<CdfCurve> > SolverCTMC::cdf_respt() {
610 std::vector<std::vector<CdfCurve> > out(sn.nstations, std::vector<CdfCurve>(sn.nclasses));
611 const std::vector<std::vector<ctmc::CdfCurve<double> > > R =
612 ctmc::solver_ctmc_cdf_respt(sn, ctmc_options(opts_));
613 for (std::size_t i = 0; i < R.size() && i < out.size(); ++i)
614 for (std::size_t c = 0; c < R[i].size() && c < out[i].size(); ++c) {
615 out[i][c].t = R[i][c].t;
616 out[i][c].F = R[i][c].F;
617 }
618 return out;
619}
620
624 ctmc::ctmc_symbolic_solution(sn, ctmc_options(opts_), ctmc_symbolic_options(opts_));
626 out.pi = r.pi;
627 out.num = r.num;
628 out.den = r.den;
629 out.engine = r.engine;
630 out.symbols = r.symbols;
631 out.rate0 = r.rate0;
632 return out;
633}
634
636 std::printf("%8s %-28s %s\n", "State", "Marking", "Rates (to: rate)");
637 for (std::size_t i = 0; i < Q.rows(); ++i) {
638 std::string mark;
639 if (i < space.rows()) {
640 std::ostringstream os;
641 os << "[";
642 for (std::size_t c = 0; c < space.cols(); ++c) os << (c ? " " : "") << space(i, c);
643 os << "]";
644 mark = os.str();
645 }
646 std::printf("%8zu %-28s", i + 1, mark.c_str());
647 for (std::size_t j = 0; j < Q.cols(); ++j)
648 if (i != j && Q(i, j) != 0.0) std::printf(" %zu: %.6g", j + 1, Q(i, j));
649 std::printf("\n");
650 }
651}
652
653// ---------------------------------------------------------------------------
654// SolverFLD
655// ---------------------------------------------------------------------------
656
659 const std::vector<fluid::FluidTranPoint> pts =
660 fluid::solver_fluid_tran_avg(sn, fluid_options(opts_), 100);
661 TranAvg out;
662 for (std::size_t i = 0; i < sn.nstations; ++i)
663 for (std::size_t c = 0; c < sn.nclasses; ++c)
664 out.label.push_back(sn.stations[i].name + "/" + sn.classes[c].name);
665 for (std::size_t s = 0; s < pts.size(); ++s) {
666 out.t.push_back(pts[s].t);
667 std::vector<double> row;
668 for (std::size_t i = 0; i < sn.nstations; ++i)
669 for (std::size_t c = 0; c < sn.nclasses; ++c) row.push_back(pts[s].QN(i, c));
670 out.QNt.push_back(row);
671 }
672 return out;
673}
674
675std::vector<std::vector<CdfCurve> > SolverFLD::cdf_respt() {
677 std::vector<std::vector<CdfCurve> > out(sn.nstations, std::vector<CdfCurve>(sn.nclasses));
678 const std::vector<std::vector<fluid::FluidPassage> > R =
679 fluid::solver_fluid_cdf_respt(sn, fluid_options(opts_));
680 for (std::size_t i = 0; i < R.size() && i < out.size(); ++i)
681 for (std::size_t c = 0; c < R[i].size() && c < out[i].size(); ++c) {
682 out[i][c].t = R[i][c].t;
683 out[i][c].F = R[i][c].cdf;
684 }
685 return out;
686}
687
688// ---------------------------------------------------------------------------
689// SolverJMT
690// ---------------------------------------------------------------------------
691
692std::vector<std::vector<CdfCurve> > SolverJMT::cdf_respt() {
694 std::vector<std::vector<CdfCurve> > out(sn.nstations, std::vector<CdfCurve>(sn.nclasses));
695 // `true` is the getCdfRespT contract: the model is first solved for its
696 // steady-state queue lengths and the logged run starts preloaded at their
697 // rounded values, which is what makes the seeded curve comparable to the
698 // reference's for the same seed.
699 const std::map<std::pair<std::size_t, std::size_t>, std::vector<std::pair<double, double> > >
700 rd = jmt::jmt_get_cdf_resp_t(sn, jmt_options(opts_), true);
701 for (std::map<std::pair<std::size_t, std::size_t>,
702 std::vector<std::pair<double, double> > >::const_iterator it = rd.begin();
703 it != rd.end(); ++it) {
704 const std::size_t i = it->first.first, c = it->first.second;
705 if (i == 0 || i > out.size() || c == 0 || c > sn.nclasses) continue;
706 // THE PAIRS ARE (F, X), as `ecdf` returns them, and the curve stores
707 // the two apart: reading them the other way round would report a
708 // probability as a time.
709 for (std::size_t j = 0; j < it->second.size(); ++j) {
710 out[i - 1][c - 1].F.push_back(it->second[j].first);
711 out[i - 1][c - 1].t.push_back(it->second[j].second);
712 }
713 }
714 return out;
715}
716
720 TranAvg out;
721 for (std::size_t i = 0; i < sn.nstations; ++i)
722 for (std::size_t c = 0; c < sn.nclasses; ++c)
723 out.label.push_back(sn.stations[i].name + "/" + sn.classes[c].name);
724 out.t = r.t;
725 for (std::size_t g = 0; g < r.t.size(); ++g) {
726 std::vector<double> row;
727 for (std::size_t i = 0; i < sn.nstations; ++i)
728 for (std::size_t c = 0; c < sn.nclasses; ++c)
729 row.push_back(i < r.QNt.size() && c < r.QNt[i].size() && g < r.QNt[i][c].size()
730 ? r.QNt[i][c][g]
731 : 0.0);
732 out.QNt.push_back(row);
733 }
734 return out;
735}
736
737double SolverJMT::prob_aggr(std::size_t node, const std::vector<double>& state) {
739 if (node == 0 || node > sn.nodes.size()) throw InputError("prob_aggr: node index out of range");
740 const std::size_t ist = sn.nodes[node - 1].station;
741 if (ist == 0) throw InputError("prob_aggr: the node is not a station");
742 const jmt::JmtProbAggr r = jmt::jmt_prob_aggr(sn, jmt_options(opts_), ist, state);
743 return r.station[ist - 1];
744}
745
746SamplePath SolverJMT::sample(std::size_t events, std::size_t node) {
748 const jmt::JmtOptions opt = jmt_options(opts_);
749 SamplePath out;
750 if (node) {
751 if (node > sn.nodes.size() || sn.nodes[node - 1].station == 0)
752 throw InputError("sample: the node is not a station, so nothing is logged for it");
754 for (std::size_t c = 0; c < sn.nclasses; ++c) out.label.push_back(sn.classes[c].name);
755 out.t = tr.t;
756 out.state = tr.qlen;
757 return out;
758 }
760 for (std::size_t i = 0; i < sn.nstations; ++i)
761 for (std::size_t c = 0; c < sn.nclasses; ++c)
762 out.label.push_back(sn.stations[i].name + "/" + sn.classes[c].name);
763 out.t = tr.t;
764 for (std::size_t g = 0; g < tr.t.size(); ++g) {
765 std::vector<double> row;
766 for (std::size_t i = 0; i < sn.nstations; ++i)
767 for (std::size_t c = 0; c < sn.nclasses; ++c)
768 row.push_back(i < tr.state.size() && g < tr.state[i].size() &&
769 c < tr.state[i][g].size()
770 ? tr.state[i][g][c]
771 : 0.0);
772 out.state.push_back(row);
773 }
774 return out;
775}
776
777// ---------------------------------------------------------------------------
778// SolverBA
779// ---------------------------------------------------------------------------
780
784 if (!opts_.method.empty()) opt.method = opts_.method;
785 opt.level = opts_.level;
787 BoundsTable t;
788 t.method = opt.method;
789 for (std::size_t i = 0; i < sn.nstations; ++i)
790 for (std::size_t c = 0; c < sn.nclasses; ++c) {
791 if (i < b.keep.size() && c < b.keep[i].size() && !b.keep[i][c]) continue;
792 t.Station.push_back(sn.stations[i].name);
793 t.JobClass.push_back(sn.classes[c].name);
794 t.Qlower.push_back(b.Qlower(i, c));
795 t.Qupper.push_back(b.Qupper(i, c));
796 t.Tlower.push_back(b.Tlower(i, c));
797 t.Tupper.push_back(b.Tupper(i, c));
798 }
799 return t;
800}
801
802} // namespace line
SolverAUTO.listValidMethods: the method names THIS MODEL can actually run.
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
AvgTable table_
Definition solver.h:96
SolverOptions opts_
Definition solver.h:95
std::string method_used()
getMethodUsed(): the method the solve actually resolved to.
Network * model_
Definition solver.h:93
std::vector< std::string > list_valid_methods() const
listValidMethods(): the methods this solver advertises on this model.
const AvgTable & avg_table()
getAvgTable(): the average table, solving on first demand.
std::string name_
Definition solver.h:94
A node of the model: the index it was given, and the model that owns it.
Definition nodes.h:59
const std::string & get_name() const
getName().
Definition nodes.h:64
BoundsTable bounds_table()
getBoundsTable(): the per-class queue-length and throughput bounds.
static void print_inf_gen(const Matrix< double > &Q, const Matrix< double > &space)
CTMC.printInfGen(Q, SS): the generator beside the state it belongs to.
Matrix< double > generator()
getGenerator(): the infinitesimal generator over that space.
double prob_aggr(std::size_t node, const std::vector< double > &state)
getProbAggr(node, state): the aggregate marginal of one state.
Matrix< double > state_space()
getStateSpace(): the aggregate state space, one row per state.
SymbolicSolution symbolic_solution()
getSymbolicSolution(): the stationary law over the rate symbols x1..xE.
std::vector< double > marg_aggr(std::size_t node)
getProbStateAggr(node): the marginal over every state of one station.
std::vector< std::vector< CdfCurve > > cdf_respt()
getCdfRespT(): the response-time CDF per (station, class).
std::vector< std::vector< CdfCurve > > cdf_respt()
getCdfRespT(): the response-time CDF per (station, class).
TranAvg tran_avg()
getTranAvg(): the transient mean queue length per station.
TranAvg tran_avg()
getTranAvg(): E[N](t), averaged over options.config.replications (SolverOptions::replications,...
SamplePath sample(std::size_t events=0, std::size_t node=0)
sampleSysAggr(events), or sampleAggr(node, events) when node is given: one logged trajectory.
std::vector< std::vector< CdfCurve > > cdf_respt()
getCdfRespT(): the EMPIRICAL response-time CDF per (station, class).
double prob_aggr(std::size_t node, const std::vector< double > &state)
getProbAggr(node, state): the time the declared state is held for.
UnsupportedError(const std::string &what)
Definition error.h:51
A network plus its refreshed NetworkStruct.
const NetworkStruct< T > & get_struct()
The refreshed struct, MATLAB's model.getStruct().
The fluid solver's outermost entry point: @@SolverFLD/runAnalyzer.m's method resolution over solver_f...
The log-driven half of SolverJMT: linkAndLog, parseLogs, parseTranState, parseTranRespT,...
Port of @NetworkSolver/mapEnvApprox.m: the solver-agnostic random-environment approximation of a netw...
The stage solvers map_env_approx injects, one per runner that can be a caller.
AutoSolver auto_choose_avg_solver(const qn::NetworkStruct< T > &sn)
const char * auto_solver_name(AutoSolver s)
std::vector< std::string > auto_list_valid_methods(const qn::NetworkStruct< T > &sn)
SolverAUTO.listValidMethods: every method name this model can be asked for.
BaBounds< T > ba_bounds(const qn::NetworkStruct< T > &L, const BaOptions &opt)
Port of SolverBA.getBounds.
mva::AvgResult< T > solver_ba_run_analyzer(const qn::NetworkStruct< T > &L, const BaOptions &opt_in)
Port of @@SolverBA/runAnalyzer.m for the lang='matlab' path.
std::vector< std::string > list_valid_methods()
Port of SolverBA.listValidMethods.
std::vector< std::string > list_valid_methods()
Port of SolverCTMC.listValidMethods.
CtmcGenerator< T > ctmc_get_infgen(const NetworkStruct< T > &sn, const CtmcSolution< T > &d)
@@SolverCTMC/getInfGen.m, a pure alias of getGenerator in the reference.
std::vector< std::vector< CdfCurve< T > > > solver_ctmc_cdf_respt(const NetworkStruct< T > &sn, const CtmcOptions &opt)
Port of @@SolverCTMC/getCdfRespT.m: the per-(station, class) response-time CDF, indexed [ist-1][r-1].
CtmcStateSpace< T > ctmc_get_state_space(const NetworkStruct< T > &, const CtmcSolution< T > &d)
Port of @@SolverCTMC/getStateSpace.m.
Matrix< T > ctmc_get_state_space_aggr(const NetworkStruct< T > &sn, const CtmcOptions &opt)
Port of @@SolverCTMC/getStateSpaceAggr.m: the per-(station, class) job counts of every state,...
CtmcSolution< T > solver_ctmc_analyzer(const NetworkStruct< T > &sn_in, const CtmcOptions &opt)
Port of solver_ctmc_analyzer.m plus the fork-join wrapper of @@SolverCTMC/runAnalyzer....
CtmcSymbolicSolution< T > ctmc_symbolic_solution(const NetworkStruct< T > &sn, const CtmcOptions &opt, const CtmcSymbolicOptions &symopt=CtmcSymbolicOptions())
Port of @@SolverCTMC/getSymbolicSolution.m: pi Q = 0 with sum(pi) = 1 over the field of rational func...
mva::AvgResult< T > solver_ctmc_run_analyzer_any(const NetworkStruct< T > &sn, const CtmcOptions &opt)
Solve on whichever path applies and format, mirroring solver_ctmc_run_analyzer.
std::vector< std::string > fluid_list_valid_methods()
Port of SolverFLD.listValidMethods.
std::vector< FluidTranPoint > solver_fluid_tran_avg(const qn::NetworkStruct< T > &sn, const FluidOptions &opt, std::size_t points=101)
getTranAvg on the first-order closing drift, over that horizon.
FluidSolution solver_fluid_run_analyzer(const qn::NetworkStruct< T > &sn, const FluidOptions &opt, qn::NetworkStruct< T > *sn_out=nullptr, qn::NetworkStruct< T > *refreshed_out=nullptr, solvers::CacheMetrics< T > *cache_out=nullptr)
Port of @@SolverFLD/runAnalyzer.m: resolve the method, route to the function the reference routes to,...
std::vector< std::vector< FluidPassage > > solver_fluid_cdf_respt(const qn::NetworkStruct< T > &sn, const FluidOptions &opt, std::size_t points=201)
Port of @@SolverFLD/getCdfRespT: the response-time law of every (station, class) pair,...
JmtReplication< T > jmt_transient_replications(const qn::NetworkStruct< T > &sn, const JmtOptions &opt)
Port of the transient ensemble of @@SolverJMT/runAnalyzer.m (default over a finite timespan).
Definition jmt_logs.h:906
JmtSysTrace< T > jmt_sample_sys_aggr(const qn::NetworkStruct< T > &sn, std::size_t num_events, const JmtOptions &opt)
Port of sampleSysAggr: every station's trajectory on one time grid.
Definition jmt_logs.h:498
JmtNodeTrace< T > jmt_sample_aggr(const qn::NetworkStruct< T > &sn, std::size_t node, std::size_t num_events, const JmtOptions &opt)
Port of sampleAggr: the queue-length trajectory of one node.
Definition jmt_logs.h:450
std::vector< std::string > jmt_list_valid_methods()
Port of SolverJMT.listValidMethods.
JmtResult< T > solver_jmt_run_analyzer(const qn::NetworkStruct< T > &sn, const JmtOptions &opt_in)
Port of @@SolverJMT/runAnalyzer.m, the jsim and jmva arms.
std::map< std::pair< std::size_t, std::size_t >, std::vector< std::pair< double, double > > > jmt_get_cdf_resp_t(const qn::NetworkStruct< T > &sn, const JmtOptions &opt, bool seed_from_steady=true)
Port of getCdfRespT: the empirical response-time distribution per (station, class),...
Definition jmt_logs.h:567
JmtProbAggr jmt_prob_aggr(const qn::NetworkStruct< T > &sn, const JmtOptions &opt, std::size_t target_station=0, const std::vector< double > &target=std::vector< double >())
Port of getProbAggr and getProbSysAggr, both off ONE instrumented run.
Definition jmt_logs.h:810
std::vector< std::string > list_valid_methods()
Port of SolverLDES.listValidMethods.
LdesResult solver_ldes(const qn::NetworkStruct< T > &sn, const LdesOptions &o, const std::vector< std::string > &extra_flags=std::vector< std::string >())
The same, for a model built through the C++ API.
std::vector< std::string > list_valid_methods()
Port of SolverMAM.listValidMethods.
mva::AvgResult< T > solver_mam_run_analyzer(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of @@SolverMAM/runAnalyzer.m for the lang='matlab' path: solve, then apply the metric filter @@N...
Matrix< T > sn_get_residt_from_respt(const qn::NetworkStruct< T > &L, const Matrix< T > &RN)
Port of sn_get_residt_from_respt: the per-JOB residence time.
std::string resolve_method(const qn::NetworkStruct< T > &L, const std::string &method)
Port of SolverMVA.resolveMethod: the feature-driven default -> rqna upgrade for a bursty single-class...
std::vector< std::string > list_valid_methods(const qn::NetworkStruct< T > &L)
Port of SolverMVA.listValidMethods.
Matrix< T > sn_get_arvr_from_tput(const qn::NetworkStruct< T > &L, const Matrix< T > &TN)
AvgResult< T > solver_mva_run_analyzer(const qn::NetworkStruct< T > &L, const MvaOptions &opt_in, const Matrix< T > &init_sol)
Port of @@SolverMVA/runAnalyzer.m for the lang='matlab' path: gate, solve, convert,...
mva::AvgResult< T > solver_nc_run_analyzer(const qn::NetworkStruct< T > &L_in, const NcSolverOptions &opt_in)
std::vector< std::string > list_valid_methods()
Port of SolverNC.listValidMethods.
FeatureSet fluid_feature_set(const std::string &method)
SolverFLD.getFeatureSet, transcribed, MINUS what the requested method cannot evaluate – the port of @...
FeatureSet nc_feature_set(const std::string &method)
SolverNC.getFeatureSet, 48 names, transcribed unchanged.
FeatureSet mva_feature_set(const std::string &raw_method)
MapEnvDecision needs_map_env(const qn::FeatureSet &declared, const qn::NetworkStruct< T > &sn, const MapEnvConfig &cfg=MapEnvConfig())
needsMapEnv: does this model need the environment image, and would the image make it solvable?
env::EnvStageAvgFn< double > nc_stage_fn(const nc::NcSolverOptions &opt)
NC stages, bound to the caller's own NcSolverOptions.
mva::AvgResult< T > run_avg(const qn::NetworkStruct< T > &sn, const std::string &solver, const qn::FeatureSet &declared, const MapEnvConfig &cfg, Run run, StageFn stage_fn, const std::string &requested_method="default")
The getAvg funnel: run the model, or its environment image when the ONLY thing in the way is a non-re...
Definition map_env.h:327
mva::AvgResult< T > map_env_approx(const qn::NetworkStruct< T > &sn, const std::string &solver, const MapEnvConfig &cfg, StageFn stage_fn, const std::string &requested_method="default")
mapEnvApprox: solve the model through the random-environment image of its non-renewal processes.
Definition map_env.h:186
env::EnvStageAvgFn< double > fluid_stage_fn(const fluid::FluidOptions &opt)
Fluid stages, bound to the caller's own FluidOptions.
env::EnvStageAvgFn< double > mva_stage_fn(const mva::MvaOptions &opt)
MVA stages, bound to the caller's own MvaOptions.
std::vector< std::string > list_valid_methods()
Port of SolverSSA.listValidMethods.
SsaSolution solver_ssa(const qn::NetworkStruct< T > &sn, const SsaOptions &opt, std::vector< SsaCacheRatio > *cache=nullptr)
@@SolverSSA/runAnalyzer itself: the engine the method selects, then the result assembly the reference...
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
std::ostream & operator<<(std::ostream &out, const AvgTable &table)
The model API a user writes, spelled as its Python twin.
The solver API a user writes, spelled as its Python twin.
The SolverAUTO chooser: which solver a model is handed to.
The SolverBA class surface: @@SolverBA/runAnalyzer.m, listValidMethods, getBounds and getBoundsTable.
Port of solver_ctmc_analyzer.m and the parts of @@SolverCTMC/runAnalyzer.m that surround one solve: t...
Port of @@SolverCTMC/getCdfRespT.m and @@SolverCTMC/getCdfSysRespT.m: the exact distribution of the r...
The remaining @@SolverCTMC accessors: getGenerator / getInfGen, getStateSpace / getStateSpaceAggr and...
The SolverCTMC probability family: solver_ctmc_joint, _jointaggr, _marg, _margaggr,...
Port of @@SolverCTMC/getSymbolicGenerator and getSymbolicSolution.
Port of solver_ctmc_fcr_waitq.m: the reachability-built generator of a model whose finite capacity re...
SolverFluid: the closing method, a port of solver_fluid.m, solver_fluid_iteration....
Port of SolverJMT, the Java Modelling Tools client.
Port of SolverLDES, the discrete-event simulator, as its C++ client.
The SolverMAM class surface: @@SolverMAM/runAnalyzer.m and the gates around it.
The SolverMVA class surface: @@SolverMVA/runAnalyzer.m and the gates around it.
The SolverNC class surface: @@SolverNC/runAnalyzer.m and the gates around it.
The SolverSSA entry surface: a port of @@SolverSSA/runAnalyzer.m's method whitelist,...
getAvgTable, one row per (station, class) that carries a metric.
Definition avg_table.h:38
std::vector< double > ArvR
Definition avg_table.h:40
std::vector< double > Tput
Definition avg_table.h:40
std::vector< double > ResidT
Definition avg_table.h:40
void print(std::ostream &out) const
Print the labelled station-class table.
std::vector< double > RespT
Definition avg_table.h:40
double lognormconst
Definition avg_table.h:47
std::vector< double > Util
Definition avg_table.h:40
void print() const
std::vector< std::string > Station
Definition avg_table.h:39
std::vector< double > column(const std::string &column) const
A complete metric column.
std::vector< double > SysTput
Definition avg_table.h:43
std::vector< std::string > JobClass
Definition avg_table.h:39
std::string warning
The reference's own warning text, empty when it did not warn.
Definition avg_table.h:51
double get(const std::string &column, const std::string &station, const std::string &jobclass) const
One cell of the table, by station and class NAME; NaN when absent.
std::string solver
Definition avg_table.h:44
AvgTable tget(const Node &node) const
AvgTable filter_by(const std::string &name) const
Rows whose station or class has name, MATLAB's one-argument filterBy.
std::string method
Definition avg_table.h:44
AvgTable operator()(const Node &node) const
Direct object indexing: table(queue, jobs).
bool has_lognormconst
Definition avg_table.h:46
std::vector< double > ListCost
getAvgCacheTable's ListCost column; empty on a model without item sizes.
Definition avg_table.h:49
std::vector< double > SysRespT
Definition avg_table.h:43
std::vector< std::string > SysClass
getAvgSysTable: system response time and throughput, per class.
Definition avg_table.h:42
std::vector< double > QLen
Definition avg_table.h:40
SolverBA(model, method).getBoundsTable().
Definition avg_table.h:95
std::vector< double > Tlower
Definition avg_table.h:97
std::string method
Definition avg_table.h:98
std::vector< double > Qupper
Definition avg_table.h:97
std::vector< std::string > Station
Definition avg_table.h:96
std::vector< double > Qlower
Definition avg_table.h:97
std::vector< std::string > JobClass
Definition avg_table.h:96
std::vector< double > Tupper
Definition avg_table.h:97
sampleSysAggr / sampleAggr: ONE simulated trajectory, not a mean.
Definition avg_table.h:138
std::vector< std::vector< double > > state
[step][column]
Definition avg_table.h:140
std::vector< std::string > label
what each column counts
Definition avg_table.h:141
std::vector< double > t
event times, ascending
Definition avg_table.h:139
The knobs a solver reads; a negative or empty field keeps the engine default.
std::string multiserver
The options.config fields of the MVA / NC / fluid families.
int level
SolverBA options.level.
std::string np_priority
std::string fork_join
MVA / NC options.config.fork_join: 'default'/'mmt'/'fjt' or 'ht'.
std::string state_space_gen
SSA options.config.state_space_gen.
getSymbolicSolution: the stationary law as a function of the rate symbols.
Definition avg_table.h:114
std::vector< double > rate0
nominal value of each symbol
Definition avg_table.h:119
std::string den
common denominator of the vector
Definition avg_table.h:117
std::vector< std::string > pi
stationary probability of each state
Definition avg_table.h:115
std::vector< std::string > symbols
x1..xE, empty where an event has no positive rate
Definition avg_table.h:118
std::string engine
backend that answered, e.g. "sage"
Definition avg_table.h:120
std::vector< std::string > num
numerator of each entry over den
Definition avg_table.h:116
getTranAvg: the transient mean queue length per (station, class).
Definition avg_table.h:124
std::vector< std::string > label
the (station, class) of each column
Definition avg_table.h:127
std::vector< std::vector< double > > QNt
[step][station*class]
Definition avg_table.h:126
std::vector< double > t
the time axis
Definition avg_table.h:125
Port of SolverBA.getBounds: the {lower,upper} bracket of a family.
std::vector< std::vector< bool > > keep
getBoundsTable's row filter, (M x K): whether the (station, class) pair earns a row.
Matrix< T > Qupper
(M x K), all-NaN on a side the family lacks
The options SolverBA reads.
The SolverCTMC knobs this port honours.
Everything one CTMC solve produces.
std::vector< T > pi
stationary distribution over chain.space
Backend selection, mirroring options.config.symbolic and its timeout.
std::string backend
auto to search, a URL, an image name, or none to stay local.
What @@SolverCTMC/getSymbolicSolution.m returns, plus the engine that answered.
std::string engine
backend that solved it, e.g. sage
std::vector< T > rate0
Nominal value of each symbol, i.e.
std::vector< std::string > symbols
x1..xE, empty for an event with no positive rate.
std::vector< std::string > num
numerator of each entry over den
std::vector< std::string > pi
stationary probability of each state
std::string den
common denominator of the vector
Controls, defaulting to SolverOptions('Fluid') in the reference.
What the analyzer returns, in the same shape as the MVA solver's result.
The per-class queue-length trajectory of one node, plus its event stream.
Definition jmt_logs.h:227
The options of one JMT solve, SolverOptions('JMT') restricted to what is read.
Definition solver_jmt.h:119
std::string method
default | jsim | jmva | jmva.<alg>
Definition solver_jmt.h:120
What jmt_prob_aggr reports: the system probability and the per-station ones.
Definition jmt_logs.h:773
std::vector< double > station
P(station i holds its declared per-class counts).
Definition jmt_logs.h:775
Transient averages over independent replications, on one time grid.
Definition jmt_logs.h:874
std::vector< double > t
Definition jmt_logs.h:875
std::vector< std::vector< std::vector< double > > > QNt
QNt[ist-1][r] over t.
Definition jmt_logs.h:877
The result of a JMT solve: the shared AvgResult plus what only JMT reports.
Definition solver_jmt.h:749
mva::AvgResult< T > avg
Definition solver_jmt.h:750
The system trajectory: one per-class block per station, on a common grid.
Definition jmt_logs.h:480
The knobs of one LDES run.
One ldes-result document, parsed.
The options SolverMAM reads.
Definition mam_types.h:30
The metrics getAvg returns, after filtering.
std::string actualmethod
the algorithm that ran
The options SolverMVA reads.
Definition mva_types.h:31
Controls, defaulting to SolverOptions('NC') in the reference.
Definition nc_types.h:33
The caller-facing map_env knobs, options.config.map_env and friends.
What the gate decided, and what it decided it about.
Controls, defaulting to SolverOptions('SSA') in the reference.
Definition ssa_types.h:69
What the analyzer returns, in the same shape as the MVA and fluid results.
Definition ssa_types.h:101
std::string method
The concrete algorithm, as the reference's method.
Definition ssa_types.h:113