5#ifndef LINE_SOLVERS_WRAPPERS_LDES_SOLVER_LDES_H
6#define LINE_SOLVERS_WRAPPERS_LDES_SOLVER_LDES_H
102#include <sys/utsname.h>
121using Json = nlohmann::json;
128inline std::string shortest(
double v) {
130 for (
int prec = 15; prec <= 17; ++prec) {
131 std::snprintf(buf,
sizeof(buf),
"%.*g", prec, v);
132 if (std::strtod(buf,
nullptr) == v)
return std::string(buf);
134 return std::string(buf);
138inline Matrix<double> mat(
const Json& v) {
139 if (v.is_null() || !v.is_array() || v.empty())
return Matrix<double>();
140 if (!v[0].is_array()) {
141 Matrix<double> m(1, v.size());
142 for (std::size_t j = 0; j < v.size(); ++j)
143 m(0, j) = v[j].is_null() ? std::numeric_limits<double>::quiet_NaN()
144 : v[j].
get<double>();
147 std::size_t cols = 0;
148 for (std::size_t i = 0; i < v.size(); ++i)
149 if (v[i].is_array() && v[i].size() > cols) cols = v[i].size();
150 Matrix<double> m(v.size(), cols, std::numeric_limits<double>::quiet_NaN());
151 for (std::size_t i = 0; i < v.size(); ++i) {
152 if (!v[i].is_array())
continue;
153 for (std::size_t j = 0; j < v[i].size(); ++j)
154 if (!v[i][j].is_null()) m(i, j) = v[i][j].get<
double>();
160inline std::vector<double> vec(
const Json& v) {
161 std::vector<double> out;
162 if (!v.is_array())
return out;
163 out.reserve(v.size());
164 for (std::size_t i = 0; i < v.size(); ++i)
165 out.push_back(v[i].is_null() ? std::numeric_limits<double>::quiet_NaN()
166 : v[i].get<
double>());
170inline const Json& field(
const Json& o,
const char* key) {
171 static const Json null_value;
172 return o.is_object() && o.contains(key) ? o[key] : null_value;
176inline std::string read_file(
const std::string& path) {
177 std::ifstream
in(path.c_str(), std::ios::binary);
178 if (!in)
throw InputError(
"SolverLDES: cannot open the model document '" + path +
"'");
179 std::ostringstream ss;
184inline void write_file(
const std::string& path,
const std::string& text) {
185 std::ofstream out(path.c_str(), std::ios::binary);
186 if (!out)
throw InputError(
"SolverLDES: cannot write '" + path +
"'");
225inline bool document_postdates_native(
const std::string& doc) {
226 static const char* keys[] = {
"\"classDependence\"",
227 "\"loadDependence\"",
"\"jointDependence\""};
228 for (std::size_t i = 0; i <
sizeof(keys) /
sizeof(keys[0]); ++i)
229 if (doc.find(keys[i]) != std::string::npos)
return true;
247 const std::vector<std::string>& extra) {
248 std::vector<std::string> f;
251 f.push_back(std::to_string(budget));
252 f.push_back(
"--seed");
253 f.push_back(std::to_string(o.
seed));
255 f.push_back(
"--method");
259 f.push_back(
"--cnvgon");
261 f.push_back(
"--cnvgtol");
262 f.push_back(detail::shortest(o.
cnvgtol));
265 f.push_back(
"--cnvgbatch");
266 f.push_back(std::to_string(o.
cnvgbatch));
269 f.push_back(
"--cnvgchk");
270 f.push_back(std::to_string(o.
cnvgchk));
274 f.push_back(
"--tranfilter");
278 f.push_back(
"--warmupfrac");
282 f.push_back(
"--tranobs");
283 f.push_back(std::to_string(o.
tranobs));
286 f.push_back(
"--mserbatch");
287 f.push_back(std::to_string(o.
mserbatch));
290 f.push_back(
"--cimethod");
294 f.push_back(
"--obmoverlap");
298 f.push_back(
"--ciminbatch");
302 f.push_back(
"--ciminobs");
303 f.push_back(std::to_string(o.
ciminobs));
306 f.push_back(
"--spectrallowfreqfrac");
312 f.push_back(
"--slotted");
314 f.push_back(
"--slotlength");
321 if ((o.
method ==
"parallel" || o.
method ==
"para") && reps <= 1) reps = 8;
323 f.push_back(
"--replications");
324 f.push_back(std::to_string(reps));
326 f.push_back(
"--numthreads");
331 f.push_back(
"--timespan");
332 f.push_back(detail::shortest(o.
t0) +
"," + detail::shortest(o.
t1));
335 f.push_back(
"--maxtime");
336 f.push_back(detail::shortest(o.
timeout));
339 f.push_back(
"--busyperiod");
346 f.push_back(
"--busyperiod-subnet");
352 for (std::size_t i = 0; i < o.
init_sol.size(); ++i)
353 v += (i ?
"," :
"") + detail::shortest(o.
init_sol[i]);
354 f.push_back(
"--initsol");
361 v += (r && !c ?
";" : (c ?
"," :
"")) + detail::shortest(o.
init_jobs(r, c));
362 f.push_back(
"--initjobs");
366 for (std::size_t i = 0; i < extra.size(); ++i) f.push_back(extra[i]);
385 const std::vector<std::string>& flags) {
387 std::vector<LdesRunner> out;
388 const std::string native = detail::native_ldes_path(dir);
389 if (!native.empty()) {
391 r.
argv.push_back(native);
392 r.
argv.push_back(
"solve");
396 const std::string java = detail::find_java();
397 const std::string jar = dir.empty() ? std::string() : dir +
"/ldes.jar";
398 if (!java.empty() && detail::is_file(jar)) {
400 r.
argv.push_back(java);
401 r.
argv.push_back(
"-jar");
402 r.
argv.push_back(jar);
403 r.
argv.push_back(
"solve");
408 if (out.size() > 1) {
412 bool prefer_jar = detail::document_postdates_native(doc);
413 for (std::size_t i = 0; i < flags.size() && !prefer_jar; ++i)
414 prefer_jar = flags[i] ==
"--respt-samples";
415 if (prefer_jar) std::swap(out[0], out[1]);
426inline detail::Json
ldes_solve_rest(
const std::string& base_url,
const std::string& doc,
427 const std::vector<std::string>& flags,
double timeout) {
428 std::string url = base_url;
429 while (!url.empty() && url[url.size() - 1] ==
'/') url.erase(url.size() - 1);
430 if (url.size() < 6 || url.compare(url.size() - 6, 6,
"/solve") != 0) url +=
"/api/v1/solve";
432 detail::Json payload = detail::Json::object();
433 detail::Json model = detail::Json::object();
434 model[
"content"] = doc;
435 model[
"base64"] =
false;
436 payload[
"model"] = model;
437 detail::Json fl = detail::Json::array();
438 for (std::size_t i = 0; i < flags.size(); ++i) fl.push_back(flags[i]);
439 payload[
"flags"] = fl;
441 const int millis = (timeout > 0.0 && std::isfinite(timeout))
442 ?
static_cast<int>((timeout + 30.0) * 1000.0)
445 if (resp.
body.empty())
447 std::to_string(resp.
status) +
" with no body");
448 detail::Json out = detail::Json::parse(resp.
body,
nullptr,
false);
449 if (out.is_discarded() || !out.is_object())
451 " returned a non-JSON body: " + resp.
body);
452 if (!out.contains(
"status") || out[
"status"].get<std::string>() !=
"ok") {
453 const std::string msg = out.contains(
"message") ? out[
"message"].get<std::string>()
454 : std::string(
"unspecified error");
455 const std::string err = out.contains(
"stderr") ? out[
"stderr"].get<std::string>()
458 (err.empty() ?
"" :
" (engine stderr: " + err +
")"));
460 return out[
"result"];
471 if (d.is_object() && d.contains(
"error"))
473 d[
"error"].get<std::string>());
474 if (!d.is_object() || !d.contains(
"format") || d[
"format"].get<std::string>() !=
"ldes-result")
476 "SolverLDES: the engine wrote a document that is not an ldes-result; the run did not "
480 r.
method = d.contains(
"method") && !d[
"method"].is_null() ? d[
"method"].get<std::string>()
481 : std::string(
"default");
482 if (d.contains(
"runtime")) r.runtime = d[
"runtime"].get<
double>();
483 if (d.contains(
"converged")) r.converged = d[
"converged"].get<
bool>();
484 if (d.contains(
"stoppingReason") && !d[
"stoppingReason"].is_null())
485 r.stopping_reason = d[
"stoppingReason"].get<std::string>();
487 if (d.contains(
"warnings") && d[
"warnings"].is_array())
488 for (
const auto& w : d[
"warnings"]) {
489 r.warnings.push_back(w.get<std::string>());
490 std::fprintf(stderr,
"[SolverLDES] Warning: %s\n", r.warnings.back().c_str());
492 if (d.contains(
"convergenceBatches")) r.convergence_batches = d[
"convergenceBatches"].get<
long>();
493 if (d.contains(
"totalSimulatedEvents"))
494 r.total_simulated_events = d[
"totalSimulatedEvents"].get<
long long>();
496 const detail::Json& dim = detail::field(d,
"dimensions");
497 if (dim.is_object()) {
498 if (dim.contains(
"nstations")) r.nstations = dim[
"nstations"].get<std::size_t>();
499 if (dim.contains(
"nclasses")) r.nclasses = dim[
"nclasses"].get<std::size_t>();
500 if (dim.contains(
"nchains")) r.nchains = dim[
"nchains"].get<std::size_t>();
501 if (dim.contains(
"stationNames"))
502 for (std::size_t i = 0; i < dim[
"stationNames"].size(); ++i)
503 r.station_names.push_back(dim[
"stationNames"][i].get<std::string>());
504 if (dim.contains(
"classNames"))
505 for (std::size_t i = 0; i < dim[
"classNames"].size(); ++i)
506 r.class_names.push_back(dim[
"classNames"][i].get<std::string>());
509 const detail::Json& m = detail::field(d,
"metrics");
510 r.QN = detail::mat(detail::field(m,
"QN"));
511 r.UN = detail::mat(detail::field(m,
"UN"));
512 r.RN = detail::mat(detail::field(m,
"RN"));
513 r.TN = detail::mat(detail::field(m,
"TN"));
514 r.AN = detail::mat(detail::field(m,
"AN"));
515 r.WN = detail::mat(detail::field(m,
"WN"));
516 r.CN = detail::mat(detail::field(m,
"CN"));
517 r.XN = detail::mat(detail::field(m,
"XN"));
518 r.DropRateJoin = detail::mat(detail::field(m,
"DropRateJoin"));
520 const detail::Json& ci = detail::field(d,
"confidenceIntervals");
521 r.QNCI = detail::mat(detail::field(ci,
"QNCI"));
522 r.UNCI = detail::mat(detail::field(ci,
"UNCI"));
523 r.RNCI = detail::mat(detail::field(ci,
"RNCI"));
524 r.TNCI = detail::mat(detail::field(ci,
"TNCI"));
525 r.ANCI = detail::mat(detail::field(ci,
"ANCI"));
526 r.WNCI = detail::mat(detail::field(ci,
"WNCI"));
528 const detail::Json& rp = detail::field(d,
"relativePrecision");
529 r.QNRelPrec = detail::mat(detail::field(rp,
"QNRelPrec"));
530 r.UNRelPrec = detail::mat(detail::field(rp,
"UNRelPrec"));
531 r.RNRelPrec = detail::mat(detail::field(rp,
"RNRelPrec"));
532 r.TNRelPrec = detail::mat(detail::field(rp,
"TNRelPrec"));
534 const detail::Json& sc = detail::field(d,
"sampleCounts");
535 r.QNSamples = detail::mat(detail::field(sc,
"QNSamples"));
536 r.UNSamples = detail::mat(detail::field(sc,
"UNSamples"));
537 r.RNSamples = detail::mat(detail::field(sc,
"RNSamples"));
538 r.TNSamples = detail::mat(detail::field(sc,
"TNSamples"));
540 const detail::Json& fcr = detail::field(d,
"fcr");
541 if (fcr.is_object()) {
542 if (fcr.contains(
"nregions")) r.nregions = fcr[
"nregions"].get<std::size_t>();
543 r.QNfcr = detail::mat(detail::field(fcr,
"QNfcr"));
544 r.UNfcr = detail::mat(detail::field(fcr,
"UNfcr"));
545 r.RNfcr = detail::mat(detail::field(fcr,
"RNfcr"));
546 r.TNfcr = detail::mat(detail::field(fcr,
"TNfcr"));
547 r.ANfcr = detail::mat(detail::field(fcr,
"ANfcr"));
548 r.WNfcr = detail::mat(detail::field(fcr,
"WNfcr"));
549 r.WeightNfcr = detail::mat(detail::field(fcr,
"WeightNfcr"));
550 r.MemOccNfcr = detail::mat(detail::field(fcr,
"MemOccNfcr"));
551 r.DropRateNfcr = detail::mat(detail::field(fcr,
"DropRateNfcr"));
555 const detail::Json& fjb = detail::field(d,
"forkJoin");
556 if (fjb.is_object()) {
557 const detail::Json& fk = detail::field(fjb,
"forks");
558 const detail::Json& jn = detail::field(fjb,
"joins");
559 for (std::size_t p = 0; fk.is_array() && p < fk.size(); ++p)
560 r.fj_forks.push_back(fk[p].is_string() ? fk[p].get<std::string>() : std::string());
561 for (std::size_t p = 0; jn.is_array() && p < jn.size(); ++p)
562 r.fj_joins.push_back(jn[p].is_string() ? jn[p].get<std::string>() : std::string());
563 r.FJQLen = detail::mat(detail::field(fjb,
"FJQLen"));
564 r.FJRespT = detail::mat(detail::field(fjb,
"FJRespT"));
565 r.FJTput = detail::mat(detail::field(fjb,
"FJTput"));
568 const detail::Json& imp = detail::field(d,
"impatience");
569 if (imp.is_object()) {
570 r.renegedCustomers = detail::mat(detail::field(imp,
"renegedCustomers"));
571 r.avgRenegingWaitTime = detail::mat(detail::field(imp,
"avgRenegingWaitTime"));
572 r.renegingRate = detail::mat(detail::field(imp,
"renegingRate"));
573 r.balkedCustomers = detail::mat(detail::field(imp,
"balkedCustomers"));
574 r.balkingProbability = detail::mat(detail::field(imp,
"balkingProbability"));
575 r.retriedCustomers = detail::mat(detail::field(imp,
"retriedCustomers"));
576 r.retrialDropped = detail::mat(detail::field(imp,
"retrialDropped"));
577 r.avgOrbitSize = detail::mat(detail::field(imp,
"avgOrbitSize"));
580 const detail::Json& cm = detail::field(d,
"cacheMetrics");
582 for (detail::Json::const_iterator it = cm.begin(); it != cm.end(); ++it) {
584 c.
hit = detail::mat(detail::field(it.value(),
"hit"));
585 c.
delayed = detail::mat(detail::field(it.value(),
"delayed"));
586 c.
miss = detail::mat(detail::field(it.value(),
"miss"));
587 c.
latency = detail::mat(detail::field(it.value(),
"latency"));
588 c.
hitList = detail::mat(detail::field(it.value(),
"hitList"));
589 c.
itemProb = detail::mat(detail::field(it.value(),
"itemProb"));
590 c.
listCost = detail::mat(detail::field(it.value(),
"listCost"));
591 r.cache_metrics[it.key()] = c;
594 const detail::Json& bp = detail::field(d,
"busyPeriods");
595 if (bp.is_object()) {
596 const detail::Json& targets = detail::field(bp,
"targets");
597 for (std::size_t i = 0; i < targets.size(); ++i) {
599 const detail::Json& tj = targets[i];
600 if (tj.contains(
"name") && tj[
"name"].is_string())
601 t.
name = tj[
"name"].get<std::string>();
602 const detail::Json& st = detail::field(tj,
"stations");
603 for (std::size_t k = 0; k < st.size(); ++k)
604 t.
stations.push_back(
static_cast<std::size_t
>(st[k].get<
double>()));
605 if (tj.contains(
"class") && tj[
"class"].is_number())
606 t.
job_class =
static_cast<int>(tj[
"class"].get<
double>());
607 t.
mean = detail::vec(detail::field(tj,
"mean"));
608 t.
count = detail::vec(detail::field(tj,
"count"));
609 r.busy_periods.push_back(t);
613 const detail::Json& js = detail::field(d,
"jobState");
614 if (js.is_object()) {
615 r.finalJobs = detail::mat(detail::field(js,
"finalJobs"));
616 r.jobDepartures = detail::mat(detail::field(js,
"jobDepartures"));
619 const detail::Json& hist = detail::field(d,
"stateHistogram");
620 if (hist.is_object()) {
621 r.histogram_space = detail::mat(detail::field(hist,
"space"));
622 r.histogram_time = detail::mat(detail::field(hist,
"time"));
623 r.traj_space = detail::mat(detail::field(hist,
"trajSpace"));
624 r.traj_time = detail::mat(detail::field(hist,
"trajTime"));
630 const detail::Json& tran = detail::field(d,
"transient");
631 if (tran.is_object()) {
632 r.t = detail::vec(detail::field(tran,
"t"));
633 const char* keys[3] = {
"QNt",
"UNt",
"TNt"};
634 std::vector<std::vector<Matrix<double>>>* dst[3] = {&r.QNt, &r.UNt, &r.TNt};
635 for (
int q = 0; q < 3; ++q) {
636 const detail::Json& blk = detail::field(tran, keys[q]);
637 if (!blk.is_array())
continue;
638 for (std::size_t i = 0; i < blk.
size(); ++i) {
639 std::vector<Matrix<double>> row;
640 for (std::size_t k = 0; k < blk[i].
size(); ++k)
641 row.push_back(detail::mat(blk[i][k]));
642 dst[q]->push_back(row);
646 const detail::Json& rts = tran.is_object() && tran.contains(
"respTimeSamples")
647 ? tran[
"respTimeSamples"]
648 : detail::field(d,
"respTimeSamples");
650 for (std::size_t i = 0; i < rts.size(); ++i) {
651 std::vector<std::vector<double>> row;
652 for (std::size_t k = 0; k < rts[i].size(); ++k) row.push_back(detail::vec(rts[i][k]));
653 r.respTimeSamples.push_back(row);
677 const std::vector<std::string>& extra_flags =
678 std::vector<std::string>()) {
679 const std::vector<std::string> flags =
ldes_flags(o, extra_flags);
681 const std::string model_path = tmp.
file(
"model.json");
682 const std::string result_path = tmp.
file(
"result.json");
684 detail::write_file(model_path, doc);
692 const std::vector<LdesRunner> runners =
ldes_runners(doc, flags);
697 ?
static_cast<int>(o.
timeout + 30.0)
699 std::string last_err;
700 for (std::size_t i = 0; i < runners.size(); ++i) {
701 std::vector<std::string> argv = runners[i].argv;
702 argv.push_back(model_path);
703 argv.push_back(
"-o");
704 argv.push_back(result_path);
705 for (std::size_t j = 0; j < flags.size(); ++j) argv.push_back(flags[j]);
706 std::remove(result_path.c_str());
709 for (std::size_t j = 0; j < argv.size(); ++j)
line += (j ?
" " :
"") + argv[j];
710 std::fprintf(stderr,
"SolverLDES command: %s\n",
line.c_str());
713 runners[i].
engine.c_str());
719 r.
engine = runners[i].engine;
722 if (p.
exitCode == 0 && detail::is_file(result_path)) {
724 const std::string text = detail::read_file(result_path);
725 detail::Json parsed = detail::Json::parse(text,
nullptr,
false);
726 if (parsed.is_discarded())
729 r.engine = runners[i].engine;
735 std::to_string(runners.size()) +
" runner(s); " + last_err);
740 const std::vector<std::string>& extra_flags =
741 std::vector<std::string>()) {
785 const std::vector<LdesStateQuery>& query) {
788 "SolverLDES: this result carries no state histogram, so no state probability can be "
789 "read from it; the run has to pass --export-histogram");
791 throw InputError(
"SolverLDES: a state probability needs the model's class count");
794 for (std::size_t s = 0; s < nstates; ++s) total += r.
histogram_time(s, 0);
795 if (!(total > 0.0))
return 0.0;
798 for (std::size_t q = 0; q < query.size(); ++q) {
799 if (query[q].station == 0)
800 throw InputError(
"SolverLDES: a state query names station 0; stations are 1-based");
801 const std::size_t last = (query[q].station - 1) * nclasses + nclasses;
804 "SolverLDES: station " + std::to_string(query[q].station) +
805 " is past the end of the state histogram, which holds " + std::to_string(ncols) +
806 " columns for " + std::to_string(nclasses) +
" classes");
809 double matched = 0.0;
810 for (std::size_t s = 0; s < nstates; ++s) {
812 for (std::size_t q = 0; q < query.size() && ok; ++q) {
813 const std::size_t base = (query[q].station - 1) * nclasses;
814 const std::size_t n = std::min(nclasses, query[q].counts.size());
815 for (std::size_t k = 0; k < n; ++k)
816 if (std::fabs(r.
histogram_space(s, base + k) - query[q].counts[k]) > 1e-9) {
823 return matched / total;
846 std::size_t job_class) {
852 std::sort(x.begin(), x.end());
853 const std::size_t n = x.size();
854 std::vector<double> tu, fu;
855 for (std::size_t i = 0; i < n; ++i) {
856 const double f =
static_cast<double>(i + 1) /
static_cast<double>(n);
857 if (i + 1 < n && x[i + 1] == x[i])
continue;
862 for (std::size_t i = 0; i < tu.size(); ++i) {
884 const std::vector<double>& counts, std::size_t nclasses) {
885 std::vector<std::string> flags;
886 flags.push_back(
"--export-histogram");
888 std::vector<LdesStateQuery> q(1);
889 q[0].station = station;
890 q[0].counts = counts;
901 if (target.
rows() == 0 || target.
cols() == 0)
902 throw InputError(
"SolverLDES: getProbSysAggr needs an (nstations x nclasses) target state");
903 std::vector<std::string> flags;
904 flags.push_back(
"--export-histogram");
906 const std::size_t K = target.
cols();
907 std::vector<LdesStateQuery> q(target.
rows());
908 for (std::size_t i = 0; i < target.
rows(); ++i) {
909 q[i].station = i + 1;
910 q[i].counts.resize(K);
911 for (std::size_t k = 0; k < K; ++k) q[i].counts[k] = target(i, k);
924 const std::size_t R =
sn.nclasses;
928 for (std::size_t p = 0; p < out.
npairs(); ++p) {
929 const std::string& fname =
sn.nodes[out.
forks[p] - 1].name;
930 const std::string& jname =
sn.nodes[out.
joins[p] - 1].name;
932 for (std::size_t k = 0; k < r.
fj_forks.size() && k < r.
fj_joins.size(); ++k)
937 if (e == r.
fj_forks.size())
continue;
938 for (
int m = 0; m < 3; ++m)
939 for (std::size_t c = 0; c < R; ++c) {
941 for (std::size_t k = 0; k < r.
class_names.size(); ++k)
947 (e < src[m]->
rows() && cc < src[m]->cols()) ? (*src[m])(e, cc) : 0.0;
948 (*dst[m])(p, c) = std::isfinite(v) ? v : 0.0;
986 std::vector<std::string> out;
987 if (QN.empty() || !
sn.regions.empty())
return out;
988 for (std::size_t i = 0; i <
sn.nodes.size(); ++i)
989 if (
sn.nodes[i].nodetype == qn::NodeType::Fork)
return out;
990 const std::vector<double> njobs =
sn.njobs();
991 for (std::size_t
nc = 0;
nc <
sn.nchains &&
nc <
sn.chains.size(); ++
nc) {
992 double njobs_chain = 0.0;
993 std::vector<std::size_t> inchain;
994 for (std::size_t r = 0; r <
sn.chains[
nc].size() && r < njobs.size(); ++r)
995 if (
sn.chains[
nc][r]) {
996 inchain.push_back(r);
997 njobs_chain += njobs[r];
999 if (!(std::isfinite(njobs_chain) && njobs_chain > 0.0))
continue;
1001 for (std::size_t i = 0; i < QN.rows(); ++i)
1002 for (std::size_t k = 0; k < inchain.size(); ++k)
1003 if (inchain[k] < QN.cols() && !std::isnan(QN(i, inchain[k]))) qtot += QN(i, inchain[k]);
1004 if (std::fabs(qtot - njobs_chain) > std::max(0.05 * njobs_chain, 0.05)) {
1006 std::snprintf(buf,
sizeof(buf),
1007 "LDES returned %.3f jobs for closed chain %zu, whose population is %g: the engine "
1008 "dropped blocked jobs at a finite-capacity station, so these averages describe a "
1009 "different model. Use SolverCTMC/SolverSSA for capped closed networks, or rebuild "
1011 qtot,
nc + 1, njobs_chain);
1020 const std::vector<std::string>& extra_flags =
1021 std::vector<std::string>()) {
1028 for (std::size_t k = 0; k < w.size(); ++k)
1029 std::fprintf(stderr,
"[SolverLDES] Warning: %s\n", w[k].c_str());
ExternalError(const std::string &what)
NumericError(const std::string &what)
A network plus its refreshed NetworkStruct.
static VerboseLevel get_verbose()
The session verbosity.
static void step(const char *fmt,...)
Write one progress line.
std::string file(const std::string &name) const
A file inside it.
The exception types the port throws.
Fork-join section metrics: FJQLen, FJRespT and FJTput of every Fork/Join pair.
Minimal HTTP/1.1 client, enough to talk to a line-*-rest service.
The option and result records of SolverLDES, the discrete-event simulator.
Where the LDES engine is, and whether this machine can run it.
Running progress log of a LINE solver run (the "solver console").
Dense matrix and non-owning view.
Response post_json(const std::string &url, const std::string &json, int timeoutMillis)
POST a JSON document.
void require_finite_pas_buffers(const qn::NetworkStruct< T > &sn)
Refuse an OI/PAS station with no finite buffer before it reaches LDES.
detail::json network_json_envelope(const qn::NetworkStruct< T > &sn)
The complete model.json envelope: {format, version, model}.
solvers::FjMetrics< double > ldes_fj_metrics(const qn::NetworkStruct< T > &sn, const LdesResult &r)
ldesParseForkJoin: the engine's "forkJoin" rows mapped onto the Fork/Join pairs of sn by (fork name,...
double ldes_prob_from_histogram(const LdesResult &r, std::size_t nclasses, const std::vector< LdesStateQuery > &query)
Residence-time probability of an aggregate joint state, from a parsed result.
LdesResult solver_ldes_text(const std::string &doc, const LdesOptions &o, const std::vector< std::string > &extra_flags=std::vector< std::string >())
Runs one LDES simulation on a model.json DOCUMENT and parses its result.
double ldes_prob_sys_aggr(const std::string &doc, const LdesOptions &o, const Matrix< double > &target)
Port of getProbSysAggr: the joint probability of a whole aggregate state.
LdesResult solver_ldes_file(const std::string &path, const LdesOptions &o, const std::vector< std::string > &extra_flags=std::vector< std::string >())
The same, reading the document from a file.
const std::string & ldes_engine_dir()
The directory holding the engine, or empty when there is none.
std::vector< std::string > ldes_flags(const LdesOptions &o, const std::vector< std::string > &extra)
The engine flags of one run, after solve <model> -o <result>.
Matrix< double > ldes_cdf_respt(const LdesResult &r, std::size_t station, std::size_t job_class)
Port of getCdfRespT: the EMPIRICAL response time CDF of one (station, class) pair,...
LdesResult parse_ldes_result(const detail::Json &d)
Parses one ldes-result document.
double ldes_prob_aggr(const std::string &doc, const LdesOptions &o, std::size_t station, const std::vector< double > &counts, std::size_t nclasses)
Port of getProbAggr: the marginal probability of a per-class job count at one station.
detail::Json ldes_solve_rest(const std::string &base_url, const std::string &doc, const std::vector< std::string > &flags, double timeout)
Solves through an LDES REST server and returns its result document.
LdesResult solver_ldes(const qn::NetworkStruct< T > &sn, const LdesOptions &o, const std::vector< std::string > &extra_flags=std::vector< std::string >())
std::vector< LdesRunner > ldes_runners(const std::string &doc, const std::vector< std::string > &flags)
The runners to try, in order (see the file header for why there are two and when the order flips).
std::vector< std::string > ldes_closed_population_warnings(const qn::NetworkStruct< T > &sn, const Matrix< double > &QN)
The same, for a model built through the C++ API.
bool in(const std::string &s, std::initializer_list< const char * > set)
const std::string * get(const Element *el, const std::string &key)
el.get(key), with Python's None as a null pointer.
std::vector< std::pair< std::size_t, std::size_t > > fj_sorted_pairs(const S &sn)
The Fork/Join pairs of a struct, sortrows([forks joins]) over find(sn.fj): ascending in fork node,...
FjMetrics< T > fj_metrics_zero(const S &sn, std::size_t nclasses)
An empty-but-shaped result: every pair of sn, all three matrices zero.
ProcResult capture(const std::vector< std::string > &argv, int timeoutSeconds, bool mergeStderr=false)
Runs a command, capturing stdout and discarding stderr.
std::string trim(const std::string &s)
Trims ASCII whitespace from both ends, as Java's String.trim() does.
Conservation laws of a layered queueing network, enumerated from its structure.
qn::NetworkStruct -> model.json, the inverse of network_reader.h.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
An HTTP response, with the body already de-chunked.
std::string body
Response body, decoded.
int status
HTTP status code.
Per-cache hit/miss/latency, as the cacheMetrics block carries them.
Matrix< double > listCost
(1 x nlists) mean storage cost held per list
Matrix< double > latency
(1 x nclasses) expected retrieval latency
Matrix< double > hitList
(nclasses x nlists) hit probability by list
Matrix< double > itemProb
(nitems x nlists+1) item position law
Matrix< double > delayed
(1 x nclasses) delayed-hit probability
Matrix< double > miss
(1 x nclasses) miss probability
Matrix< double > hit
(1 x nclasses) hit probability
The knobs of one LDES run.
double warmupfrac
–warmupfrac, only for tranfilter=fixed
int ciminbatch
–ciminbatch
double spectral_low_freq_frac
–spectrallowfreqfrac
bool verbose
echo the resolved command line before running it
std::string rest_url
Base URL of an LDES REST server (the imperialqore/ldes container).
double obmoverlap
–obmoverlap; 0 reduces OBM to plain batch means
std::vector< std::vector< std::size_t > > busy_period_subnets
–busyperiod-subnet, zero-based station indexes, one flag per set.
long seed
–seed; -1 requests a random stream
bool slotted
–slotted, run on the slot lattice
int mserbatch
–mserbatch, MSER batch size
std::string cimethod
–cimethod: obm | bm | spectral | none
double slot_length
–slotlength
Matrix< double > init_jobs
–initjobs, the JOB-LEVEL initial state, one row per job: [station, class, remaining,...
int tranobs
–tranobs, equally spaced transient observation points over [t0,t1]; fixes the time grid only.
int cnvgchk
–cnvgchk, events between checks; 0 = samples/50
int replications
–replications; 0 = not given (one path)
bool track_jobs
–trackjobs: fill finalJobs and jobDepartures; implied by init_jobs.
int ciminobs
–ciminobs, below which no CI is reported
std::size_t events
0 = not given; overrides samples when set
int cnvgbatch
–cnvgbatch, batches before the first check
std::string tranfilter
–tranfilter: mser5 | fixed | none
std::vector< double > init_sol
–initsol, the warm-start placement as a STATION-MAJOR vector [st0_cl0, st0_cl1, .....
int numthreads
–numthreads; 0 = not given
std::size_t samples
-s, service-completion budget
double timeout
–maxtime, a COOPERATIVE wall-clock budget the event loop polls.
bool has_timespan
true when [t0,t1] was set: a TRANSIENT run
int busy_period_orders
–busyperiod: highest busy period order measured, 0 disabling the measurement.
One measured busy period target (–busyperiod): a station, a station-class pair, or a declared station...
std::vector< std::size_t > stations
station indexes, zero-based
std::vector< double > mean
std::vector< double > count
One ldes-result document, parsed.
std::vector< std::string > fj_forks
Fork and Join node NAMES of each pair, ordered by fork node then join node; empty without a pair.
Matrix< double > FJTput
getAvgFJ, FJQLen = FJTput * FJRespT
std::vector< std::vector< std::vector< double > > > respTimeSamples
[station][class] -> the per-job response times the engine recorded.
Matrix< double > histogram_space
The exact joint-state residence-time histogram (–export-histogram).
std::string stopping_reason
convergence | max_events | max_sim_events | max_time.
std::vector< std::string > class_names
std::string engine
"native" or "jar": which runner produced these numbers.
std::vector< std::string > fj_joins
bool timed_out
True when the HARD subprocess bound fired, not the cooperative one.
Matrix< double > histogram_time
One runner: the argv prefix that runs the engine, and the name of its image.
std::vector< std::string > argv
up to and including "solve"
std::string engine
"native" or "jar"
One constraint of a joint-state query: a station and the per-class job counts it is required to hold.
std::vector< double > counts
self.result.FJ: one row per Fork/Join pair, one column per class.
std::vector< std::size_t > forks
1-based fork node of each pair
Matrix< T > Tput
FJTput, P x R.
Matrix< T > QLen
FJQLen, P x R.
std::size_t npairs() const
std::vector< std::size_t > joins
1-based join node of each pair
Matrix< T > RespT
FJRespT, P x R.
Outcome of a captured command.
int exitCode
Exit status, or -1 when the command could not run.
bool timedOut
True when the deadline expired and the child was killed.
std::string out
Everything the command wrote to stdout.
Running an external command and capturing its output, with a deadline.
A scratch directory for the subprocess wrappers, the port's lineTempName.