123 "solver_mam_ldqbd: the level-dependent QBD recursion inverts a matrix per level and "
124 "falls back to a pseudo-inverse (singular vectors) when a level is singular, neither "
125 "of which is exact arithmetic; rerun with --arith double or --arith real");
132 throw UnsupportedError(
"solver_mam_ldqbd: the LDQBD method requires a single-class model");
134 std::size_t nDelay = 0, nQueue = 0, nSource = 0;
135 std::size_t delayIdx = 0, queueIdx = 0, srcIdx = 0;
136 for (std::size_t i = 1; i <= M; ++i) {
138 if (s == SchedStrategy::INF) { ++nDelay; delayIdx = i; }
139 else if (s == SchedStrategy::FCFS) { ++nQueue; queueIdx = i; }
140 else if (s == SchedStrategy::EXT) { ++nSource; srcIdx = i; }
142 const bool isOpen = std::isinf(L.
classes[0].population);
144 if (nSource != 1 || nQueue != 1 || M != 2)
146 "solver_mam_ldqbd: the open LDQBD method requires exactly one Source and one "
149 if (nDelay != 1 || nQueue != 1 || M != 2)
151 "solver_mam_ldqbd: the closed LDQBD method requires exactly one Delay and one "
157 const double nServers = L.
stations[queueIdx - 1].nservers;
158 const std::size_t nPhases = PHq.
D0.rows();
159 const bool isPH = nPhases > 1;
160 T mu = zero, mean_service = zero;
161 std::vector<T> alpha;
163 mu = T(-PHq.
D0(0, 0));
165 throw InputError(
"solver_mam_ldqbd: the queue has a non-positive service rate");
166 mean_service = T(one / mu);
176 bool hasSetup =
false;
177 T alpharate = zero, alphascv = one, betarate = zero, betascv = one;
179 const typename std::map<std::size_t, qn::SetupDelayOffParam<T>>::const_iterator sit =
183 if (sit->second.last(su, doff) && !doff.
disabled) {
186 "solver_mam_ldqbd: open LDQBD does not model a setup/delay-off server; "
187 "use method 'dec.source', whose qbd_setupdelayoff covers the open case");
188 if (isPH || nServers > 1)
190 "solver_mam_ldqbd: closed LDQBD models a setup/delay-off server with "
191 "exponential service at a single server only; this station has "
192 "phase-type service or several servers");
194 alpharate = T(one / su.
mean);
196 betarate = T(one / doff.
mean);
204 const std::vector<T>& lld = L.
stations[queueIdx - 1].lldscaling;
206 for (
const T& v : lld)
207 if (v != one) hasLLD =
true;
213 double utilPeak = nServers;
218 const std::size_t Kc = K;
219 auto rt_at = [&](std::size_t from, std::size_t to) -> T {
222 if (a >= L.
rt.rows() || b >= L.
rt.cols())
return zero;
226 std::size_t Nlev = 0;
227 T lambda_eff = zero, delayRate = zero;
228 std::vector<T> arrRate;
231 if (arv.
D0.rows() > 1)
233 "solver_mam_ldqbd: the open LDQBD method currently supports Poisson (exponential) "
234 "arrivals only; the Source uses a MAP/MMPP process");
235 const T lambda = L.
rates(srcIdx - 1, 0);
236 lambda_eff = T(lambda * rt_at(srcIdx, queueIdx));
241 "solver_mam_ldqbd: the open LDQBD method requires a stable queue (rho = " +
242 std::to_string(rho) +
243 " >= 1). Increase service capacity or reduce the arrival rate");
244 const std::size_t c =
static_cast<std::size_t
>(
245 std::isfinite(nServers) ? std::llround(nServers) : 1);
246 if (
opt.cutoff > 0) {
247 Nlev = std::max(c + 1,
opt.cutoff);
249 const double tailTol = 1e-10;
251 static_cast<long>(c) +
static_cast<long>(std::ceil(std::log(tailTol) / std::log(rho)));
252 Nlev =
static_cast<std::size_t
>(
253 std::min<long>(std::max<long>(lv,
static_cast<long>(c) + 10), 100000));
255 arrRate.assign(Nlev + 1, lambda_eff);
256 arrRate[Nlev] = zero;
258 delayRate = L.
rates(delayIdx - 1, 0);
259 lambda_eff = T(delayRate * rt_at(delayIdx, queueIdx));
260 const double Nd = L.
classes[0].population;
261 Nlev =
static_cast<std::size_t
>(std::llround(Nd));
262 arrRate.assign(Nlev + 1, zero);
265 for (std::size_t n = 0; n <= Nlev; ++n)
270 "solver_mam_ldqbd: the model has no levels to solve (a zero population)");
272 std::vector<T> sf(Nlev + 1, zero);
273 for (std::size_t n = 1; n <= Nlev; ++n) {
275 sf[n] = lld[std::min(n, lld.size()) - 1];
278 std::min(
static_cast<double>(n), std::isfinite(nServers) ? nServers : 1.0));
285 std::vector<Matrix<T>> Q0(Nlev), Q1(Nlev + 1), Q2(Nlev + 1);
287 for (std::size_t n = 0; n + 1 <= Nlev; ++n) Q0[n] =
Matrix<T>(1, 1, arrRate[n]);
288 for (std::size_t n = 0; n <= Nlev; ++n) {
289 const T dep = (n > 0) ? T(sf[n] * mu) : zero;
290 Q1[n] =
Matrix<T>(1, 1, T(-(arrRate[n] + dep)));
293 for (std::size_t n = 1; n <= Nlev; ++n) Q2[n] = Matrix<T>(1, 1, T(sf[n] * mu));
300 std::vector<T> sf1(Nlev, zero);
301 for (std::size_t n = 1; n <= Nlev; ++n) sf1[n - 1] = sf[n];
304 !basic_detail::is_renewal_map(PHq));
311 T mean_queue = zero, x_setup = zero;
322 alpharate, alphascv, betarate, betascv);
328 if (p.size() != Nlev + 1)
329 throw NumericError(
"solver_mam_ldqbd: the LD-QBD solve returned the wrong level count");
331 for (std::size_t n = 0; n <= Nlev; ++n)
355 for (std::size_t n = 1; n <= Nlev; ++n)
util += T(sf[n] / peak * p[n]);
370 const T X = T(lambda_eff * T(one - p[Nlev]));
371 const T Rq = (X > zero) ? T(mean_queue / X) : zero;
372 s.
Tp(srcIdx - 1, 0) = X;
373 s.
Q(queueIdx - 1, 0) = mean_queue;
374 s.
U(queueIdx - 1, 0) =
util;
375 s.
R(queueIdx - 1, 0) = Rq;
376 s.
Tp(queueIdx - 1, 0) = X;
381 const T X = T(mean_delay * lambda_eff);
382 const T Rq = (X > zero) ? T(mean_queue / X) : zero;
383 const T Rd = T(one / delayRate);
387 s.
Q(delayIdx - 1, 0) = mean_delay;
388 s.
U(delayIdx - 1, 0) = mean_delay;
389 s.
R(delayIdx - 1, 0) = Rd;
390 s.
Tp(delayIdx - 1, 0) = T(mean_delay * delayRate);
391 s.
Q(queueIdx - 1, 0) = mean_queue;
392 s.
U(queueIdx - 1, 0) =
util;
393 s.
R(queueIdx - 1, 0) = Rq;
394 s.
Tp(queueIdx - 1, 0) = X;
408 b.
refIdx = isOpen ? srcIdx : delayIdx;
422 b.
N = isOpen ? std::numeric_limits<double>::infinity() : L.
classes[0].population;