LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
ldes_cli.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 * `ldes`: the native LDES engine behind the interface of `jline.cli.LdesCLI`.
9 *
10 * WHAT THIS BINARY IS FOR. `common/ldes` has been a GraalVM image of the JAVA
11 * engine; this program is its replacement, built from
12 * `cpp/include/line/solvers/ldes/`. Every client of that binary -- MATLAB's
13 * `@SolverLDES/solveCli.m`, native Python's `wrappers/solver_ldes`, and the C++
14 * client `wrappers/ldes/solver_ldes.h` -- speaks to it through exactly two
15 * artefacts: the ARGUMENT VECTOR and the `ldes-result` DOCUMENT. Neither may
16 * drift. A client is not recompiled when the engine changes hands, so an
17 * argument this program rejects and the Java one accepted, or a key it spells
18 * differently, is a silent breakage in three codebases at once.
19 *
20 * THE INTERFACE IS THEREFORE COPIED, not designed:
21 *
22 * ldes solve <model.json> -o <result.json> [flags]
23 *
24 * with the flag set of `LdesCLI.handleSolveCommand` and the document shape of
25 * `LDESResultIO.write`. Flags this engine cannot honour are REFUSED BY NAME
26 * rather than ignored, which is the one deliberate difference from the AOT
27 * image it replaces: that image ignored flags it predated, and an ignored
28 * `--slotlength` silently ran a continuous-time simulation under a slotted
29 * model's name. A refusal is visible; a silent default is not.
30 *
31 * WHAT IS NOT HERE. `--rest`/server mode belongs to `line_cli` and is refused
32 * with the reason rather than accepted and dropped.
33 */
34
35#include <cmath>
36#include <cstdio>
37#include <ctime>
38#include <cstdlib>
39#include <fstream>
40#include <iostream>
41#include <sstream>
42#include <string>
43#include <vector>
44
48#include "line/util/error.h"
49
50namespace {
51
54
55void print_help() {
56 std::cout
57 << "LDES: discrete-event simulation engine for LINE models.\n\n"
58 << "Usage: ldes solve <model.json> [-o <result.json>] [options]\n\n"
59 << "Options:\n"
60 << " -s, --samples N service completions to simulate (default 200000)\n"
61 << " -e, --maxevents N event budget; overrides --samples\n"
62 << " --maxtime SECONDS wall-clock budget\n"
63 << " --seed N run seed (default 23000; -1 draws one)\n"
64 << " --method NAME engine method (default 'default')\n"
65 << " --cnvgon stop on the convergence check\n"
66 << " --cnvgtol X relative tolerance of that check\n"
67 << " --cnvgbatch N batches before the first check\n"
68 << " --cnvgchk N events between checks\n"
69 << " --tranfilter NAME mser5 | fixed | none\n"
70 << " --mserbatch N MSER batch size\n"
71 << " --warmupfrac X warmup fraction, for --tranfilter fixed\n"
72 << " --tranobs N transient observation points over --timespan\n"
73 << " --cimethod NAME obm | bm | spectral | none\n"
74 << " --obmoverlap X overlap of the batch means\n"
75 << " --ciminbatch N batches below which no CI is reported\n"
76 << " --ciminobs N observations below which no CI is reported\n"
77 << " --spectrallowfreqfrac X low-frequency fraction of the spectral CI\n"
78 << " --slotted run on the slot lattice\n"
79 << " --slotlength X slot length (implies --slotted)\n"
80 << " --replications N independent replications\n"
81 << " --numthreads N threads across replications\n"
82 << " --timespan T0,T1 transient run over [T0,T1]\n"
83 << " --busyperiod K busy-period orders 1..K\n"
84 << " --busyperiod-subnet i,j,... an extra busy-period target\n"
85 << " --initsol V initial state, station-major, comma separated\n"
86 << " --export-histogram fill the state histogram block\n"
87 << " --trajectory fill the state trajectory block\n"
88 << " --respt-samples record every per-visit response time\n"
89 << " -h, --help this text\n";
90}
91
92[[noreturn]] void fail(const std::string& msg) {
93 std::cerr << "Error: " << msg << "\n";
94 std::exit(1);
95}
96
97/** A flag the reference accepts and this engine cannot honour: say so. */
98[[noreturn]] void refuse(const std::string& flag, const std::string& why) {
99 std::cerr << "Error: " << flag << " is accepted by the Java engine but not by this one: "
100 << why << ".\nRun the model through common/ldes.jar for it, rather than "
101 "taking a silently different simulation.\n";
102 std::exit(1);
103}
104
105std::string need_value(int argc, char** argv, int& i, const std::string& flag) {
106 if (i + 1 >= argc) fail(flag + " requires a value.");
107 return std::string(argv[++i]);
108}
109
110std::vector<double> parse_doubles(const std::string& csv) {
111 std::vector<double> out;
112 std::stringstream ss(csv);
113 std::string tok;
114 while (std::getline(ss, tok, ',')) {
115 if (!tok.empty()) out.push_back(std::atof(tok.c_str()));
116 }
117 return out;
118}
119
120std::vector<std::size_t> parse_indices(const std::string& csv) {
121 std::vector<std::size_t> out;
122 std::stringstream ss(csv);
123 std::string tok;
124 while (std::getline(ss, tok, ',')) {
125 if (!tok.empty()) out.push_back(static_cast<std::size_t>(std::atol(tok.c_str())));
126 }
127 return out;
128}
129
130/** A JSON number the way the reference writes it: full round-trip precision. */
131std::string num(double v) {
132 if (std::isnan(v)) return "null";
133 if (std::isinf(v)) return v > 0 ? "1e999" : "-1e999";
134 char buf[40];
135 std::snprintf(buf, sizeof(buf), "%.17g", v);
136 return std::string(buf);
137}
138
139/** A (rows x cols) matrix as the nested array `LDESResultIO` emits. */
140std::string mat_json(const line::Matrix<double>& m, std::size_t rows, std::size_t cols) {
141 std::string s = "[";
142 for (std::size_t i = 0; i < rows; ++i) {
143 if (i) s += ", ";
144 s += "[";
145 for (std::size_t j = 0; j < cols; ++j) {
146 if (j) s += ", ";
147 const bool inside = (i < m.rows() && j < m.cols());
148 s += num(inside ? m(i, j) : 0.0);
149 }
150 s += "]";
151 }
152 s += "]";
153 return s;
154}
155
156/** The per-visit response times as the nested `[station][class][sample]` array. */
157std::string respt_samples_json(const LdesResult& r) {
158 if (r.respTimeSamples.empty()) return std::string();
159 std::string s = "[";
160 for (std::size_t i = 0; i < r.respTimeSamples.size(); ++i) {
161 if (i) s += ", ";
162 s += "[";
163 for (std::size_t k = 0; k < r.respTimeSamples[i].size(); ++k) {
164 if (k) s += ", ";
165 s += "[";
166 for (std::size_t j = 0; j < r.respTimeSamples[i][k].size(); ++j) {
167 if (j) s += ", ";
168 s += num(r.respTimeSamples[i][k][j]);
169 }
170 s += "]";
171 }
172 s += "]";
173 }
174 s += "]";
175 return s;
176}
177
178std::string str_json(const std::string& s) {
179 std::string out = "\"";
180 for (char c : s) {
181 if (c == '"' || c == '\\') {
182 out += '\\';
183 out += c;
184 } else if (c == '\n') {
185 out += "\\n";
186 } else {
187 out += c;
188 }
189 }
190 out += "\"";
191 return out;
192}
193
194/**
195 * The `ldes-result` document, key for key with `LDESResultIO.write`.
196 *
197 * The order of the keys is the reference's, and the blocks a run did not fill
198 * are still emitted with their zero matrices: every client indexes this
199 * document by key and by station row, and a missing block reads as a parse
200 * failure rather than as an absent measurement.
201 */
202std::string result_json(const LdesResult& r, const LdesOptions& o, double runtime,
203 std::size_t events) {
204 const std::size_t S = r.nstations, C = r.nclasses;
205 std::string s;
206 s += "{\n";
207 s += " \"format\": \"ldes-result\",\n";
208 s += " \"version\": \"1.0\",\n";
209 s += " \"solver\": \"SolverLDES\",\n";
210 s += " \"method\": " + str_json(o.method) + ",\n";
211 s += " \"runtime\": " + num(runtime) + ",\n";
212 s += " \"converged\": false,\n";
213 s += " \"stoppingReason\": \"max_events\",\n";
214 s += " \"convergenceBatches\": 0,\n";
215 s += " \"totalSimulatedEvents\": " + std::to_string(events) + ",\n";
216
217 s += " \"dimensions\": {\"nstations\": " + std::to_string(S) +
218 ", \"nclasses\": " + std::to_string(C) + ", \"nchains\": " + std::to_string(r.nchains) +
219 ", \"stationNames\": [";
220 for (std::size_t i = 0; i < r.station_names.size(); ++i) {
221 if (i) s += ", ";
222 s += str_json(r.station_names[i]);
223 }
224 s += "], \"classNames\": [";
225 for (std::size_t i = 0; i < r.class_names.size(); ++i) {
226 if (i) s += ", ";
227 s += str_json(r.class_names[i]);
228 }
229 s += "]},\n";
230
231 s += " \"metrics\": {";
232 s += "\"QN\": " + mat_json(r.QN, S, C);
233 s += ", \"UN\": " + mat_json(r.UN, S, C);
234 s += ", \"RN\": " + mat_json(r.RN, S, C);
235 s += ", \"TN\": " + mat_json(r.TN, S, C);
236 s += ", \"AN\": " + mat_json(r.AN, S, C);
237 s += ", \"WN\": " + mat_json(r.WN, S, C);
238 s += ", \"CN\": " + mat_json(r.CN, 1, C);
239 s += ", \"XN\": " + mat_json(r.XN, 1, C);
240 // The sibling-drop rate at a Join, where LossRate = ArvR - Tput does NOT
241 // hold: ArvR counts the SIBLINGS offered and Tput the PARENT jobs released.
242 // `LDESResultIO.write` puts it inside `metrics`, so it goes here and not in
243 // an optional block of its own.
244 if (!r.DropRateJoin.empty()) s += ", \"DropRateJoin\": " + mat_json(r.DropRateJoin, S, C);
245 s += "},\n";
246
247 // THE OPTIONAL BLOCKS, each written ONLY when the run measured it, exactly
248 // as `LDESResultIO.write` gates them. A block emitted empty is not the same
249 // as an absent one: `ldes_prob_from_histogram` and `getAvgBusyPeriod` both
250 // treat presence as the claim that the measurement was taken.
251 // GATED ON THE FLAG, not on the measurement. A transient run computes the
252 // histogram whether or not anyone asked for it, and `LDESResultIO` writes
253 // the block only when the flag was given; emitting it anyway would make
254 // this engine's document differ from the reference's on every transient run.
255 if (o.export_histogram && r.histogram_space.rows() > 0 && r.histogram_time.rows() > 0) {
256 s += " \"stateHistogram\": {";
257 s += "\"space\": " + mat_json(r.histogram_space, r.histogram_space.rows(),
258 r.histogram_space.cols());
259 s += ", \"time\": " + mat_json(r.histogram_time, r.histogram_time.rows(), 1);
260 if (r.traj_space.rows() > 0 && r.traj_time.rows() > 0) {
261 s += ", \"trajSpace\": " +
262 mat_json(r.traj_space, r.traj_space.rows(), r.traj_space.cols());
263 s += ", \"trajTime\": " + mat_json(r.traj_time, r.traj_time.rows(), 1);
264 }
265 s += "},\n";
266 }
267
268 if (!r.busy_periods.empty()) {
269 s += " \"busyPeriods\": {\"orders\": " +
270 std::to_string(r.busy_periods[0].mean.size()) + ", \"targets\": [";
271 for (std::size_t ti = 0; ti < r.busy_periods.size(); ++ti) {
272 const LdesResult::BusyPeriodTarget& t = r.busy_periods[ti];
273 if (ti) s += ", ";
274 s += "{\"name\": " + str_json(t.name) + ", \"stations\": [";
275 for (std::size_t k = 0; k < t.stations.size(); ++k) {
276 if (k) s += ", ";
277 s += std::to_string(t.stations[k]);
278 }
279 s += "], \"class\": " + std::to_string(t.job_class) + ", \"mean\": [";
280 for (std::size_t k = 0; k < t.mean.size(); ++k) {
281 if (k) s += ", ";
282 s += num(t.mean[k]);
283 }
284 s += "], \"count\": [";
285 for (std::size_t k = 0; k < t.count.size(); ++k) {
286 if (k) s += ", ";
287 s += num(t.count[k]);
288 }
289 s += "]}";
290 }
291 s += "]},\n";
292 }
293
294 if (!r.cache_metrics.empty()) {
295 s += " \"cacheMetrics\": {";
296 bool first = true;
297 for (std::map<std::string, line::ldes::LdesCacheMetrics>::const_iterator it =
298 r.cache_metrics.begin();
299 it != r.cache_metrics.end(); ++it) {
300 if (!first) s += ", ";
301 first = false;
302 const line::ldes::LdesCacheMetrics& cm = it->second;
303 s += str_json(it->first) + ": {";
304 s += "\"hit\": " + mat_json(cm.hit, cm.hit.rows(), cm.hit.cols());
305 s += ", \"delayed\": " + mat_json(cm.delayed, cm.delayed.rows(), cm.delayed.cols());
306 s += ", \"miss\": " + mat_json(cm.miss, cm.miss.rows(), cm.miss.cols());
307 s += ", \"latency\": " + mat_json(cm.latency, cm.latency.rows(), cm.latency.cols());
308 s += ", \"hitList\": " + mat_json(cm.hitList, cm.hitList.rows(), cm.hitList.cols());
309 s += ", \"itemProb\": " + mat_json(cm.itemProb, cm.itemProb.rows(), cm.itemProb.cols());
310 s += ", \"listCost\": " + mat_json(cm.listCost, cm.listCost.rows(), cm.listCost.cols());
311 s += "}";
312 }
313 s += "},\n";
314 }
315
316 if (r.QNfcr.rows() > 0) {
317 const std::size_t G = r.QNfcr.rows();
318 s += " \"fcr\": {\"nregions\": " + std::to_string(G);
319 s += ", \"QNfcr\": " + mat_json(r.QNfcr, G, C);
320 s += ", \"UNfcr\": " + mat_json(r.UNfcr, G, C);
321 s += ", \"RNfcr\": " + mat_json(r.RNfcr, G, C);
322 s += ", \"TNfcr\": " + mat_json(r.TNfcr, G, C);
323 s += ", \"ANfcr\": " + mat_json(r.ANfcr, G, C);
324 s += ", \"WNfcr\": " + mat_json(r.WNfcr, G, C);
325 s += ", \"WeightNfcr\": " + mat_json(r.WeightNfcr, G, C);
326 s += ", \"MemOccNfcr\": " + mat_json(r.MemOccNfcr, G, C);
327 s += ", \"DropRateNfcr\": " + mat_json(r.DropRateNfcr, G, C);
328 s += "},\n";
329 }
330
331 // The reference gates the whole transient block on `--trajectory`, not on
332 // the run being transient.
333 // The samples appear at TOP LEVEL under --respt-samples and INSIDE the
334 // transient block under --trajectory. Both spellings are the reference's,
335 // and both are read: the C++ client takes whichever is present, MATLAB's
336 // `sample()` and `sampleSys()` read the nested one and returned nothing
337 // while only the top-level spelling existed.
338 const std::string respt_json = respt_samples_json(r);
339 if (o.export_trajectory && !r.t.empty()) {
340 s += " \"transient\": {\"t\": [";
341 for (std::size_t k = 0; k < r.t.size(); ++k) {
342 if (k) s += ", ";
343 s += num(r.t[k]);
344 }
345 s += "]";
346 const char* keys[3] = {"QNt", "UNt", "TNt"};
347 const std::vector<std::vector<line::Matrix<double>>>* src[3] = {&r.QNt, &r.UNt, &r.TNt};
348 for (int q = 0; q < 3; ++q) {
349 s += std::string(", \"") + keys[q] + "\": [";
350 for (std::size_t i = 0; i < src[q]->size(); ++i) {
351 if (i) s += ", ";
352 s += "[";
353 for (std::size_t k = 0; k < (*src[q])[i].size(); ++k) {
354 if (k) s += ", ";
355 const line::Matrix<double>& mm = (*src[q])[i][k];
356 s += mat_json(mm, mm.rows(), mm.cols());
357 }
358 s += "]";
359 }
360 s += "]";
361 }
362 if (!respt_json.empty()) s += ", \"respTimeSamples\": " + respt_json;
363 s += "},\n";
364 }
365
366 if (o.export_respt && !respt_json.empty())
367 s += " \"respTimeSamples\": " + respt_json + ",\n";
368 s += " \"sampleCounts\": {";
369 s += "\"QNSamples\": " + mat_json(r.QNSamples, S, C);
370 s += ", \"UNSamples\": " + mat_json(r.UNSamples, S, C);
371 s += ", \"RNSamples\": " + mat_json(r.RNSamples, S, C);
372 s += ", \"TNSamples\": " + mat_json(r.TNSamples, S, C);
373 s += "},\n";
374
375 s += " \"confidenceIntervals\": {";
376 s += "\"QNCI\": " + mat_json(r.QNCI, S, C);
377 s += ", \"UNCI\": " + mat_json(r.UNCI, S, C);
378 s += ", \"RNCI\": " + mat_json(r.RNCI, S, C);
379 s += ", \"TNCI\": " + mat_json(r.TNCI, S, C);
380 s += ", \"ANCI\": " + mat_json(r.ANCI, S, C);
381 s += ", \"WNCI\": " + mat_json(r.WNCI, S, C);
382 s += "},\n";
383
384 s += " \"relativePrecision\": {";
385 s += "\"QNRelPrec\": " + mat_json(r.QNRelPrec, S, C);
386 s += ", \"UNRelPrec\": " + mat_json(r.UNRelPrec, S, C);
387 s += ", \"RNRelPrec\": " + mat_json(r.RNRelPrec, S, C);
388 s += ", \"TNRelPrec\": " + mat_json(r.TNRelPrec, S, C);
389 s += "},\n";
390
391 s += " \"impatience\": {";
392 s += "\"renegedCustomers\": " + mat_json(r.renegedCustomers, S, C);
393 s += ", \"avgRenegingWaitTime\": " + mat_json(r.avgRenegingWaitTime, S, C);
394 s += ", \"renegingRate\": " + mat_json(r.renegingRate, S, C);
395 s += ", \"balkedCustomers\": " + mat_json(r.balkedCustomers, S, C);
396 s += ", \"balkingProbability\": " + mat_json(r.balkingProbability, S, C);
397 s += ", \"retriedCustomers\": " + mat_json(r.retriedCustomers, S, C);
398 s += ", \"retrialDropped\": " + mat_json(r.retrialDropped, S, C);
399 s += ", \"avgOrbitSize\": " + mat_json(r.avgOrbitSize, S, C);
400 s += "}\n";
401 s += "}\n";
402 return s;
403}
404
405} // namespace
406
407int main(int argc, char** argv) {
408 if (argc < 2) {
409 print_help();
410 return 1;
411 }
412 const std::string cmd = argv[1];
413 if (cmd == "-h" || cmd == "--help") {
414 print_help();
415 return 0;
416 }
417 if (cmd != "solve") {
418 std::cerr << "Error: unknown command '" << cmd << "'. The only command is 'solve'.\n";
419 return 1;
420 }
421 if (argc < 3) fail("solve requires a model file.");
422
423 const std::string model_path = argv[2];
424 std::string output_path;
425 LdesOptions o;
426 bool have_samples = false, have_events = false;
427
428 for (int i = 3; i < argc; ++i) {
429 const std::string a = argv[i];
430 if (a == "-o") {
431 output_path = need_value(argc, argv, i, a);
432 } else if (a == "-s" || a == "--samples") {
433 o.samples = static_cast<std::size_t>(std::atol(need_value(argc, argv, i, a).c_str()));
434 have_samples = true;
435 } else if (a == "-e" || a == "--maxevents") {
436 o.events = static_cast<std::size_t>(std::atol(need_value(argc, argv, i, a).c_str()));
437 have_events = true;
438 } else if (a == "--maxtime") {
439 o.timeout = std::atof(need_value(argc, argv, i, a).c_str());
440 } else if (a == "--seed") {
441 o.seed = std::atol(need_value(argc, argv, i, a).c_str());
442 } else if (a == "--method") {
443 o.method = need_value(argc, argv, i, a);
444 } else if (a == "--cnvgon") {
445 o.cnvgon = true;
446 } else if (a == "--cnvgtol") {
447 o.cnvgtol = std::atof(need_value(argc, argv, i, a).c_str());
448 } else if (a == "--cnvgbatch") {
449 o.cnvgbatch = std::atoi(need_value(argc, argv, i, a).c_str());
450 } else if (a == "--cnvgchk") {
451 o.cnvgchk = std::atoi(need_value(argc, argv, i, a).c_str());
452 } else if (a == "--tranfilter") {
453 o.tranfilter = need_value(argc, argv, i, a);
454 } else if (a == "--mserbatch") {
455 o.mserbatch = std::atoi(need_value(argc, argv, i, a).c_str());
456 } else if (a == "--tranobs") {
457 o.tranobs = std::atoi(need_value(argc, argv, i, a).c_str());
458 if (o.tranobs <= 0) fail("--tranobs must be positive.");
459 } else if (a == "--warmupfrac") {
460 o.warmupfrac = std::atof(need_value(argc, argv, i, a).c_str());
461 } else if (a == "--cimethod") {
462 o.cimethod = need_value(argc, argv, i, a);
463 } else if (a == "--obmoverlap") {
464 o.obmoverlap = std::atof(need_value(argc, argv, i, a).c_str());
465 } else if (a == "--ciminbatch") {
466 o.ciminbatch = std::atoi(need_value(argc, argv, i, a).c_str());
467 } else if (a == "--ciminobs") {
468 o.ciminobs = std::atoi(need_value(argc, argv, i, a).c_str());
469 } else if (a == "--spectrallowfreqfrac") {
470 o.spectral_low_freq_frac = std::atof(need_value(argc, argv, i, a).c_str());
471 } else if (a == "--slotted") {
472 o.slotted = true;
473 } else if (a == "--slotlength") {
474 o.slot_length = std::atof(need_value(argc, argv, i, a).c_str());
475 o.slotted = true; // the reference lets --slotlength imply --slotted
476 } else if (a == "--replications") {
477 o.replications = std::atoi(need_value(argc, argv, i, a).c_str());
478 } else if (a == "--numthreads") {
479 o.numthreads = std::atoi(need_value(argc, argv, i, a).c_str());
480 } else if (a == "--timespan") {
481 const std::vector<double> t = parse_doubles(need_value(argc, argv, i, a));
482 if (t.size() != 2) fail("--timespan takes T0,T1.");
483 o.has_timespan = true;
484 o.t0 = t[0];
485 o.t1 = t[1];
486 } else if (a == "--busyperiod") {
487 o.busy_period_orders = std::atoi(need_value(argc, argv, i, a).c_str());
488 } else if (a == "--busyperiod-subnet") {
489 o.busy_period_subnets.push_back(parse_indices(need_value(argc, argv, i, a)));
490 } else if (a == "--initsol") {
491 o.init_sol = parse_doubles(need_value(argc, argv, i, a));
492 } else if (a == "--export-histogram") {
493 o.export_histogram = true;
494 } else if (a == "--trajectory") {
495 o.export_trajectory = true;
496 } else if (a == "--respt-samples") {
497 o.export_respt = true;
498 } else if (a == "--rest") {
499 refuse(a, "server mode belongs to line-cli, not to this binary");
500 } else if (a == "-h" || a == "--help") {
501 print_help();
502 return 0;
503 } else {
504 fail("unknown option '" + a + "'.");
505 }
506 }
507 if (have_events && !have_samples) o.samples = o.events;
508
509 try {
512
513 const std::clock_t t_start = std::clock();
514 const LdesResult r = line::ldes::ldes_engine_solve(sn, o);
515 const double runtime = static_cast<double>(std::clock() - t_start) / CLOCKS_PER_SEC;
516
517 const std::string doc = result_json(r, o, runtime, o.samples);
518 if (output_path.empty()) {
519 std::cout << doc;
520 } else {
521 std::ofstream out(output_path.c_str());
522 if (!out) fail("cannot write '" + output_path + "'.");
523 out << doc;
524 }
525 std::cerr << "LDES analysis [method: " << o.method
526 << "; type: approximate, randomized; lang: cpp] completed in " << runtime
527 << "s. Iterations: " << o.samples << ".\n";
528 return 0;
529 } catch (const line::UnsupportedError& e) {
530 std::cerr << "Error: " << e.what() << "\n";
531 return 2;
532 } catch (const std::exception& e) {
533 std::cerr << "Error: " << e.what() << "\n";
534 return 1;
535 }
536}
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
Requested feature or arithmetic mode is not ported yet.
Definition error.h:49
A network plus its refreshed NetworkStruct.
A queueing network under construction.
const NetworkStruct< T > & get_struct()
The refreshed struct, MATLAB's model.getStruct().
The exception types the port throws.
int main(int argc, char **argv)
Definition ldes_cli.cpp:407
The NATIVE LDES discrete-event engine.
The option and result records of SolverLDES, the discrete-event simulator.
qn::Network< T > read_network_json(const std::string &path)
Parse a model.json file into a qn::Network<T>.
LdesResult ldes_engine_solve(const qn::NetworkStruct< T > &sn, const LdesOptions &o)
Simulate sn, over independent REPLICATIONS when options.replications > 1.
Reader for the LINE model.json interchange (a Network model) into a qn::Network<T> built through the ...
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.
One ldes-result document, parsed.