5#ifndef LINE_SOLVERS_MAM_SOLVER_MAM_RETRIAL_H
6#define LINE_SOLVERS_MAM_SOLVER_MAM_RETRIAL_H
162namespace retrial_detail {
167 const typename std::map<std::size_t, qn::RetrialParam<T> >::const_iterator it =
170 for (std::size_t r = 0; r < it->second.retrial_proc.size(); ++r)
171 if (!it->second.retrial_proc[r].disabled)
return true;
188 "' is disabled; the matrix-analytic retrial solver has no state for "
189 "a class that does not visit its own station");
190 if (!basic_detail::is_markovian_type(d.
type))
192 std::string(
"SolverMAM: the ") + role +
" process of class '" + L.
classes[r - 1].name +
194 ", which has no Markovian (D0,D1) representation. The reference replaces it by an "
195 "Erlang of matching mean and squared coefficient of variation and warns that the "
196 "answer is approximate; this port refuses instead, because the substitute is a "
197 "different model under the same method name. Use SolverCTMC, SolverSSA or SolverLDES");
199 if (!basic_detail::is_markovian_map(m))
200 throw UnsupportedError(ErrorKind{
"Model"}, std::string(
"SolverMAM: the ") + role +
" process of class '" +
201 L.
classes[r - 1].name +
"' at station '" +
203 "' has a (D0,D1) pair that is not a Markovian generator (a negative "
204 "off-diagonal, a negative arrival entry, or rows that do not sum to "
217inline double retrial_block_width(std::size_t V, std::size_t M,
int N) {
218 double d = 0.0, term = 1.0;
219 for (
int n = 0; n <= N; ++n) {
220 if (n > 0) term *=
static_cast<double>(n +
static_cast<int>(M) - 1) /
static_cast<double>(n);
222 if (d > 1e12)
return 1e12;
224 return static_cast<double>(V) * d;
232double orbit_trunc_error(
const qsys::BmapPhNnRetrialResult<T>& r) {
233 const double lorbit = num_traits<T>::to_double(r.L_orbit);
234 const double top = num_traits<T>::to_double(r.topLevelMass);
235 const double denom = lorbit > std::numeric_limits<double>::min()
237 : std::numeric_limits<double>::min();
238 return static_cast<double>(r.truncLevel) * top / denom;
258 info.
why =
"the BMAP/PH/N/N retrial solver requires an open queueing model";
262 info.
why =
"the BMAP/PH/N/N retrial solver supports a single class only";
270 std::vector<std::size_t> bufferless;
271 for (std::size_t i = 1; i <= L.
nstations; ++i) {
272 if (L.
stations[i - 1].nodetype != qn::NodeType::Queue)
continue;
273 const double cap = L.
cap[i - 1];
274 const bool isbufferless = std::isfinite(cap) && cap == L.
stations[i - 1].nservers;
275 if (retrial_detail::has_retrial_proc(L, i) || isbufferless) bufferless.push_back(i);
277 if (bufferless.empty()) {
278 info.
why =
"no retrial queue found (declare one with setRetrial, or give a station a "
279 "capacity equal to its server count)";
283 for (std::size_t i : bufferless) {
284 for (std::size_t r = 0; r < L.
nclasses; ++r)
285 if (L.
droprule[i - 1][r] == DropStrategy::RETRIAL ||
286 L.
droprule[i - 1][r] == DropStrategy::RETRIAL_WITH_LIMIT) {
293 info.
why =
"no RETRIAL drop strategy is configured on a bufferless queue";
300 typename std::map<std::size_t, qn::RetrialParam<T> >::const_iterator rp =
302 const std::size_t c = info.
cls - 1;
303 if (rp != L.
retrialparam.end() && c < rp->second.retrial_proc.size() &&
304 !rp->second.retrial_proc[c].disabled) {
307 info.
why =
"the BMAP/PH/N/N retrial solver requires an exponential retrial delay; "
308 "use SolverLDES for a non-exponential one";
311 if (c < rp->second.max_attempts.size() && rp->second.max_attempts[c] > 0) {
313 info.
why =
"the BMAP/PH/N/N retrial solver supports only unlimited retrial attempts; "
314 "use SolverLDES for a finite cap";
319 if (c < rst.orbit_impatience.size() && !rst.orbit_impatience[c].disabled &&
322 info.
why =
"the BMAP/PH/N/N retrial solver requires an exponential orbit impatience; "
323 "use SolverLDES for a non-exponential one";
328 for (std::size_t i = 1; i <= L.
nstations; ++i)
329 if (L.
stations[i - 1].nodetype == qn::NodeType::Source) {
334 info.
why =
"no Source node found";
339 if (!std::isfinite(ns) || ns < 1.0) {
340 info.
why =
"the retrial station must have a finite, positive server count";
343 info.
nservers =
static_cast<int>(ns);
350 info.
why =
"Invalid arrival process at source.";
355 info.
why =
"Invalid service process at queue.";
362 info.
R =
static_cast<long>(info.
nservers) - 1;
363 for (std::size_t f = 0; f < L.
regions.size(); ++f) {
364 const std::vector<std::vector<double> >& cap = L.
regions[f].cap;
365 if (cap.size() < info.
station)
continue;
366 const std::vector<double>& row = cap[info.
station - 1];
367 if (row.empty())
continue;
368 const double rfcr = row[row.size() - 1];
370 if (rfcr <=
static_cast<double>(info.
nservers) - 1.0)
371 info.
R =
static_cast<long>(rfcr);
397 if (info.
ok)
return std::string();
402 if (ren.
ok)
return std::string();
404 return "SolverMAM: the 'retrial' method needs a BMAP/PH/N/N bufferless retrial topology "
405 "or a MAP/M/s+G reneging shape (one Source, one FCFS Queue with exponential "
406 "service, one open class, a patience distribution); this model has neither. " +
407 ren.
why +
" Use method 'default'";
408 return "SolverMAM: the 'retrial' method needs a BMAP/PH/N/N bufferless retrial topology; "
409 "this model declares no orbit (" + info.
why +
"). Use method 'default'";
435 "SolverMAM: no valid impatience configuration was detected (retrial or reneging): " +
440 retrial_detail::assert_markovian(L, src, r,
"arrival");
441 retrial_detail::assert_markovian(L, q, r,
"service");
445 if (L.
droprule[q - 1][r - 1] == DropStrategy::RETRIAL_WITH_LIMIT)
447 "SolverMAM: station '" + L.
stations[q - 1].name +
448 "' declares DropStrategy.RETRIAL_WITH_LIMIT. The BMAP/PH/N/N generator indexes its "
449 "levels by the orbit population alone and has no per-job attempt counter, so a job "
450 "about to give up cannot be told from a persistent one. Use SolverCTMC or SolverSSA");
458 std::vector<Matrix<T> > D;
465 const std::vector<T> beta =
map_pie(svc);
474 const typename std::map<std::size_t, qn::RetrialParam<T> >::const_iterator rit =
480 "SolverMAM: station '" + L.
stations[q - 1].name +
"' bounds the retrial count at " +
482 " attempts. The BMAP/PH/N/N generator has no per-job attempt counter in its state "
483 "descriptor. Use SolverCTMC or SolverSSA");
486 if (ret.
order() != 1)
488 "SolverMAM: the retrial process of class '" + L.
classes[r - 1].name +
489 "' at station '" + L.
stations[q - 1].name +
"' has " +
490 std::to_string(ret.
order()) +
491 " phases. The generator retries each orbiting job at a single scalar rate, so "
492 "only an exponential inter-retry time can be represented; the reference reads "
493 "-D0(1,1) regardless, which is the exit rate of the first phase and not the "
494 "retry rate of the process");
495 alpha = T(zero - ret.
D0(0, 0));
501 throw InputError(
"SolverMAM: the retrial rate at station '" + L.
stations[q - 1].name +
502 "' must be positive; a zero rate means an orbiting job never retries and "
503 "the orbit is not positive recurrent at any load");
505 const T gamma = zero;
509 const double blockw =
510 retrial_detail::retrial_block_width(arv.order(), svc.
order(), info.
nservers);
511 if (blockw > cfg.max_block)
513 "SolverMAM: the per-level block of the retrial generator at station '" +
514 L.
stations[q - 1].name +
"' is " + std::to_string(
static_cast<long long>(blockw)) +
515 " states wide, past the " + std::to_string(
static_cast<long long>(cfg.max_block)) +
516 "-state cap: the service has " + std::to_string(svc.
order()) +
517 " phases and the station " + std::to_string(info.
nservers) +
518 " servers, and the busy servers' phase multisets grow as C(n + phases - 1, phases - 1). "
519 "Fit the service with fewer phases, reduce the server count, or raise "
520 "MamRetrialConfig::max_block if the memory cost is acceptable");
522 const std::vector<long> Rv(1, info.
R);
526 qopt.
maxDim = cfg.max_dim;
531 if (cfg.orbit_maxlevel == 0) {
535 while (retrial_detail::orbit_trunc_error(res) > cfg.orbit_tailtol) {
537 if (
static_cast<double>(next + 1) * blockw > cfg.max_dim)
539 "SolverMAM: the orbit truncation of station '" + L.
stations[q - 1].name +
540 "' did not reach the requested accuracy: the tail still contributes " +
541 std::to_string(retrial_detail::orbit_trunc_error(res)) +
" of the mean orbit "
542 "length at level " + std::to_string(res.
truncLevel) +
543 ", against a target of " + std::to_string(cfg.orbit_tailtol) +
544 ", and doubling would pass the dimension cap. The orbit measures would be "
545 "underestimated, which the reference reports as a warning and this port has no "
546 "channel for. Raise MamRetrialConfig::max_dim, or set "
547 "MamRetrialConfig::orbit_maxlevel to accept a fixed truncation");
560 out.
C.assign(K, zero);
561 out.
X.assign(K, zero);
568 out.
R(q - 1, r - 1) = T(out.
Q(q - 1, r - 1) / res.
throughput);
575 out.
C[r - 1] = out.
R(q - 1, r - 1);
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
std::vector< std::vector< bool > > disabled
std::map< std::size_t, RetrialParam< T > > retrialparam
Retrial parameters, keyed by 1-based STATION index.
std::vector< double > cap
sn.cap and sn.classcap: the total and per-class buffers.
std::vector< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
bool is_open_model() const
sn_is_open_model: EVERY class is open, which is not has_open_classes.
std::vector< std::vector< DropStrategy > > droprule
std::vector< Region > regions
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
The option and result types SolverMAM shares with its analyzers.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
mam::Map< T > dist_to_map(const Distrib< T > &d)
The (D0,D1) pair that reaches sn.proc.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
const char * process_to_text(ProcessType p)
The MATLAB ProcessType name, as sn.procid prints it.
bool mam_declares_reneging(const qn::NetworkStruct< T > &L)
True when some station-class pair declares RENEGING, with or without a patience law this analyzer can...
MamRenegingInfo mam_reneging_detect(const qn::NetworkStruct< T > &L)
Port of mam_reneging_applicable.m: is this the MAP/M/s+G shape?
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
MamRetrialInfo mam_retrial_detect(const qn::NetworkStruct< T > &L)
Port of qsys_is_retrial.m, plus the reneging gate of solver_mam_retrial.m.
mva::MvaSolution< T > solver_mam_retrial(const qn::NetworkStruct< T > &L, const MamOptions &opt, const MamRetrialConfig &cfg=MamRetrialConfig())
Port of solver_mam_retrial.m.
mva::MvaSolution< T > solver_mam_reneging(const qn::NetworkStruct< T > &L, const MamOptions &opt, const MamRenegingInfo &info)
The reneging route of solver_mam_retrial.m.
std::string mam_retrial_refusal(const qn::NetworkStruct< T > &L)
The 'retrial' method's applicability as one sentence; empty when applicable.
BmapPhNnRetrialResult< T > qsys_bmapphnn_retrial(const std::vector< Matrix< T > > &D, const std::vector< T > &beta, const Matrix< T > &S, int N, const T &alpha, const T &gamma, const T &p, const std::vector< long > &R, const BmapPhNnRetrialOptions &opt)
The BMAP/PH/N/N bufferless retrial queue.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
The BMAP/PH/N/N bufferless retrial queue with flexible retrial admission control.
Port of solver_mam_basic.m, the dec.source analyzer and the default algorithm of SolverMAM.
The reneging route of solver_mam_retrial.m: the MAP/M/s+G queue whose waiting jobs abandon after a ge...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
The options SolverMAM reads.
What mam_reneging_applicable.m returns: the station it found, or why it found none.
std::string why
the reference's errorMsg, empty when ok
The knobs solver_mam_retrial.m reads from options.config and from model fields the C++ NetworkStruct ...
double max_dim
'MaxDim': the largest generator dimension the refinement may reach.
double orbit_tailtol
options.config.orbit_tailtol, the relative orbit-truncation target.
std::size_t orbit_maxlevel
options.config.orbit_maxlevel; 0 asks for the adaptive refinement.
double max_block
'MaxBlockSize': the largest per-level block V*d that may be assembled.
What qsys_is_retrial.m returns: the station it found, or why it found none.
std::string why
the reference's errorMsg, empty when ok
std::size_t station
1-based station index of the retrial queue
double alpha
retInfo.alpha: the reference's placeholder, overridden by the solver
std::size_t cls
1-based class index; the reference is single-class
long R
admission threshold, N-1 unless an FCR lowers it
double p
retInfo.p: batch rejection probability default
double gamma
retInfo.gamma: orbit impatience default
std::size_t source
1-based station index of the Source
A MAP as the pair of matrices (D0, D1).
std::size_t order() const
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
The parameters of a retrial station: MATLAB sn.retrialProc and friends.
std::vector< int > max_attempts
0 = unbounded
std::vector< T > retrial_rate
mu_r, the per-class orbit retry rate
std::vector< lang::Distrib< T > > retrial_proc
retrial_proc[r] is the class-r retrial process; empty = not a retrial class.
Options of qsys_bmapphnn_retrial (the name-value pairs of the .m file).
std::size_t maxLevel
Fixed orbit truncation level ('MaxLevel'); 0 selects the reference's adaptive choice,...
double maxDim
'MaxDim': cap on the total generator dimension of the refinement.
double tailTolerance
'TailTolerance': relative orbit-truncation error target.
double maxBlockSize
'MaxBlockSize': cap on the per-level block size V*d; exceeding it is an error.
Result of qsys_bmapphnn_retrial.
T L_orbit
mean number of customers in orbit
std::size_t truncLevel
truncation level used
T utilization
N_server / N.
T throughput
N_server / b1.
T N_server
mean number of busy servers