LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
uq_dispatch.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_UQ_UQ_DISPATCH_H
6#define LINE_SOLVERS_UQ_UQ_DISPATCH_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * The stage solver of SolverUQ, named rather than passed.
12 *
13 * `UQ(model, @@SolverMVA)` gives the reference a factory; a caller who has a
14 * solver NAME -- the CLI, a host bridge -- needs that name turned into one, and
15 * this is where the turning happens. It is separate from `solver_uq.h` for the
16 * reason `env_dispatch.h` is separate from `solver_env.h`: the UQ machinery
17 * itself depends on nothing but the `AvgResult` contract, while this file pulls
18 * in every Network solver in the port, and a caller that already has a functor
19 * should not pay for that.
20 *
21 * WHAT EACH TOKEN COSTS, since UQ multiplies it by the design size: `mva`, `nc`
22 * and `ba` are closed forms or short iterations; `mam`, `ctmc`, `ssa` and
23 * `fluid` are not, and a 121-point design over two continuous Priors under
24 * `-s ctmc` enumerates the state space 121 times. That is the intended
25 * behaviour -- each design point IS a different model -- and it is why the
26 * design cap exists.
27 *
28 * THE ARITHMETIC RESTRICTIONS ARE THE INNER SOLVER'S, and they are enforced
29 * here at COMPILE time by `if constexpr` plus a run-time refusal: `mam` fits
30 * phase-type representations, `ssa` draws exponential clocks and `fluid`
31 * integrates with LSODA, so all three are double-only and an exact or
32 * high-precision instantiation of them would fail to compile rather than refuse.
33 *
34 * ARVR AND RESIDT ON THE SIMULATION AND FLUID PATHS are filled exactly as
35 * `line_cli.cpp` fills them for `-s ssa` and `-s fluid` -- residence time equals
36 * response time, arrival rate equals throughput except at a Source, which has no
37 * arrivals to itself. Those two solution types carry no separate AN/WN matrix,
38 * and inventing a different convention here would make the UQ expectation of a
39 * column disagree with the same column printed by the solver alone.
40 */
41
42#include <cstddef>
43#include <string>
44#include <type_traits>
45#include <vector>
46
48#include "line/num/number.h"
59#include "line/util/error.h"
60
61namespace line {
62namespace uq {
63
64/** The inner solver's knobs, carried through untranslated. */
66 /** `mva` | `nc` | `mam` | `ba` | `ctmc` | `fluid` | `ssa`. */
67 std::string solver;
68 /** The method WITHIN that solver; empty or `default` leaves its own default. */
69 std::string method;
70 double tol = -1.0; ///< < 0 = not given
71 double iter_tol = -1.0;
72 int iter_max = -1;
73 std::size_t samples = 0; ///< ssa run length; 0 = not given
74 unsigned long seed = 0; ///< ssa stream; 0 = not given
75 double cutoff = -1.0; ///< ctmc open-population cutoff; < 0 = not given
76};
77
78/** The solver method names `uq_stage_solver` accepts, for a caller that lists them. */
79inline std::vector<std::string> uq_list_stage_solvers() {
80 return std::vector<std::string>{"mva", "nc", "mam", "ba", "ctmc", "fluid", "ssa"};
81}
82
83namespace detail {
84
85/** The double-matrix solutions of the fluid and simulation paths, as an AvgResult. */
86template <class T, class Sol>
87mva::AvgResult<T> from_double_solution(const qn::NetworkStruct<T>& sn, const Sol& r) {
89 const std::size_t M = r.QN.rows(), K = r.QN.cols();
90 const T zero = num_traits<T>::from_int(0);
91 a.QN = Matrix<T>(M, K, zero);
92 a.UN = Matrix<T>(M, K, zero);
93 a.RN = Matrix<T>(M, K, zero);
94 a.TN = Matrix<T>(M, K, zero);
95 a.AN = Matrix<T>(M, K, zero);
96 a.WN = Matrix<T>(M, K, zero);
97 for (std::size_t i = 0; i < M; ++i)
98 for (std::size_t c = 0; c < K; ++c) {
99 a.QN(i, c) = num_traits<T>::from_double(r.QN(i, c));
100 a.UN(i, c) = num_traits<T>::from_double(r.UN(i, c));
101 a.RN(i, c) = num_traits<T>::from_double(r.RN(i, c));
102 a.TN(i, c) = num_traits<T>::from_double(r.TN(i, c));
103 // Residence time IS the response time and the arrival rate IS the
104 // throughput on these paths, except at a Source, which does not
105 // arrive at itself; see the header note.
106 a.WN(i, c) = a.RN(i, c);
107 a.AN(i, c) = sn.stations[i].sched == lang::SchedStrategy::EXT ? zero : a.TN(i, c);
108 }
109 for (double v : r.CN) a.CN.push_back(num_traits<T>::from_double(v));
110 for (double v : r.XN) a.XN.push_back(num_traits<T>::from_double(v));
111 a.method = r.method;
112 a.actualmethod = r.method;
113 return a;
114}
115
116} // namespace detail
117
118/**
119 * The stage solver named by `o.solver`.
120 *
121 * An unknown or unported name is refused BY NAME rather than answered with a
122 * default engine: which solver ran is a property of every number UQ reports.
123 */
124template <class T>
126 const std::string s = o.solver;
127 if (s == "mva") {
128 return [o](const qn::NetworkStruct<T>& sn) {
130 if (!o.method.empty() && o.method != "default") opt.method = o.method;
131 if (o.tol >= 0.0) opt.tol = o.tol;
132 if (o.iter_tol >= 0.0) opt.iter_tol = o.iter_tol;
133 if (o.iter_max >= 0) opt.iter_max = o.iter_max;
134 Matrix<T> init;
135 return mva::solver_mva_run_analyzer(sn, opt, init);
136 };
137 }
138 if (s == "nc") {
139 return [o](const qn::NetworkStruct<T>& sn) {
141 if (!o.method.empty() && o.method != "default") opt.method = o.method;
142 if (o.tol >= 0.0) opt.tol = o.tol;
143 if (o.iter_tol >= 0.0) opt.iter_tol = o.iter_tol;
144 if (o.iter_max >= 0) opt.iter_max = o.iter_max;
146 };
147 }
148 if (s == "ba") {
149 return [o](const qn::NetworkStruct<T>& sn) {
151 if (!o.method.empty() && o.method != "default") opt.method = o.method;
153 };
154 }
155 if (s == "ctmc") {
156 return [o](const qn::NetworkStruct<T>& sn) {
158 if (!o.method.empty() && o.method != "default") opt.method = o.method;
159 if (o.cutoff >= 0.0) opt.cutoff = o.cutoff;
161 };
162 }
163 if (s == "mam") {
164 if constexpr (std::is_same<T, double>::value) {
165 return [o](const qn::NetworkStruct<T>& sn) {
167 if (!o.method.empty() && o.method != "default") opt.method = o.method;
168 if (o.tol >= 0.0) opt.tol = o.tol;
169 if (o.iter_max >= 0) opt.iter_max = o.iter_max;
171 };
172 } else {
173 throw UnsupportedError(
174 "SolverUQ: the MAM stage solver fits phase-type representations, whose fitter "
175 "requires transcendental arithmetic; run UQ under the double backend");
176 }
177 }
178 if (s == "fluid" || s == "fld") {
179 if constexpr (std::is_same<T, double>::value) {
180 return [o](const qn::NetworkStruct<T>& sn) {
182 if (!o.method.empty()) opt.method = o.method;
183 if (o.tol >= 0.0) opt.tol = o.tol;
184 if (o.iter_tol >= 0.0) opt.iter_tol = o.iter_tol;
185 if (o.iter_max >= 0) opt.iter_max = static_cast<std::size_t>(o.iter_max);
186 return detail::from_double_solution<T>(sn, fluid::solver_fluid_run_analyzer(sn, opt));
187 };
188 } else {
189 throw UnsupportedError(
190 "SolverUQ: the fluid stage solver integrates its drift with LSODA, which is double "
191 "precision by construction; run UQ under the double backend");
192 }
193 }
194 if (s == "ssa") {
195 if constexpr (std::is_same<T, double>::value) {
196 return [o](const qn::NetworkStruct<T>& sn) {
198 if (!o.method.empty() && o.method != "default") opt.method = o.method;
199 if (o.samples) opt.samples = o.samples;
200 if (o.seed) opt.seed = o.seed;
201 // EVERY DESIGN POINT GETS THE SAME STREAM, deliberately: the
202 // points differ by the model, so a per-point seed would mix
203 // Monte Carlo error into the epistemic spread the design is
204 // there to measure. Common random numbers is the standard
205 // variance-reduction pairing for exactly this comparison.
206 return detail::from_double_solution<T>(sn, ssa::solver_ssa(sn, opt));
207 };
208 } else {
209 throw UnsupportedError(
210 "SolverUQ: an SSA sample path is generated from exponential clocks, which are "
211 "transcendental; run UQ under the double backend");
212 }
213 }
214 if (s.empty())
215 throw InputError(
216 "SolverUQ: no stage solver was named. UQ solves nothing itself; name the solver that "
217 "runs at each design point (mva, nc, mam, ba, ctmc, fluid or ssa)");
218 throw UnsupportedError("SolverUQ: '" + s +
219 "' is not a stage solver this port carries; the ported names are mva, "
220 "nc, mam, ba, ctmc, fluid and ssa");
221}
222
223} // namespace uq
224} // namespace line
225
226#endif // LINE_SOLVERS_UQ_UQ_DISPATCH_H
InputError(const std::string &what)
Definition error.h:39
UnsupportedError(const std::string &what)
Definition error.h:51
A network plus its refreshed NetworkStruct.
The exception types the port throws.
The fluid solver's outermost entry point: @@SolverFLD/runAnalyzer.m's method resolution over solver_f...
mva::AvgResult< T > solver_ba_run_analyzer(const qn::NetworkStruct< T > &L, const BaOptions &opt_in)
Port of @@SolverBA/runAnalyzer.m for the lang='matlab' path.
mva::AvgResult< T > solver_ctmc_run_analyzer_any(const NetworkStruct< T > &sn, const CtmcOptions &opt)
Solve on whichever path applies and format, mirroring solver_ctmc_run_analyzer.
FluidSolution solver_fluid_run_analyzer(const qn::NetworkStruct< T > &sn, const FluidOptions &opt, qn::NetworkStruct< T > *sn_out=nullptr, qn::NetworkStruct< T > *refreshed_out=nullptr, solvers::CacheMetrics< T > *cache_out=nullptr)
Port of @@SolverFLD/runAnalyzer.m: resolve the method, route to the function the reference routes to,...
mva::AvgResult< T > solver_mam_run_analyzer(const qn::NetworkStruct< T > &L, const MamOptions &opt)
Port of @@SolverMAM/runAnalyzer.m for the lang='matlab' path: solve, then apply the metric filter @@N...
AvgResult< T > solver_mva_run_analyzer(const qn::NetworkStruct< T > &L, const MvaOptions &opt_in, const Matrix< T > &init_sol)
Port of @@SolverMVA/runAnalyzer.m for the lang='matlab' path: gate, solve, convert,...
mva::AvgResult< T > solver_nc_run_analyzer(const qn::NetworkStruct< T > &L_in, const NcSolverOptions &opt_in)
Port of @@SolverNC/runAnalyzer.m for the lang='matlab' path: solve, then apply the metric filter @@Ne...
SsaSolution solver_ssa(const qn::NetworkStruct< T > &sn, const SsaOptions &opt, std::vector< SsaCacheRatio > *cache=nullptr)
solver_ssa_analyzer.m: choose the method.
std::function< mva::AvgResult< T >(const qn::NetworkStruct< T > &)> UqStageSolver
What solves one design point: the C++ spelling of @(m) SolverXXX(m).
Definition solver_uq.h:332
UqStageSolver< T > uq_stage_solver(const UqStageOptions &o)
The stage solver named by o.solver.
std::vector< std::string > uq_list_stage_solvers()
The solver method names uq_stage_solver accepts, for a caller that lists them.
Definition uq_dispatch.h:79
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
The SolverBA class surface: @@SolverBA/runAnalyzer.m, listValidMethods, getBounds and getBoundsTable.
Port of solver_ctmc_analyzer.m and the parts of @@SolverCTMC/runAnalyzer.m that surround one solve: t...
Port of solver_ctmc_fcr_waitq.m: the reachability-built generator of a model whose finite capacity re...
SolverFluid: the closing method, a port of solver_fluid.m, solver_fluid_iteration....
The SolverMAM class surface: @@SolverMAM/runAnalyzer.m and the gates around it.
The SolverMVA class surface: @@SolverMVA/runAnalyzer.m and the gates around it.
The SolverNC class surface: @@SolverNC/runAnalyzer.m and the gates around it.
SolverUQ: uncertainty quantification by expansion over a Prior.
The SolverSSA entry surface: a port of @@SolverSSA/runAnalyzer.m's method whitelist,...
The options SolverBA reads.
The SolverCTMC knobs this port honours.
Controls, defaulting to SolverOptions('Fluid') in the reference.
The options SolverMAM reads.
Definition mam_types.h:29
The metrics getAvg returns, after filtering.
Matrix< T > TN
throughput
Matrix< T > RN
response time, per visit
Matrix< T > UN
utilization
Matrix< T > WN
residence time, per job
std::string method
the method asked for
std::string actualmethod
the algorithm that ran
Matrix< T > QN
queue length
std::vector< T > CN
system response time per class
std::vector< T > XN
system throughput per class
Matrix< T > AN
arrival rate
The options SolverMVA reads.
Definition mva_types.h:31
Controls, defaulting to SolverOptions('NC') in the reference.
Definition nc_types.h:33
Controls, defaulting to SolverOptions('SSA') in the reference.
Definition ssa_types.h:69
The inner solver's knobs, carried through untranslated.
Definition uq_dispatch.h:65
double tol
< 0 = not given
Definition uq_dispatch.h:70
std::size_t samples
ssa run length; 0 = not given
Definition uq_dispatch.h:73
std::string method
The method WITHIN that solver; empty or default leaves its own default.
Definition uq_dispatch.h:69
unsigned long seed
ssa stream; 0 = not given
Definition uq_dispatch.h:74
double cutoff
ctmc open-population cutoff; < 0 = not given
Definition uq_dispatch.h:75
std::string solver
mva | nc | mam | ba | ctmc | fluid | ssa.
Definition uq_dispatch.h:67