LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
ldes_options.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_SOLVERS_WRAPPERS_LDES_LDES_OPTIONS_H
6#define LINE_SOLVERS_WRAPPERS_LDES_LDES_OPTIONS_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * The option and result records of SolverLDES, the discrete-event simulator.
12 *
13 * SOURCE. `LDESOptions` transcribes `python/line_solver/solvers/wrappers/
14 * solver_ldes/ldes_options.py` and the `ldesOpt` reads of
15 * `@@SolverLDES/solveCli.m`; `LdesResult` transcribes the `ldes-result`
16 * document `jline/io/LDESResultIO.java` writes, block for block. The two
17 * subprocess clients that precede this one (MATLAB, native Python) parse a
18 * SUBSET of that document; this record carries all of it, because a block that
19 * is parsed nowhere is a block whose meaning drifts unnoticed.
20 *
21 * EVERY KNOB IS A CLI FLAG, and the defaults here are the ENGINE's, not this
22 * port's: `ldes_flags` below emits a flag only when the value differs from the
23 * default, exactly as `_build_flag_args` and `solveCli` do, so a default run
24 * produces the minimal command line an older AOT native binary still parses. A
25 * default that disagreed with `LdesCLI`'s would therefore be invisible in the
26 * command line and change the answer -- which is why `seed` is the one
27 * exception and is ALWAYS emitted: the CLI default is -1 (a random seed), not
28 * 23000, so omitting it at the nominal seed would make the run irreproducible
29 * and silently different from the MATLAB and Python clients.
30 */
31
32#include <cstddef>
33#include <limits>
34#include <map>
35#include <string>
36#include <vector>
37
39#include "line/util/matrix.h"
40
41namespace line {
42namespace ldes {
43
44/**
45 * Port of `SolverLDES.listValidMethods`.
46 *
47 * Two names, and only one engine behind them: 'parallel' asks the engine for
48 * INDEPENDENT REPLICATIONS and the mean over them, which the client turns into
49 * `--replications` (the caller's count when set, 8 otherwise). It is not a
50 * second simulator, which is why the list is this short in all four codebases.
51 * 'para' is its short spelling, as in SolverSSA.
52 */
53inline std::vector<std::string> list_valid_methods() {
54 return {"default", "para", "parallel"};
55}
56
57/**
58 * The knobs of one LDES run.
59 *
60 * `samples` is an EVENT BUDGET (service completions), not a sample size in the
61 * statistical sense, and `events` is the same budget under the name the newer
62 * API uses: when set it overrides `samples`, as it does in both clients.
63 */
65 std::size_t samples = 200000; ///< -s, service-completion budget
66 std::size_t events = 0; ///< 0 = not given; overrides `samples` when set
67 long seed = 23000; ///< --seed; -1 requests a random stream
68 std::string method = "default";
69
70 // ---- convergence-based stopping ----------------------------------------
71 bool cnvgon = false; ///< --cnvgon
72 double cnvgtol = 0.05; ///< --cnvgtol
73 int cnvgbatch = 20; ///< --cnvgbatch, batches before the first check
74 int cnvgchk = 0; ///< --cnvgchk, events between checks; 0 = samples/50
75
76 // ---- warmup (transient) filter -----------------------------------------
77 std::string tranfilter = "mser5"; ///< --tranfilter: mser5 | fixed | none
78 int mserbatch = 5; ///< --mserbatch, MSER batch size
79 double warmupfrac = 0.2; ///< --warmupfrac, only for tranfilter=fixed
80 /// --tranobs, equally spaced transient observation points over [t0,t1]; fixes the time grid only.
81 /// 0 = not given: the engine default, 1000 in both the native and the JAR engine.
82 int tranobs = 0;
83
84 // ---- confidence intervals ----------------------------------------------
85 std::string cimethod = "obm"; ///< --cimethod: obm | bm | spectral | none
86 double obmoverlap = 0.5; ///< --obmoverlap; 0 reduces OBM to plain batch means
87 int ciminbatch = 10; ///< --ciminbatch
88 int ciminobs = 100; ///< --ciminobs, below which no CI is reported
89 /**
90 * Confidence level of the reported half-widths.
91 *
92 * `LDESOptions` sets 0.95 where the shared `SolverOptions` leaves it at 0
93 * (disabled), so the LDES default is intervals ON. It is not a CLI flag:
94 * the engine reads it from the options object, so the subprocess client
95 * has no way to vary it and the native engine keeps the same default.
96 */
97 double confint = 0.95;
98 double spectral_low_freq_frac = 0.25; ///< --spectrallowfreqfrac
99 /**
100 * `options.config.runLengthPlan`: ask for the run length this run SHOULD
101 * have had, for a target relative precision.
102 *
103 * Non-positive leaves the plan uncomputed. The half-widths above pin the
104 * ASYMPTOTIC variance of each estimator -- not its stationary variance,
105 * which on M/M/1 differs from it by a factor blowing up like (1-rho)^-2 --
106 * and `sim_runlength_plan` turns that into the run length that reaches this
107 * precision. The plan lands in `LdesResult::run_length_plan`.
108 */
110
111 // ---- discrete time ------------------------------------------------------
112 bool slotted = false; ///< --slotted, run on the slot lattice
113 double slot_length = 1.0; ///< --slotlength
114
115 // ---- busy periods -------------------------------------------------------
116 /**
117 * --busyperiod: highest busy period order measured, 0 disabling the
118 * measurement. A busy period of order n for a set of stations runs from the
119 * instant an arrival raises the jobs it holds to n up to the instant it
120 * falls back below n (Daduna, J. ACM 35(3), 1988). When positive the orders
121 * 1..busy_period_orders are measured for every station, for every
122 * station-class pair, and for every set in `busy_period_subnets`.
123 */
125 /** --busyperiod-subnet, zero-based station indexes, one flag per set. */
126 std::vector<std::vector<std::size_t>> busy_period_subnets;
127
128 // ---- replication --------------------------------------------------------
129 int replications = 0; ///< --replications; 0 = not given (one path)
130 int numthreads = 0; ///< --numthreads; 0 = not given
131
132 // ---- horizon and budget --------------------------------------------------
133 bool has_timespan = false; ///< true when [t0,t1] was set: a TRANSIENT run
134 double t0 = 0.0;
135 double t1 = 0.0;
136 /** --maxtime, a COOPERATIVE wall-clock budget the event loop polls. */
137 double timeout = std::numeric_limits<double>::infinity();
138
139 /**
140 * --initsol, the warm-start placement as a STATION-MAJOR vector
141 * [st0_cl0, st0_cl1, ..., stM-1_clK-1]. Empty = the engine's own default
142 * (closed jobs at their reference stations).
143 */
144 std::vector<double> init_sol;
145
146 /**
147 * --export-histogram and --trajectory: the joint-state residence-time law
148 * and the state path.
149 *
150 * A TRANSIENT run fills both unconditionally, because it has to walk the
151 * state to produce QNt at all. In STEADY STATE they are opt-in: the
152 * histogram costs a map lookup per event and the trajectory grows one row
153 * per event, which on a million-completion run is a million rows nobody
154 * asked for.
155 */
156 bool export_histogram = false;
157 bool export_trajectory = false;
158
159 /**
160 * --respt-samples: every per-visit response time, not just its mean.
161 *
162 * It is the input to an EMPIRICAL CDF, which a mean cannot stand in for, and
163 * it costs one push per completion, so it is opt-in like the two above.
164 */
165 bool export_respt = false;
166
167 /**
168 * Base URL of an LDES REST server (the imperialqore/ldes container). When
169 * set the model is POSTed to `<url>/api/v1/solve` instead of being handed to
170 * a local binary; the wire format is the same on both sides, so a fixed
171 * seed gives the same numbers.
172 */
173 std::string rest_url;
174
175 bool verbose = false; ///< echo the resolved command line before running it
176};
177
178/** Per-cache hit/miss/latency, as the `cacheMetrics` block carries them. */
180 Matrix<double> hit; ///< (1 x nclasses) hit probability
181 Matrix<double> delayed; ///< (1 x nclasses) delayed-hit probability
182 Matrix<double> miss; ///< (1 x nclasses) miss probability
183 Matrix<double> latency; ///< (1 x nclasses) expected retrieval latency
184 Matrix<double> hitList; ///< (nclasses x nlists) hit probability by list
185 Matrix<double> itemProb; ///< (nitems x nlists+1) item position law
186 Matrix<double> listCost; ///< (1 x nlists) mean storage cost held per list
187};
188
189/**
190 * One `ldes-result` document, parsed.
191 *
192 * A metric the engine did not report stays EMPTY rather than becoming a matrix
193 * of zeros: zero is a measurement and absence is not, and the tables below
194 * distinguish them.
195 */
197 // ---- dimensions and labels ----------------------------------------------
198 std::size_t nstations = 0;
199 std::size_t nclasses = 0;
200 std::size_t nchains = 0;
201 std::size_t nregions = 0;
202 std::vector<std::string> station_names;
203 std::vector<std::string> class_names;
204
205 // ---- means, (nstations x nclasses) --------------------------------------
207 Matrix<double> CN, XN; ///< (1 x nclasses), per-class visits and system tput
208 Matrix<double> DropRateJoin; ///< quorum-Join sibling drops, Join rows only
209
210 // ---- confidence intervals and relative precision -------------------------
212 /**
213 * `options.config.runLengthPlan`: the run length the caller would need for
214 * the precision they asked for, from the half-widths above. Empty unless
215 * `LdesOptions::run_length_plan_precision` is positive.
216 */
221
222 // ---- finite capacity regions, (nregions x nclasses) ----------------------
225
226 // ---- impatience, (nstations x nclasses) ----------------------------------
230
231 /** Per-cache metrics, keyed by the Cache NODE name. */
232 std::map<std::string, LdesCacheMetrics> cache_metrics;
233
234 /**
235 * The exact joint-state residence-time histogram (--export-histogram).
236 *
237 * `histogram_space` is (nstates x nstations*nclasses) in the aggregate
238 * layout `ctmc_state_space_aggr` builds and `histogram_time` the residence
239 * time of each row, so E[r] = sum_s (t_s / sum t) r(state_s) is exact also
240 * for a NONLINEAR reward. `traj_*` is the same encoding along the sampled
241 * path, downsampled, and is what the transient reward reads.
242 */
247
248 /**
249 * One measured busy period target (--busyperiod): a station, a
250 * station-class pair, or a declared station set. `mean` and `count` are
251 * indexed by order 1..K, and a mean whose count is zero is not an
252 * observation. `job_class` is -1 when the target aggregates every class.
253 */
255 std::string name;
256 std::vector<std::size_t> stations; ///< station indexes, zero-based
257 int job_class = -1;
258 std::vector<double> mean;
259 std::vector<double> count;
260 };
261 std::vector<BusyPeriodTarget> busy_periods;
262
263 // ---- transient trajectory (--timespan + --trajectory) --------------------
264 std::vector<double> t; ///< the time vector, empty on a steady-state run
265 /**
266 * [STATION][class] -> (npoints x 2), columns [value, time].
267 *
268 * THE OUTER INDEX IS THE STATION, not the stateful node: the engine builds
269 * these as `result.QNt = new Matrix[numStations][numClasses]` and fills them
270 * at `serviceStation` (`Solver_ssj.getTransientLDESResultBody`). The two
271 * index spaces coincide only when every stateful node is a station, so on a
272 * model holding a Router, a Cache or a Place the stateful index selects
273 * another station's series or runs off the end. That is the defect recorded
274 * as BUG-95 and fixed in the MATLAB and Python clients.
275 *
276 * THE VALUES ARE INTERVAL TIME-AVERAGES, not the integer states the path
277 * visits: each entry is (integral of the queue length over the sampling
278 * interval) / (interval length). They are a trajectory of MEANS, so they
279 * must not be compared against a state to estimate a probability -- use
280 * `histogram_space` / `histogram_time` for that.
281 */
282 std::vector<std::vector<Matrix<double>>> QNt, UNt, TNt;
283 /** [station][class] -> the per-job response times the engine recorded. */
284 std::vector<std::vector<std::vector<double>>> respTimeSamples;
285
286 // ---- provenance -----------------------------------------------------------
287 std::string method = "default";
288 double runtime = 0.0;
289 bool converged = false;
290 /** convergence | max_events | max_sim_events | max_time. */
291 std::string stopping_reason;
292 /// ldes-result "warnings": what the engine warned on its own console, e.g. a
293 /// parallel pool that timed out and averaged fewer replications than requested
294 std::vector<std::string> warnings;
297 /** True when the HARD subprocess bound fired, not the cooperative one. */
298 bool timed_out = false;
299 /** "native" or "jar": which runner produced these numbers. */
300 std::string engine;
301};
302
303} // namespace ldes
304} // namespace line
305
306#endif // LINE_SOLVERS_WRAPPERS_LDES_LDES_OPTIONS_H
Dense matrix and non-owning view.
std::vector< std::string > list_valid_methods()
Port of SolverLDES.listValidMethods.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Run-length planning for steady-state simulation.
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
double cnvgtol
–cnvgtol
double confint
Confidence level of the reported half-widths.
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
double run_length_plan_precision
options.config.runLengthPlan: ask for the run length this run SHOULD have had, for a target relative ...
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
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
bool export_respt
–respt-samples: every per-visit response time, not just its mean.
int replications
–replications; 0 = not given (one path)
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
bool export_histogram
–export-histogram and –trajectory: the joint-state residence-time law and the state path.
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
One ldes-result document, parsed.
Matrix< double > balkedCustomers
Matrix< double > QNSamples
sim::RunLengthPlan< double > run_length_plan
std::map< std::string, LdesCacheMetrics > cache_metrics
Per-cache metrics, keyed by the Cache NODE name.
Matrix< double > avgOrbitSize
Matrix< double > QNRelPrec
Matrix< double > TNCI
Matrix< double > WNfcr
std::vector< std::vector< Matrix< double > > > QNt
[STATION][class] -> (npoints x 2), columns [value, time].
Matrix< double > renegingRate
Matrix< double > XN
(1 x nclasses), per-class visits and system tput
std::vector< std::vector< std::vector< double > > > respTimeSamples
[station][class] -> the per-job response times the engine recorded.
Matrix< double > UNfcr
Matrix< double > WeightNfcr
std::vector< std::string > warnings
ldes-result "warnings": what the engine warned on its own console, e.g.
Matrix< double > RNRelPrec
Matrix< double > avgRenegingWaitTime
Matrix< double > QNfcr
Matrix< double > traj_space
bool has_run_length_plan
options.config.runLengthPlan: the run length the caller would need for the precision they asked for,...
Matrix< double > UN
Matrix< double > DropRateNfcr
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::vector< Matrix< double > > > UNt
std::vector< std::string > class_names
Matrix< double > TNRelPrec
Matrix< double > TN
Matrix< double > UNCI
Matrix< double > TNSamples
Matrix< double > RN
Matrix< double > UNRelPrec
std::string engine
"native" or "jar": which runner produced these numbers.
Matrix< double > QNCI
Matrix< double > traj_time
std::vector< BusyPeriodTarget > busy_periods
Matrix< double > CN
Matrix< double > RNfcr
Matrix< double > WN
Matrix< double > retriedCustomers
Matrix< double > retrialDropped
Matrix< double > AN
long long total_simulated_events
Matrix< double > MemOccNfcr
Matrix< double > TNfcr
std::vector< std::string > station_names
Matrix< double > DropRateJoin
quorum-Join sibling drops, Join rows only
Matrix< double > UNSamples
bool timed_out
True when the HARD subprocess bound fired, not the cooperative one.
Matrix< double > balkingProbability
Matrix< double > renegedCustomers
Matrix< double > histogram_time
Matrix< double > RNSamples
Matrix< double > RNCI
std::vector< double > t
the time vector, empty on a steady-state run
Matrix< double > WNCI
Matrix< double > QN
std::vector< std::vector< Matrix< double > > > TNt
Matrix< double > ANfcr
Matrix< double > ANCI
The plan of sim_runlength_plan: what the run should have been.