57 <<
"LDES: discrete-event simulation engine for LINE models.\n\n"
58 <<
"Usage: ldes solve <model.json> [-o <result.json>] [options]\n\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 <<
" --cimethod NAME obm | bm | spectral | none\n"
73 <<
" --obmoverlap X overlap of the batch means\n"
74 <<
" --ciminbatch N batches below which no CI is reported\n"
75 <<
" --ciminobs N observations below which no CI is reported\n"
76 <<
" --spectrallowfreqfrac X low-frequency fraction of the spectral CI\n"
77 <<
" --slotted run on the slot lattice\n"
78 <<
" --slotlength X slot length (implies --slotted)\n"
79 <<
" --replications N independent replications\n"
80 <<
" --numthreads N threads across replications\n"
81 <<
" --timespan T0,T1 transient run over [T0,T1]\n"
82 <<
" --busyperiod K busy-period orders 1..K\n"
83 <<
" --busyperiod-subnet i,j,... an extra busy-period target\n"
84 <<
" --initsol V initial state, station-major, comma separated\n"
85 <<
" --export-histogram fill the state histogram block\n"
86 <<
" --trajectory fill the state trajectory block\n"
87 <<
" --respt-samples record every per-visit response time\n"
88 <<
" -h, --help this text\n";
91[[noreturn]]
void fail(
const std::string& msg) {
92 std::cerr <<
"Error: " << msg <<
"\n";
97[[noreturn]]
void refuse(
const std::string& flag,
const std::string& why) {
98 std::cerr <<
"Error: " << flag <<
" is accepted by the Java engine but not by this one: "
99 << why <<
".\nRun the model through common/ldes.jar for it, rather than "
100 "taking a silently different simulation.\n";
104std::string need_value(
int argc,
char** argv,
int& i,
const std::string& flag) {
105 if (i + 1 >= argc) fail(flag +
" requires a value.");
106 return std::string(argv[++i]);
109std::vector<double> parse_doubles(
const std::string& csv) {
110 std::vector<double> out;
111 std::stringstream ss(csv);
113 while (std::getline(ss, tok,
',')) {
114 if (!tok.empty()) out.push_back(std::atof(tok.c_str()));
119std::vector<std::size_t> parse_indices(
const std::string& csv) {
120 std::vector<std::size_t> out;
121 std::stringstream ss(csv);
123 while (std::getline(ss, tok,
',')) {
124 if (!tok.empty()) out.push_back(
static_cast<std::size_t
>(std::atol(tok.c_str())));
130std::string num(
double v) {
131 if (std::isnan(v))
return "null";
132 if (std::isinf(v))
return v > 0 ?
"1e999" :
"-1e999";
134 std::snprintf(buf,
sizeof(buf),
"%.17g", v);
135 return std::string(buf);
141 for (std::size_t i = 0; i < rows; ++i) {
144 for (std::size_t j = 0; j < cols; ++j) {
146 const bool inside = (i < m.
rows() && j < m.
cols());
147 s += num(inside ? m(i, j) : 0.0);
156std::string respt_samples_json(
const LdesResult& r) {
157 if (r.respTimeSamples.empty())
return std::string();
159 for (std::size_t i = 0; i < r.respTimeSamples.size(); ++i) {
162 for (std::size_t k = 0; k < r.respTimeSamples[i].size(); ++k) {
165 for (std::size_t j = 0; j < r.respTimeSamples[i][k].size(); ++j) {
167 s += num(r.respTimeSamples[i][k][j]);
177std::string str_json(
const std::string& s) {
178 std::string out =
"\"";
180 if (c ==
'"' || c ==
'\\') {
183 }
else if (c ==
'\n') {
201std::string result_json(
const LdesResult& r,
const LdesOptions& o,
double runtime,
202 std::size_t events) {
203 const std::size_t S = r.nstations, C = r.nclasses;
206 s +=
" \"format\": \"ldes-result\",\n";
207 s +=
" \"version\": \"1.0\",\n";
208 s +=
" \"solver\": \"SolverLDES\",\n";
209 s +=
" \"method\": " + str_json(o.method) +
",\n";
210 s +=
" \"runtime\": " + num(runtime) +
",\n";
211 s +=
" \"converged\": false,\n";
212 s +=
" \"stoppingReason\": \"max_events\",\n";
213 s +=
" \"convergenceBatches\": 0,\n";
214 s +=
" \"totalSimulatedEvents\": " + std::to_string(events) +
",\n";
216 s +=
" \"dimensions\": {\"nstations\": " + std::to_string(S) +
217 ", \"nclasses\": " + std::to_string(C) +
", \"nchains\": " + std::to_string(r.nchains) +
218 ", \"stationNames\": [";
219 for (std::size_t i = 0; i < r.station_names.size(); ++i) {
221 s += str_json(r.station_names[i]);
223 s +=
"], \"classNames\": [";
224 for (std::size_t i = 0; i < r.class_names.size(); ++i) {
226 s += str_json(r.class_names[i]);
230 s +=
" \"metrics\": {";
231 s +=
"\"QN\": " + mat_json(r.QN, S, C);
232 s +=
", \"UN\": " + mat_json(r.UN, S, C);
233 s +=
", \"RN\": " + mat_json(r.RN, S, C);
234 s +=
", \"TN\": " + mat_json(r.TN, S, C);
235 s +=
", \"AN\": " + mat_json(r.AN, S, C);
236 s +=
", \"WN\": " + mat_json(r.WN, S, C);
237 s +=
", \"CN\": " + mat_json(r.CN, 1, C);
238 s +=
", \"XN\": " + mat_json(r.XN, 1, C);
243 if (!r.DropRateJoin.empty()) s +=
", \"DropRateJoin\": " + mat_json(r.DropRateJoin, S, C);
254 if (o.export_histogram && r.histogram_space.rows() > 0 && r.histogram_time.rows() > 0) {
255 s +=
" \"stateHistogram\": {";
256 s +=
"\"space\": " + mat_json(r.histogram_space, r.histogram_space.rows(),
257 r.histogram_space.cols());
258 s +=
", \"time\": " + mat_json(r.histogram_time, r.histogram_time.rows(), 1);
259 if (r.traj_space.rows() > 0 && r.traj_time.rows() > 0) {
260 s +=
", \"trajSpace\": " +
261 mat_json(r.traj_space, r.traj_space.rows(), r.traj_space.cols());
262 s +=
", \"trajTime\": " + mat_json(r.traj_time, r.traj_time.rows(), 1);
267 if (!r.busy_periods.empty()) {
268 s +=
" \"busyPeriods\": {\"orders\": " +
269 std::to_string(r.busy_periods[0].mean.size()) +
", \"targets\": [";
270 for (std::size_t ti = 0; ti < r.busy_periods.size(); ++ti) {
271 const LdesResult::BusyPeriodTarget& t = r.busy_periods[ti];
273 s +=
"{\"name\": " + str_json(t.name) +
", \"stations\": [";
274 for (std::size_t k = 0; k < t.stations.size(); ++k) {
276 s += std::to_string(t.stations[k]);
278 s +=
"], \"class\": " + std::to_string(t.job_class) +
", \"mean\": [";
279 for (std::size_t k = 0; k < t.mean.size(); ++k) {
283 s +=
"], \"count\": [";
284 for (std::size_t k = 0; k < t.count.size(); ++k) {
286 s += num(t.count[k]);
293 if (!r.cache_metrics.empty()) {
294 s +=
" \"cacheMetrics\": {";
296 for (std::map<std::string, line::ldes::LdesCacheMetrics>::const_iterator it =
297 r.cache_metrics.begin();
298 it != r.cache_metrics.end(); ++it) {
299 if (!first) s +=
", ";
302 s += str_json(it->first) +
": {";
315 if (r.QNfcr.rows() > 0) {
316 const std::size_t G = r.QNfcr.rows();
317 s +=
" \"fcr\": {\"nregions\": " + std::to_string(G);
318 s +=
", \"QNfcr\": " + mat_json(r.QNfcr, G, C);
319 s +=
", \"UNfcr\": " + mat_json(r.UNfcr, G, C);
320 s +=
", \"RNfcr\": " + mat_json(r.RNfcr, G, C);
321 s +=
", \"TNfcr\": " + mat_json(r.TNfcr, G, C);
322 s +=
", \"ANfcr\": " + mat_json(r.ANfcr, G, C);
323 s +=
", \"WNfcr\": " + mat_json(r.WNfcr, G, C);
324 s +=
", \"WeightNfcr\": " + mat_json(r.WeightNfcr, G, C);
325 s +=
", \"MemOccNfcr\": " + mat_json(r.MemOccNfcr, G, C);
326 s +=
", \"DropRateNfcr\": " + mat_json(r.DropRateNfcr, G, C);
337 const std::string respt_json = respt_samples_json(r);
338 if (o.export_trajectory && !r.t.empty()) {
339 s +=
" \"transient\": {\"t\": [";
340 for (std::size_t k = 0; k < r.t.size(); ++k) {
345 const char* keys[3] = {
"QNt",
"UNt",
"TNt"};
346 const std::vector<std::vector<line::Matrix<double>>>* src[3] = {&r.QNt, &r.UNt, &r.TNt};
347 for (
int q = 0; q < 3; ++q) {
348 s += std::string(
", \"") + keys[q] +
"\": [";
349 for (std::size_t i = 0; i < src[q]->size(); ++i) {
352 for (std::size_t k = 0; k < (*src[q])[i].size(); ++k) {
355 s += mat_json(mm, mm.
rows(), mm.
cols());
361 if (!respt_json.empty()) s +=
", \"respTimeSamples\": " + respt_json;
365 if (o.export_respt && !respt_json.empty())
366 s +=
" \"respTimeSamples\": " + respt_json +
",\n";
367 s +=
" \"sampleCounts\": {";
368 s +=
"\"QNSamples\": " + mat_json(r.QNSamples, S, C);
369 s +=
", \"UNSamples\": " + mat_json(r.UNSamples, S, C);
370 s +=
", \"RNSamples\": " + mat_json(r.RNSamples, S, C);
371 s +=
", \"TNSamples\": " + mat_json(r.TNSamples, S, C);
374 s +=
" \"confidenceIntervals\": {";
375 s +=
"\"QNCI\": " + mat_json(r.QNCI, S, C);
376 s +=
", \"UNCI\": " + mat_json(r.UNCI, S, C);
377 s +=
", \"RNCI\": " + mat_json(r.RNCI, S, C);
378 s +=
", \"TNCI\": " + mat_json(r.TNCI, S, C);
379 s +=
", \"ANCI\": " + mat_json(r.ANCI, S, C);
380 s +=
", \"WNCI\": " + mat_json(r.WNCI, S, C);
383 s +=
" \"relativePrecision\": {";
384 s +=
"\"QNRelPrec\": " + mat_json(r.QNRelPrec, S, C);
385 s +=
", \"UNRelPrec\": " + mat_json(r.UNRelPrec, S, C);
386 s +=
", \"RNRelPrec\": " + mat_json(r.RNRelPrec, S, C);
387 s +=
", \"TNRelPrec\": " + mat_json(r.TNRelPrec, S, C);
390 s +=
" \"impatience\": {";
391 s +=
"\"renegedCustomers\": " + mat_json(r.renegedCustomers, S, C);
392 s +=
", \"avgRenegingWaitTime\": " + mat_json(r.avgRenegingWaitTime, S, C);
393 s +=
", \"renegingRate\": " + mat_json(r.renegingRate, S, C);
394 s +=
", \"balkedCustomers\": " + mat_json(r.balkedCustomers, S, C);
395 s +=
", \"balkingProbability\": " + mat_json(r.balkingProbability, S, C);
396 s +=
", \"retriedCustomers\": " + mat_json(r.retriedCustomers, S, C);
397 s +=
", \"retrialDropped\": " + mat_json(r.retrialDropped, S, C);
398 s +=
", \"avgOrbitSize\": " + mat_json(r.avgOrbitSize, S, C);
406int main(
int argc,
char** argv) {
411 const std::string cmd = argv[1];
412 if (cmd ==
"-h" || cmd ==
"--help") {
416 if (cmd !=
"solve") {
417 std::cerr <<
"Error: unknown command '" << cmd <<
"'. The only command is 'solve'.\n";
420 if (argc < 3) fail(
"solve requires a model file.");
422 const std::string model_path = argv[2];
423 std::string output_path;
425 bool have_samples =
false, have_events =
false;
427 for (
int i = 3; i < argc; ++i) {
428 const std::string a = argv[i];
430 output_path = need_value(argc, argv, i, a);
431 }
else if (a ==
"-s" || a ==
"--samples") {
432 o.samples =
static_cast<std::size_t
>(std::atol(need_value(argc, argv, i, a).c_str()));
434 }
else if (a ==
"-e" || a ==
"--maxevents") {
435 o.events =
static_cast<std::size_t
>(std::atol(need_value(argc, argv, i, a).c_str()));
437 }
else if (a ==
"--maxtime") {
438 o.timeout = std::atof(need_value(argc, argv, i, a).c_str());
439 }
else if (a ==
"--seed") {
440 o.seed = std::atol(need_value(argc, argv, i, a).c_str());
441 }
else if (a ==
"--method") {
442 o.method = need_value(argc, argv, i, a);
443 }
else if (a ==
"--cnvgon") {
445 }
else if (a ==
"--cnvgtol") {
446 o.cnvgtol = std::atof(need_value(argc, argv, i, a).c_str());
447 }
else if (a ==
"--cnvgbatch") {
448 o.cnvgbatch = std::atoi(need_value(argc, argv, i, a).c_str());
449 }
else if (a ==
"--cnvgchk") {
450 o.cnvgchk = std::atoi(need_value(argc, argv, i, a).c_str());
451 }
else if (a ==
"--tranfilter") {
452 o.tranfilter = need_value(argc, argv, i, a);
453 }
else if (a ==
"--mserbatch") {
454 o.mserbatch = std::atoi(need_value(argc, argv, i, a).c_str());
455 }
else if (a ==
"--warmupfrac") {
456 o.warmupfrac = std::atof(need_value(argc, argv, i, a).c_str());
457 }
else if (a ==
"--cimethod") {
458 o.cimethod = need_value(argc, argv, i, a);
459 }
else if (a ==
"--obmoverlap") {
460 o.obmoverlap = std::atof(need_value(argc, argv, i, a).c_str());
461 }
else if (a ==
"--ciminbatch") {
462 o.ciminbatch = std::atoi(need_value(argc, argv, i, a).c_str());
463 }
else if (a ==
"--ciminobs") {
464 o.ciminobs = std::atoi(need_value(argc, argv, i, a).c_str());
465 }
else if (a ==
"--spectrallowfreqfrac") {
466 o.spectral_low_freq_frac = std::atof(need_value(argc, argv, i, a).c_str());
467 }
else if (a ==
"--slotted") {
469 }
else if (a ==
"--slotlength") {
470 o.slot_length = std::atof(need_value(argc, argv, i, a).c_str());
472 }
else if (a ==
"--replications") {
473 o.replications = std::atoi(need_value(argc, argv, i, a).c_str());
474 }
else if (a ==
"--numthreads") {
475 o.numthreads = std::atoi(need_value(argc, argv, i, a).c_str());
476 }
else if (a ==
"--timespan") {
477 const std::vector<double> t = parse_doubles(need_value(argc, argv, i, a));
478 if (t.size() != 2) fail(
"--timespan takes T0,T1.");
479 o.has_timespan =
true;
482 }
else if (a ==
"--busyperiod") {
483 o.busy_period_orders = std::atoi(need_value(argc, argv, i, a).c_str());
484 }
else if (a ==
"--busyperiod-subnet") {
485 o.busy_period_subnets.push_back(parse_indices(need_value(argc, argv, i, a)));
486 }
else if (a ==
"--initsol") {
487 o.init_sol = parse_doubles(need_value(argc, argv, i, a));
488 }
else if (a ==
"--export-histogram") {
489 o.export_histogram =
true;
490 }
else if (a ==
"--trajectory") {
491 o.export_trajectory =
true;
492 }
else if (a ==
"--respt-samples") {
493 o.export_respt =
true;
494 }
else if (a ==
"--rest") {
495 refuse(a,
"server mode belongs to line-cli, not to this binary");
496 }
else if (a ==
"-h" || a ==
"--help") {
500 fail(
"unknown option '" + a +
"'.");
503 if (have_events && !have_samples) o.samples = o.events;
509 const std::clock_t t_start = std::clock();
511 const double runtime =
static_cast<double>(std::clock() - t_start) / CLOCKS_PER_SEC;
513 const std::string doc = result_json(r, o, runtime, o.samples);
514 if (output_path.empty()) {
517 std::ofstream out(output_path.c_str());
518 if (!out) fail(
"cannot write '" + output_path +
"'.");
521 std::cerr <<
"LDES analysis [method: " << o.method
522 <<
"; type: approximate, randomized; lang: cpp] completed in " << runtime
523 <<
"s. Iterations: " << o.samples <<
".\n";
526 std::cerr <<
"Error: " << e.what() <<
"\n";
528 }
catch (
const std::exception& e) {
529 std::cerr <<
"Error: " << e.what() <<
"\n";
Requested feature or arithmetic mode is not ported yet.
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)
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.