5#ifndef LINE_SOLVERS_MAM_SOLVER_MAM_RENEGING_H
6#define LINE_SOLVERS_MAM_SOLVER_MAM_RENEGING_H
91 if (!
declares(L, ist, r))
return nullptr;
93 if (r - 1 >= st.
patience.size())
return nullptr;
97 !basic_detail::is_markovian_type(p.
type))
106 for (std::size_t i = 0; i < A.
rows(); ++i)
113 for (std::size_t i = 0; i < B.
rows(); ++i)
114 for (std::size_t j = 0; j < B.
cols(); ++j) A(r0 + i, c0 + j) += scale * B(i, j);
141 while (
map_cdf(pat, std::vector<double>(1, Th))[0] < 1.0 - 1e-4) Th *= 1.25;
142 std::vector<double> BL(Qz);
143 for (std::size_t k = 0; k < Qz; ++k)
144 BL[k] = Th *
static_cast<double>(k) /
static_cast<double>(Qz - 1);
146 std::vector<double> vrep(Qz);
147 for (std::size_t k = 0; k + 1 < Qz; ++k) vrep[k] = (BL[k] + BL[k + 1]) / 2.0;
148 vrep[Qz - 1] = BL[Qz - 1];
149 const std::vector<double> ga =
map_cdf(pat, vrep);
152 const std::size_t m = C.
rows();
153 const std::size_t S =
static_cast<std::size_t
>(s);
154 const std::size_t n = (S + 1) * m;
157 for (std::size_t row = 1; row <= S + 1; ++row) {
158 const std::size_t rs = (row - 1) * m;
159 const double busy =
static_cast<double>(row - 1) * mu;
163 }
else if (row == S + 1) {
175 std::vector<Matrix<double>> Qy;
177 for (std::size_t k = 0; k < Qz; ++k) {
179 const std::size_t rs = (S - 1) * m;
182 add_block(Qk, rs, rs + m, D, 1.0 - ga[k]);
183 const std::size_t rs2 = S * m;
184 add_block(Qk, rs2, rs2, I, -
static_cast<double>(S) * mu);
185 add_block(Qk, rs2, rs2 - m, I,
static_cast<double>(S) * mu);
188 std::vector<double> Rd(n, -1.0);
189 for (std::size_t i = n - m; i < n; ++i) Rd[i] = 1.0;
190 std::vector<std::vector<double>> R(Qz, Rd), Rt(Qz + 1, Rd);
191 std::vector<Matrix<double>> Qt;
194 for (std::size_t k = 0; k < Qz; ++k) Qt.push_back(Qy[k]);
196 std::vector<double> B(Qz);
197 for (std::size_t k = 0; k + 1 < Qz; ++k) B[k] = BL[k + 1];
199 std::vector<double> cdfpts(Qz + 1, 0.0);
200 for (std::size_t k = 0; k < Qz; ++k) cdfpts[k + 1] = B[k];
203 const std::vector<std::vector<double>>& F = res.
cdf;
204 const std::vector<std::vector<double>>& Fm = res.
cdfm;
207 std::vector<double> lmap(m, 0.0);
208 for (std::size_t i = 0; i < m; ++i)
209 for (std::size_t j = 0; j < m; ++j) lmap[i] += D(i, j);
211 const std::vector<double> alpha =
map_pie(pat);
212 const std::size_t np = P0.
rows();
214 for (std::size_t i = 0; i < np; ++i)
215 for (std::size_t j = 0; j < np; ++j) negS(i, j) = -P0(i, j);
216 std::vector<double> meanMin(Qz, 0.0);
217 for (std::size_t k = 0; k < Qz; ++k) {
219 std::vector<double> rhs(np, 0.0);
220 for (std::size_t i = 0; i < np; ++i) {
222 for (std::size_t j = 0; j < np; ++j) rhs[i] -= E(i, j);
224 const std::vector<double> x =
solve(negS, rhs);
225 for (std::size_t i = 0; i < np; ++i) meanMin[k] += alpha[i] * x[i];
227 const std::size_t norm = S * m;
228 double total = 0.0, abandon = 0.0, queued = 0.0;
229 for (std::size_t i = 0; i < n; ++i) total += Fm[0][i] * lmap[i % m];
230 for (std::size_t d = 0; d < Qz; ++d)
231 for (std::size_t i = 0; i < norm; ++i) {
232 const double mass = (F[d + 1][i] - Fm[d][i]) * lmap[i % m];
234 abandon += ga[d] * mass;
235 queued += meanMin[d] * mass;
237 const double abandonProb = abandon / total;
238 const double expectedQueued = queued / total;
248 out.
tput = lambda * (1.0 - abandonProb);
249 out.
util = out.
tput / (mu *
static_cast<double>(S));
250 out.
qlen = out.
tput / mu + lambda * expectedQueued;
266 for (std::size_t i = 1; i <= L.
nstations; ++i)
267 for (std::size_t r = 1; r <= L.
nclasses; ++r)
275 for (std::size_t i = 1; i <= L.
nstations; ++i)
276 for (std::size_t r = 1; r <= L.
nclasses; ++r)
289 for (std::size_t k = 0; k < L.
nclasses; ++k)
290 if (std::isfinite(L.
classes[k].population)) {
291 info.
why =
"MAPMsG requires open queueing model.";
295 info.
why =
"MAPMsG currently supports single class only.";
299 for (std::size_t i = 1; i <= L.
nstations; ++i) {
301 if (nt == qn::NodeType::Source) {
303 }
else if (nt == qn::NodeType::Queue) {
308 info.
why =
"MAPMsG requires single queue station.";
314 info.
why =
"No Source node found.";
318 info.
why =
"No Queue node found.";
324 info.
why =
"No patience/impatience configuration found.";
328 info.
why =
"Queue does not have reneging configured.";
333 const bool named = r - 1 < st.
patience.size() && !st.
patience[r - 1].disabled &&
335 info.
why = named ? std::string(
"The patience of this class is ") +
337 ", not a Markovian law (the patience must be a Markovian law: "
338 "Exp, Erlang, HyperExp, Coxian, APH or PH)."
339 : std::string(
"No patience distribution for this class (the patience must be a "
340 "Markovian law: Exp, Erlang, HyperExp, Coxian, APH or PH).");
344 info.
why =
"MAPMsG requires FCFS scheduling.";
349 info.
why =
"Invalid service process.";
352 if (!svc.
has_map() || svc.
D0.rows() != 1) {
353 info.
why =
"MAPMsG requires exponential service (single-phase).";
358 !basic_detail::is_markovian_type(arv.
type)) {
359 info.
why =
"Invalid arrival process.";
362 const double ns = L.
stations[q - 1].nservers;
363 if (!std::isfinite(ns) || ns < 1.0) {
364 info.
why =
"MAPMsG requires a finite, positive server count.";
367 info.
nservers =
static_cast<int>(ns);
383 return "SolverMAM: this model declares a reneging patience outside the MAP/M/s+G shape "
384 "(one Source, one FCFS Queue with exponential service, one open class) that the "
385 "default method solves through the reneging analyzer; dec.source would ignore the "
386 "patience. " + info.
why +
" Use SolverCTMC, SolverSSA, SolverJMT or SolverLDES.";
388 return "SolverMAM: this model combines setup/delay-off times with a reneging topology; only "
389 "the dec.source decomposition honours setup times, and the default method routes "
390 "such a topology to an analyzer that does not read them. Use SolverJMT or SolverLDES.";
391 return std::string();
406 "SolverMAM: not a MAP/M/s+G reneging queue: " + info.
why);
407 if (
opt.mapmsg_quantization < 2)
408 throw InputError(
"SolverMAM: options.config.mapmsg_quantization must be at least 2, got " +
409 std::to_string(
opt.mapmsg_quantization) +
410 "; one regime leaves no grid between 0 and the patience horizon");
411 if constexpr (!std::is_same<T, double>::value) {
413 "SolverMAM: the MAP/M/s+G reneging analyzer solves a multi-regime fluid queue "
414 "through mfq_multiregime, whose ordered real Schur basis is double only; use --arith "
421 const double mu = -svc.
D0(0, 0);
424 opt.mapmsg_quantization);
432 out.
C.assign(K, 0.0);
433 out.
X.assign(K, 0.0);
434 out.
Q(q - 1, r - 1) = met.
qlen;
435 out.
U(q - 1, r - 1) = met.
util;
436 out.
R(q - 1, r - 1) = met.
respt;
437 out.
Tp(q - 1, r - 1) = met.
tput;
438 out.
X[r - 1] = met.
tput;
441 out.
iter =
static_cast<int>(
opt.mapmsg_quantization);
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::map< std::size_t, SetupDelayOffParam< T > > setupparam
Setup / delay-off, keyed by 1-based STATION index.
std::vector< std::vector< bool > > disabled
std::vector< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LU factorization with partial pivoting, templated on the number type.
The option and result types SolverMAM shares with its analyzers.
Cumulative distribution of the inter-arrival time of a MAP.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
Multi-regime FEEDBACK Markovian fluid queue: density, density derivative and distribution of the flui...
const char * process_to_text(ProcessType p)
The MATLAB ProcessType name, as sn.procid prints it.
NodeType
Node kinds, with the values of MATLAB NodeType.
RenegingMetrics solve(const Matrix< double > &C, const Matrix< double > &D, double mu, int s, const Matrix< double > &P0, const Matrix< double > &P1, std::size_t Qz)
solveReneging + convertPatienceToRegimes of solver_mam_retrial.m, as fixed in 2c0848d76f (stationary ...
const lang::Distrib< T > * markovian_patience(const qn::NetworkStruct< T > &L, std::size_t ist, std::size_t r)
The patience law at (ist, r) when it is a usable (D0, D1) pair; null otherwise.
void add_block(Matrix< double > &A, std::size_t r0, std::size_t c0, const Matrix< double > &B, double scale)
Matrix< double > to_double(const Matrix< T > &A)
Matrix<T> to Matrix<double>.
bool declares(const qn::NetworkStruct< T > &L, std::size_t ist, std::size_t r)
Does station ist (1-based) declare RENEGING for class r (1-based)?
MultiRegimeResult mfq_multiregime(const std::vector< Matrix< double > > &Q, const std::vector< std::vector< double > > &R, const std::vector< Matrix< double > > &Qt, const std::vector< std::vector< double > > &Rt, const std::vector< double > &Thr, const std::vector< double > &pdfpoints, const std::vector< double > &cdfpoints)
Multi-regime feedback fluid queue.
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?
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
bool mam_has_reneging_patience(const qn::NetworkStruct< T > &L)
mam_has_reneging_patience.m: RENEGING together with a usable patience law.
std::vector< T > map_cdf(const Map< T > &m, const std::vector< T > &points)
Cumulative distribution of the inter-arrival time at the given points.
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
std::string mam_reneging_default_refusal(const qn::NetworkStruct< T > &L)
The sentence supportsModelMethod('default') refuses a declared reneging with when the model is not th...
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
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.
Conservation laws of a layered queueing network, enumerated from its structure.
Matrix< T > eye(std::size_t n)
Identity of order n.
Matrix< T > expm(const Matrix< T > &A)
Matrix exponential exp(A).
A queueing network and its refreshed NetworkStruct.
Port of solver_mam_basic.m, the dec.source analyzer and the default algorithm of SolverMAM.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
bool has_map() const
True when the type carries a (D0,D1) pair of its own.
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
The options SolverMAM reads.
What mam_reneging_applicable.m returns: the station it found, or why it found none.
std::size_t cls
1-based class index; the reference is single-class
std::size_t source
1-based station index of the Source
std::string why
the reference's errorMsg, empty when ok
std::size_t station
1-based station index of the reneging queue
A MAP as the pair of matrices (D0, D1).
Return value of mfq_multiregime: one row of N per-state values per point.
std::vector< std::vector< double > > cdf
P(X < p).
std::vector< std::vector< double > > cdfm
P(X <= p).
The station metrics of the MAP/M/s+G queue.
Class-level results, the [Q,U,R,T,C,X] of the MATLAB analyzers.
One station of the network.
std::vector< Distrib< T > > patience
Queue.setPatience(class, dist): the abandonment timer of a WAITING job, with impatience[r] naming whi...
std::vector< lang::ImpatienceType > impatience