5#ifndef LINE_SOLVERS_MAP_ENV_H
6#define LINE_SOLVERS_MAP_ENV_H
68 return info.
is_mmpp ?
"exact-modulation" :
"intensity-matched";
75 for (std::size_t s = 0; s < e.
nstages(); ++s)
77 if (!std::isfinite(t) || !(t > 0.0))
79 "map_env: the environment image has no finite stage sojourn, so no integration "
80 "horizon can be set");
98 const std::size_t E = e.
nstages();
99 std::vector<double> exit_rate;
100 for (std::size_t a = 0; a < E; ++a) {
102 for (std::size_t b = 0; b < E; ++b) {
103 if (a == b)
continue;
109 if (m > 0.0) r += 1.0 / m;
111 if (r > 0.0) exit_rate.push_back(r);
115 if (exit_rate.empty())
return "dec";
116 double tau_env = 0.0;
117 for (std::size_t i = 0; i < exit_rate.size(); ++i) tau_env += 1.0 / exit_rate[i];
118 tau_env /=
static_cast<double>(exit_rate.size());
120 double minrate = std::numeric_limits<double>::infinity();
121 for (std::size_t i = 0; i <
sn.rates.rows(); ++i)
122 for (std::size_t r = 0; r <
sn.rates.cols(); ++r) {
124 if (std::isfinite(v) && v > 0.0) minrate = std::min(minrate, v);
126 if (!std::isfinite(minrate))
return "dec";
127 const std::vector<double> nj =
sn.njobs();
129 for (std::size_t k = 0; k < nj.size(); ++k)
130 if (std::isfinite(nj[k])) totn += nj[k];
131 const double tau_sys = (1.0 + totn) / minrate;
132 return tau_env >= tau_sys ?
"dec" :
"avg";
147 for (std::size_t s = 0; s < e.
nstages(); ++s) {
150 st.
nodes.size() !=
sn.nodes.size())
151 throw InputError(
"map_env: stage " + std::to_string(s + 1) +
152 " of the environment image has a different station, class or node "
153 "count from the base model; every metric is blended at the base's "
155 for (std::size_t i = 0; i <
sn.nodes.size(); ++i)
156 if (st.
nodes[i].name !=
sn.nodes[i].name)
157 throw InputError(
"map_env: node " + std::to_string(i + 1) +
" of stage " +
158 std::to_string(s + 1) +
" is '" + st.
nodes[i].name +
159 "' where the base model has '" +
sn.nodes[i].name +
160 "'; the image must keep the base's node order");
185template <
class T,
class StageFn>
188 const std::string& requested_method =
"default") {
189 if (!std::is_same<T, double>::value)
191 "map_env: the random-environment fallback for a MAP/MMPP/MMAP/MPH process solves "
192 "each stage through SolverENV, whose couplings are double-only (a fluid stage "
193 "integrates with LSODA and a mean-field sojourn is a double quadrature). Rerun with "
194 "--arith double, or set map_env='off' to keep the plain rejection");
202 std::string method = cfg.
method.empty() ? std::string(
"auto") : cfg.
method;
203 if (method ==
"auto") {
209 ? std::string(
"meanfield")
212 if (method !=
"dec" && method !=
"avg" && method !=
"meanfield")
214 "' is not a supported environment recombination; use 'meanfield', "
215 "'dec', 'avg' or 'auto'");
216 if (method ==
"meanfield" && backend.empty())
218 "map_env: the mean-field environment coupling integrates each stage over its sojourn, "
219 "so it needs a stage backend that produces transient averages, and this port wires "
220 "'fluid' and 'ctmc' only -- " +
222 " has neither. Use map_env_method='dec' or 'avg', which solve each stage in steady "
223 "state with the calling solver itself");
226 o.
method = method ==
"meanfield" ?
"meanfield" : method;
227 if (method ==
"meanfield") {
243 out.
method = requested_method;
246 const std::size_t M =
sn.nstations, K =
sn.nclasses;
250 out.
XN.assign(K, zero);
251 out.
CN.assign(K, zero);
252 for (std::size_t i = 0; i < M; ++i)
253 for (std::size_t k = 0; k < K; ++k)
255 out.
RN(i, k) = T(out.
QN(i, k) / out.
TN(i, k));
256 for (std::size_t k = 0; k < K; ++k) {
257 const std::size_t ist =
sn.classes[k].refstat;
258 if (ist >= 1 && ist <= M) out.
XN[k] = out.
TN(ist - 1, k);
261 for (std::size_t i = 0; i < M; ++i) q = T(q + out.
QN(i, k));
262 out.
CN[k] = T(q / out.
XN[k]);
271 out.
warning =
"This solver has no native support for the non-renewal (MAP/MMPP) processes of "
272 "this model; the reported averages come from its " +
273 method +
" random-environment approximation (" + std::to_string(info.
nstages) +
275 " image). Set map_env='off' to reject the model instead.";
282 const std::vector<double> nj =
sn.njobs();
283 std::string openzero;
284 for (std::size_t k = 0; k < K; ++k) {
285 if (k < nj.size() && std::isfinite(nj[k]))
continue;
288 for (std::size_t i = 0; i < M; ++i)
290 const std::size_t ist =
sn.classes[k].refstat;
291 if (!any && ist >= 1 && ist <=
sn.rates.rows() && k <
sn.rates.cols()) {
293 any = std::isfinite(lam) && lam > 0.0;
296 if (!openzero.empty()) openzero +=
", ";
297 openzero += std::to_string(k + 1);
299 if (!openzero.empty())
300 out.
warning +=
" The " + method +
301 " environment coupling returned no reference-station throughput for open "
304 ", so their system throughput and system response time are reported as "
305 "zero; use map_env_method='dec' or 'avg' for system-level metrics.";
326template <
class T,
class Run,
class StageFn>
329 StageFn stage_fn,
const std::string& requested_method =
"default") {
UnsupportedError(const std::string &what)
const EnvStage< T > & stage(std::size_t e) const
std::size_t nstages() const
std::vector< mam::Mmap< double > > hold_time
holdTime[e]
const EnvArc< T > & arc(std::size_t e, std::size_t h) const
A subset of the registry: MATLAB's SolverFeatureSet, whose list is a flag per field.
A network plus its refreshed NetworkStruct.
std::vector< NodeDef > nodes
every node, in creation order
The SolverENV entry surface: a port of the analyzer selection that @@SolverENV/SolverENV....
The exception types the port throws.
Port of matlab/src/io/map2renv.m and matlab/src/io/MAPQN2RENV.m (python twin in api/io/converters....
The map_env decision, as a predicate over a feature set.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
std::function< EnvStageAvg< T >(const qn::NetworkStruct< T > &)> EnvStageAvgFn
The stage solver, as a callable: the C++ spelling of the MATLAB function handle SolverENV(renv,...
EnvAnalyzerSolution< T > solver_env(Environment< T > &e, const EnvOptions &o)
SolverENV.init's analyzer selection: solve the environment with the coupling o.method names.
env::Environment< T > map2renv(const qn::NetworkStruct< T > &base, Map2RenvInfo< T > *info=nullptr, std::size_t max_stages=64)
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
Matrix< T > sn_get_residt_from_respt(const qn::NetworkStruct< T > &L, const Matrix< T > &RN)
Port of sn_get_residt_from_respt: the per-JOB residence time.
double max_hold_time(const env::Environment< T > &e)
Longest mean stage sojourn of the environment, the mean-field horizon's scale.
void assert_stage_correspondence(const qn::NetworkStruct< T > &sn, const env::Environment< T > &e)
The base-to-stage index correspondence map_env_approx relies on, asserted rather than assumed.
const char * image_kind(const io::Map2RenvInfo< T > &info)
mapImageKind: an MMPP image preserves the modulating chain exactly; a general MAP image aggregates th...
std::string select_env_limit(const qn::NetworkStruct< T > &sn, const env::Environment< T > &e)
selectEnvLimit: the timescale test between the two closed-form limits.
MapEnvDecision needs_map_env(const qn::FeatureSet &declared, const qn::NetworkStruct< T > &sn, const MapEnvConfig &cfg=MapEnvConfig())
needsMapEnv: does this model need the environment image, and would the image make it solvable?
std::string map_env_stage_backend(const std::string &solver)
Which ENV stage backend a solver's name maps to, or empty when it has none.
mva::AvgResult< T > run_avg(const qn::NetworkStruct< T > &sn, const std::string &solver, const qn::FeatureSet &declared, const MapEnvConfig &cfg, Run run, StageFn stage_fn, const std::string &requested_method="default")
The getAvg funnel: run the model, or its environment image when the ONLY thing in the way is a non-re...
mva::AvgResult< T > map_env_approx(const qn::NetworkStruct< T > &sn, const std::string &solver, const MapEnvConfig &cfg, StageFn stage_fn, const std::string &requested_method="default")
mapEnvApprox: solve the model through the random-environment image of its non-renewal processes.
bool supports_transient_analysis(const std::string &solver)
supportsTransientAnalysis: does this solver produce transient averages?
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
The SolverMVA class surface: @@SolverMVA/runAnalyzer.m and the gates around it.
What the ENV entry reports: the environment-blended metrics, and the whole result of whichever coupli...
Matrix< T > QN
Environment-averaged metrics, (nstations x nclasses).
solvers::CacheMetrics< T > cache
The environment-blended cache surface, whichever coupling produced it.
One arc of the environment process.
lang::Distrib< T > dist
the e -> h transition time
std::string method
The inter-stage coupling: meanfield is the reference's default.
std::string stage_solver
Which solver runs each FLAT stage: the fluid transient or the enumerated CTMC.
double timespan_end
options.timespan(2) of the inner solver: the transient horizon.
The INFO output of map2renv: how the stage set was assembled.
bool is_mmpp
True when every process was an MMPP, so the image is exact in structure.
The metrics getAvg returns, after filtering.
Matrix< T > RN
response time, per visit
std::string warning
The reference's own warning text, verbatim, empty when it did not warn.
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
solvers::CacheMetrics< T > cache
What the cache branches observed, EMPTY on a model with no Cache node and on every solver that does n...
Matrix< T > AN
arrival rate
The caller-facing map_env knobs, options.config.map_env and friends.
std::size_t max_stages
0 = the transform's own default cap
What the gate decided, and what it decided it about.