![]() |
LINE Solver (C++)
Templated C++ port of the LINE queueing solver
|
Port of SolverLDES, the discrete-event simulator, as its C++ client. More...
#include "line/util/line_console.h"#include <algorithm>#include <cmath>#include <cstddef>#include <cstdio>#include <cstdlib>#include <cstring>#include <fstream>#include <limits>#include <sstream>#include <string>#include <utility>#include <vector>#include <sys/stat.h>#include <sys/utsname.h>#include <unistd.h>#include "json.hpp"#include "line/io/network_writer.h"#include "line/solvers/wrappers/ldes/ldes_options.h"#include "line/solvers/wrappers/ldes/ldes_probe.h"#include "line/util/error.h"#include "line/util/http.h"#include "line/util/matrix.h"#include "line/util/subprocess.h"#include "line/util/tempdir.h"Go to the source code of this file.
Classes | |
| struct | line::ldes::LdesRunner |
| One runner: the argv prefix that runs the engine, and the name of its image. More... | |
| struct | line::ldes::LdesStateQuery |
| One constraint of a joint-state query: a station and the per-class job counts it is required to hold. More... | |
Namespaces | |
| namespace | line |
| namespace | line::ldes |
Functions | |
| std::vector< std::string > | line::ldes::ldes_flags (const LdesOptions &o, const std::vector< std::string > &extra) |
| The engine flags of one run, after solve <model> -o <result>. | |
| std::vector< LdesRunner > | line::ldes::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). | |
| detail::Json | line::ldes::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 | line::ldes::parse_ldes_result (const detail::Json &d) |
| Parses one ldes-result document. | |
| LdesResult | line::ldes::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. | |
| LdesResult | line::ldes::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. | |
| double | line::ldes::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. | |
| Matrix< double > | line::ldes::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, built from the per-job samples the engine exports under --respt-samples. | |
| double | line::ldes::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. | |
| double | line::ldes::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. | |
| template<class T> | |
| LdesResult | line::ldes::solver_ldes (const qn::NetworkStruct< T > &sn, const LdesOptions &o, const std::vector< std::string > &extra_flags=std::vector< std::string >()) |
| The same, for a model built through the C++ API. | |
Port of SolverLDES, the discrete-event simulator, as its C++ client.
WHAT IS PORTED, AND WHY IT IS A CLIENT AND NOT AN ENGINE. LDES has ONE implementation, jar/src/main/java/jline/solvers/wrappers/ldes/ (~25 kLOC on SSJ), and every codebase that is not the JAR reaches it the same way: MATLAB (@@SolverLDES/solveCli.m) and native Python (wrappers/solver_ldes/solver_ldes.py) serialize the model to model.json, run solve model.json -o result.json as a subprocess and parse the result document back. Nothing is marshalled in process. This file is the third such client, and it is the port of SolverLDES for the same reason those two are: a second simulator would answer with different numbers under the same name, and "LDES agrees across the codebases" – which the parity harness checks and CLAUDE.md states as an invariant – is a property of there being ONE engine.
THE PRIMARY ENTRY IS A DOCUMENT, not a model object, and that is a deliberate difference from the two other clients. They hold a live model and serialize it; solver_ldes_text / solver_ldes_file are given the model.json DOCUMENT and forward its bytes UNCHANGED. There is a writer (io/network_writer.h), so a round trip through qn::NetworkStruct<T> is possible – solver_ldes below is exactly that, for a caller who built the model in C++ – but the reader that would have to parse the document first is deliberately scoped to the subset the analytical solvers read, so making the trip MANDATORY would degrade exactly the models LDES exists for (a cache with retrieval, an SPN, a polling server, a G-network signal). Forwarding the bytes keeps the client lossless on every model the engine accepts, including those this port cannot itself represent.
WHICH BINARY RUNS. ldes_runners reproduces getLdesRunners and _build_cli_runners: the AOT GraalVM binary common/ldes first, for its startup time, then <java> -jar common/ldes.jar, and each is tried in turn because the AOT image lacks reflective features the JVM has (the fork-join MMT transform deep-copies the model by Java serialization). THE ORDER IS REVERSED, not merely extended, when the run uses something the prebuilt image predates – --respt-samples, --initsol, a cache cost cap, a marked class, a load/class-dependent scaling – because on that image the flag is not refused but IGNORED, and an ignored cost cap silently simulates the uncapped cache. The two other clients inspect the live model object for those; this one inspects the document, which is the same information at the point where it actually crosses.
STATE PROBABILITIES ARE READ FROM THE HISTOGRAM, not from the trajectory. ldes_prob_aggr / ldes_prob_sys_aggr take the target state as an ARGUMENT and weigh it against stateHistogram, the exact residence time of every integer joint state the run visited. The no-argument form the reference also offers – "the probability of the model's CURRENT state" – has no counterpart here because qn::NetworkStruct carries no current state row (only a Cache's initstate, a Place's initmarking and the stateprior/statespace pair). The DEFAULT INITIAL state is a different thing and is derivable, by the same ctmc::analyzer_detail::default_init_state the -a prob arms of CTMC, MVA and NC already answer over; the LDES arm of the CLI still refuses -a prob on the older premise that no state exists at all, which is a reachability gap in cpp/src/cli/line_cli.cpp rather than a gap in this client. The three other clients answer BOTH forms off the transient QNt series, which is a trajectory of interval MEANS, and so report a near-zero probability for a state the chain mostly occupies; see BUG-96 and the note on LdesResult::QNt.
WHAT THIS CLIENT DOES NOT PORT, and why it refuses instead of approximating: LayeredNetwork MATLAB simulates an LQN through the Java ensemble backend (self.obj), an in-process path with no JSON interface; LdesCLI itself refuses a LayeredNetwork document ("Network models only"). There is nothing to forward. server breakdowns linemodel_save does not serialize setBreakdown at all, so a breakdown cannot reach this client: the refusal the Python client performs is on the OBJECT, before serialization, and has no wire counterpart.
WHERE THE ENGINE IS, and whether there is one, lives in ldes_probe.h: SolverAUTO needs that answer – LDES leads several of the reference's rankings, and a chooser that named it on a host without the engine would be proposing something that cannot run – and must not take on this file's HTTP transport and result parser to get it.
Definition in file solver_ldes.h.