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 <<
" --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";
92[[noreturn]]
void fail(
const std::string& msg) {
93 std::cerr <<
"Error: " << msg <<
"\n";
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";
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]);
110std::vector<double> parse_doubles(
const std::string& csv) {
111 std::vector<double> out;
112 std::stringstream ss(csv);
114 while (std::getline(ss, tok,
',')) {
115 if (!tok.empty()) out.push_back(std::atof(tok.c_str()));
120std::vector<std::size_t> parse_indices(
const std::string& csv) {
121 std::vector<std::size_t> out;
122 std::stringstream ss(csv);
124 while (std::getline(ss, tok,
',')) {
125 if (!tok.empty()) out.push_back(
static_cast<std::size_t
>(std::atol(tok.c_str())));
131std::string num(
double v) {
132 if (std::isnan(v))
return "null";
133 if (std::isinf(v))
return v > 0 ?
"1e999" :
"-1e999";
135 std::snprintf(buf,
sizeof(buf),
"%.17g", v);
136 return std::string(buf);
142 for (std::size_t i = 0; i < rows; ++i) {
145 for (std::size_t j = 0; j < cols; ++j) {
147 const bool inside = (i < m.
rows() && j < m.
cols());
148 s += num(inside ? m(i, j) : 0.0);
157std::string respt_samples_json(
const LdesResult& r) {
158 if (r.respTimeSamples.empty())
return std::string();
160 for (std::size_t i = 0; i < r.respTimeSamples.size(); ++i) {
163 for (std::size_t k = 0; k < r.respTimeSamples[i].size(); ++k) {
166 for (std::size_t j = 0; j < r.respTimeSamples[i][k].size(); ++j) {
168 s += num(r.respTimeSamples[i][k][j]);
178std::string str_json(
const std::string& s) {
179 std::string out =
"\"";
181 if (c ==
'"' || c ==
'\\') {
184 }
else if (c ==
'\n') {
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;
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";
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) {
222 s += str_json(r.station_names[i]);
224 s +=
"], \"classNames\": [";
225 for (std::size_t i = 0; i < r.class_names.size(); ++i) {
227 s += str_json(r.class_names[i]);
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);
244 if (!r.DropRateJoin.empty()) s +=
", \"DropRateJoin\": " + mat_json(r.DropRateJoin, S, C);
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);
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];
274 s +=
"{\"name\": " + str_json(t.name) +
", \"stations\": [";
275 for (std::size_t k = 0; k < t.stations.size(); ++k) {
277 s += std::to_string(t.stations[k]);
279 s +=
"], \"class\": " + std::to_string(t.job_class) +
", \"mean\": [";
280 for (std::size_t k = 0; k < t.mean.size(); ++k) {
284 s +=
"], \"count\": [";
285 for (std::size_t k = 0; k < t.count.size(); ++k) {
287 s += num(t.count[k]);
294 if (!r.cache_metrics.empty()) {
295 s +=
" \"cacheMetrics\": {";
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 +=
", ";
303 s += str_json(it->first) +
": {";
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);
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) {
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) {
353 for (std::size_t k = 0; k < (*src[q])[i].size(); ++k) {
356 s += mat_json(mm, mm.
rows(), mm.
cols());
362 if (!respt_json.empty()) s +=
", \"respTimeSamples\": " + respt_json;
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);
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);
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);
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);
407int main(
int argc,
char** argv) {
412 const std::string cmd = argv[1];
413 if (cmd ==
"-h" || cmd ==
"--help") {
417 if (cmd !=
"solve") {
418 std::cerr <<
"Error: unknown command '" << cmd <<
"'. The only command is 'solve'.\n";
421 if (argc < 3) fail(
"solve requires a model file.");
423 const std::string model_path = argv[2];
424 std::string output_path;
426 bool have_samples =
false, have_events =
false;
428 for (
int i = 3; i < argc; ++i) {
429 const std::string a = argv[i];
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()));
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()));
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") {
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") {
473 }
else if (a ==
"--slotlength") {
474 o.slot_length = std::atof(need_value(argc, argv, i, a).c_str());
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;
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") {
504 fail(
"unknown option '" + a +
"'.");
507 if (have_events && !have_samples) o.samples = o.events;
513 const std::clock_t t_start = std::clock();
515 const double runtime =
static_cast<double>(std::clock() - t_start) / CLOCKS_PER_SEC;
517 const std::string doc = result_json(r, o, runtime, o.samples);
518 if (output_path.empty()) {
521 std::ofstream out(output_path.c_str());
522 if (!out) fail(
"cannot write '" + output_path +
"'.");
525 std::cerr <<
"LDES analysis [method: " << o.method
526 <<
"; type: approximate, randomized; lang: cpp] completed in " << runtime
527 <<
"s. Iterations: " << o.samples <<
".\n";
530 std::cerr <<
"Error: " << e.what() <<
"\n";
532 }
catch (
const std::exception& e) {
533 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.