122 "solver_mam_ldqbd: the level-dependent QBD recursion inverts a matrix per level and "
123 "falls back to a pseudo-inverse (singular vectors) when a level is singular, neither "
124 "of which is exact arithmetic; rerun with --arith double or --arith real");
131 throw UnsupportedError(
"solver_mam_ldqbd: the LDQBD method requires a single-class model");
133 std::size_t nDelay = 0, nQueue = 0, nSource = 0;
134 std::size_t delayIdx = 0, queueIdx = 0, srcIdx = 0;
135 for (std::size_t i = 1; i <= M; ++i) {
137 if (s == SchedStrategy::INF) { ++nDelay; delayIdx = i; }
138 else if (s == SchedStrategy::FCFS) { ++nQueue; queueIdx = i; }
139 else if (s == SchedStrategy::EXT) { ++nSource; srcIdx = i; }
141 const bool isOpen = std::isinf(L.
classes[0].population);
143 if (nSource != 1 || nQueue != 1 || M != 2)
145 "solver_mam_ldqbd: the open LDQBD method requires exactly one Source and one "
148 if (nDelay != 1 || nQueue != 1 || M != 2)
150 "solver_mam_ldqbd: the closed LDQBD method requires exactly one Delay and one "
156 const double nServers = L.
stations[queueIdx - 1].nservers;
157 const std::size_t nPhases = PHq.
D0.rows();
158 const bool isPH = nPhases > 1;
159 T mu = zero, mean_service = zero;
160 std::vector<T> alpha;
162 mu = T(-PHq.
D0(0, 0));
164 throw InputError(
"solver_mam_ldqbd: the queue has a non-positive service rate");
165 mean_service = T(one / mu);
175 bool hasSetup =
false;
176 T alpharate = zero, alphascv = one, betarate = zero, betascv = one;
178 const typename std::map<std::size_t, qn::SetupDelayOffParam<T>>::const_iterator sit =
182 if (sit->second.last(su, doff) && !doff.
disabled) {
185 "solver_mam_ldqbd: open LDQBD does not model a setup/delay-off server; "
186 "use method 'dec.source', whose qbd_setupdelayoff covers the open case");
187 if (isPH || nServers > 1)
189 "solver_mam_ldqbd: closed LDQBD models a setup/delay-off server with "
190 "exponential service at a single server only; this station has "
191 "phase-type service or several servers");
193 alpharate = T(one / su.
mean);
195 betarate = T(one / doff.
mean);
203 const std::vector<T>& lld = L.
stations[queueIdx - 1].lldscaling;
205 for (
const T& v : lld)
206 if (v != one) hasLLD =
true;
212 double utilPeak = nServers;
217 const std::size_t Kc = K;
218 auto rt_at = [&](std::size_t from, std::size_t to) -> T {
221 if (a >= L.
rt.rows() || b >= L.
rt.cols())
return zero;
225 std::size_t Nlev = 0;
226 T lambda_eff = zero, delayRate = zero;
227 std::vector<T> arrRate;
230 if (arv.
D0.rows() > 1)
232 "solver_mam_ldqbd: the open LDQBD method currently supports Poisson (exponential) "
233 "arrivals only; the Source uses a MAP/MMPP process");
234 const T lambda = L.
rates(srcIdx - 1, 0);
235 lambda_eff = T(lambda * rt_at(srcIdx, queueIdx));
240 "solver_mam_ldqbd: the open LDQBD method requires a stable queue (rho = " +
241 std::to_string(rho) +
242 " >= 1). Increase service capacity or reduce the arrival rate");
243 const std::size_t c =
static_cast<std::size_t
>(
244 std::isfinite(nServers) ? std::llround(nServers) : 1);
245 if (
opt.cutoff > 0) {
246 Nlev = std::max(c + 1,
opt.cutoff);
248 const double tailTol = 1e-10;
250 static_cast<long>(c) +
static_cast<long>(std::ceil(std::log(tailTol) / std::log(rho)));
251 Nlev =
static_cast<std::size_t
>(
252 std::min<long>(std::max<long>(lv,
static_cast<long>(c) + 10), 100000));
254 arrRate.assign(Nlev + 1, lambda_eff);
255 arrRate[Nlev] = zero;
257 delayRate = L.
rates(delayIdx - 1, 0);
258 lambda_eff = T(delayRate * rt_at(delayIdx, queueIdx));
259 const double Nd = L.
classes[0].population;
260 Nlev =
static_cast<std::size_t
>(std::llround(Nd));
261 arrRate.assign(Nlev + 1, zero);
264 for (std::size_t n = 0; n <= Nlev; ++n)
269 "solver_mam_ldqbd: the model has no levels to solve (a zero population)");
271 std::vector<T> sf(Nlev + 1, zero);
272 for (std::size_t n = 1; n <= Nlev; ++n) {
274 sf[n] = lld[std::min(n, lld.size()) - 1];
277 std::min(
static_cast<double>(n), std::isfinite(nServers) ? nServers : 1.0));
284 std::vector<Matrix<T>> Q0(Nlev), Q1(Nlev + 1), Q2(Nlev + 1);
286 for (std::size_t n = 0; n + 1 <= Nlev; ++n) Q0[n] =
Matrix<T>(1, 1, arrRate[n]);
287 for (std::size_t n = 0; n <= Nlev; ++n) {
288 const T dep = (n > 0) ? T(sf[n] * mu) : zero;
289 Q1[n] =
Matrix<T>(1, 1, T(-(arrRate[n] + dep)));
292 for (std::size_t n = 1; n <= Nlev; ++n) Q2[n] = Matrix<T>(1, 1, T(sf[n] * mu));
299 std::vector<T> sf1(Nlev, zero);
300 for (std::size_t n = 1; n <= Nlev; ++n) sf1[n - 1] = sf[n];
309 T mean_queue = zero, x_setup = zero;
320 alpharate, alphascv, betarate, betascv);
326 if (p.size() != Nlev + 1)
327 throw NumericError(
"solver_mam_ldqbd: the LD-QBD solve returned the wrong level count");
329 for (std::size_t n = 0; n <= Nlev; ++n)
353 for (std::size_t n = 1; n <= Nlev; ++n)
util += T(sf[n] / peak * p[n]);
368 const T X = T(lambda_eff * T(one - p[Nlev]));
369 const T Rq = (X > zero) ? T(mean_queue / X) : zero;
370 s.
Tp(srcIdx - 1, 0) = X;
371 s.
Q(queueIdx - 1, 0) = mean_queue;
372 s.
U(queueIdx - 1, 0) =
util;
373 s.
R(queueIdx - 1, 0) = Rq;
374 s.
Tp(queueIdx - 1, 0) = X;
379 const T X = T(mean_delay * lambda_eff);
380 const T Rq = (X > zero) ? T(mean_queue / X) : zero;
381 const T Rd = T(one / delayRate);
385 s.
Q(delayIdx - 1, 0) = mean_delay;
386 s.
U(delayIdx - 1, 0) = mean_delay;
387 s.
R(delayIdx - 1, 0) = Rd;
388 s.
Tp(delayIdx - 1, 0) = T(mean_delay * delayRate);
389 s.
Q(queueIdx - 1, 0) = mean_queue;
390 s.
U(queueIdx - 1, 0) =
util;
391 s.
R(queueIdx - 1, 0) = Rq;
392 s.
Tp(queueIdx - 1, 0) = X;
406 b.
refIdx = isOpen ? srcIdx : delayIdx;
420 b.
N = isOpen ? std::numeric_limits<double>::infinity() : L.
classes[0].population;