113 const std::size_t M =
sn.nstations, R =
sn.nclasses;
115 bool anyOpen =
false, anyClosed =
false;
117 (std::isinf(c.
population) ? anyOpen : anyClosed) =
true;
118 const bool isopen = anyOpen && !anyClosed;
119 const bool isclosed = anyClosed && !anyOpen;
120 const bool ismixed = anyOpen && anyClosed;
126 case qn::NodeType::Queue:
127 case qn::NodeType::Delay:
129 case qn::NodeType::Source:
130 case qn::NodeType::Sink:
132 out.
reason =
"MEM supports only Queue and Delay nodes in closed models.";
137 out.
reason =
"MEM supports only Source, Queue, Delay and Sink nodes.";
142 if (
sn.has_class_switching()) {
143 out.
reason =
"MEM does not support class switching.";
149 for (std::size_t ist = 1; ist <= M; ++ist) {
150 const std::size_t nd =
sn.node_of_station(ist);
151 for (std::size_t r = 0; r < R; ++r)
155 "MEM does not support absorbing self-loop routing (reducible network).";
161 for (std::size_t i = 0; i < M; ++i) {
163 if (s != qn::SchedStrategy::EXT && s != qn::SchedStrategy::INF &&
164 s != qn::SchedStrategy::FCFS && s != qn::SchedStrategy::PS &&
165 s != qn::SchedStrategy::SIRO && s != qn::SchedStrategy::LCFS &&
166 s != qn::SchedStrategy::LCFSPR) {
167 out.
reason = std::string(
"MEM does not support the ") + detail::sched_text(s) +
168 " scheduling strategy.";
173 if (isopen || ismixed) {
174 bool hasSource =
false;
176 if (nd.
nodetype == qn::NodeType::Source) hasSource =
true;
178 out.
reason =
"MEM requires a Source node when open classes are present.";
182 if (isclosed || ismixed) {
184 for (std::size_t i = 0; i < M; ++i)
185 if (std::isfinite(
sn.stations[i].nservers) &&
sn.stations[i].nservers > 1.0) {
187 "MEM does not support multiserver stations in closed or mixed models.";
194 std::vector<bool> capped(M,
false);
195 bool anyCapped =
false;
196 for (std::size_t i = 0; i < M; ++i) {
197 if (
sn.stations[i].sched == qn::SchedStrategy::EXT)
continue;
198 if (std::isfinite(detail::buffer_size(
sn, i + 1))) {
205 out.
reason =
"MEM supports finite station buffers only in open models.";
210 "MEM supports finite station buffers only in single-class models: the censored "
211 "GE/GE/c/0;N building block is single class.";
214 for (std::size_t i = 0; i < M; ++i) {
215 if (!capped[i])
continue;
216 const double c =
sn.stations[i].nservers;
217 if (!std::isfinite(c) || c < 1.0) {
218 out.
reason =
"MEM cannot apply a finite buffer to the infinite-server station " +
219 std::to_string(i + 1) +
".";
222 if (
sn.stations[i].sched != qn::SchedStrategy::FCFS) {
224 "MEM supports finite station buffers only under FCFS scheduling; station " +
225 std::to_string(i + 1) +
" uses " + detail::sched_text(
sn.stations[i].sched) +
229 const int dr =
sn.stations[i].droprule.empty() ? 0 :
sn.stations[i].droprule[0];
233 "MEM supports the DROP and BAS drop rules at a finite buffer; station " +
234 std::to_string(i + 1) +
" uses another rule.";
239 if (!
sn.disabled[i][0] &&
241 out.
reason =
"MEM with finite buffers needs a service scv of at least 1 at "
242 "station " + std::to_string(i + 1) +
243 ": the GE distribution is not defined below 1.";
247 for (std::size_t i = 0; i < M; ++i)
248 if (
sn.stations[i].sched == qn::SchedStrategy::EXT && !
sn.disabled[i][0] &&
250 out.
reason =
"MEM with finite buffers needs an external interarrival scv of at "
251 "least 1: the GE distribution is not defined below 1.";
274 "solver_nc_mem: the Maximum Entropy Method is built on GE (generalised exponential) "
275 "blocks whose entropy maximisation is transcendental throughout; this backend has "
279 const std::size_t M =
sn.nstations, R =
sn.nclasses;
288 bool anyOpen =
false, anyClosed =
false;
290 (std::isinf(c.
population) ? anyOpen : anyClosed) =
true;
291 const bool isclosed = anyClosed && !anyOpen;
292 const bool isopen = anyOpen && !anyClosed;
296 const auto fill_service = [&](
const std::vector<std::size_t>& sts,
Matrix<T>& mu,
300 c.assign(sts.size(), 1);
301 for (std::size_t k = 0; k < sts.size(); ++k) {
302 const std::size_t i = sts[k] - 1;
304 c[k] = std::isinf(
sn.stations[i].nservers)
306 :
static_cast<long>(std::llround(
sn.stations[i].nservers));
307 for (std::size_t r = 0; r < R; ++r) {
308 if (
sn.disabled[i][r] || !(
sn.rates(i, r) > zero))
continue;
309 mu(k, r) =
sn.rates(i, r);
310 if (
sn.scv(i, r) > zero) Cs(k, r) =
sn.scv(i, r);
314 const auto fill_routing = [&](
const std::vector<std::size_t>& sts) {
315 std::vector<Matrix<T>> P(R,
Matrix<T>(sts.size(), sts.size(), zero));
316 for (std::size_t r = 0; r < R; ++r)
317 for (std::size_t j = 0; j < sts.size(); ++j) {
318 const std::size_t jn =
sn.node_of_station(sts[j]);
319 for (std::size_t k = 0; k < sts.size(); ++k) {
320 const std::size_t kn =
sn.node_of_station(sts[k]);
321 P[r](j, k) =
sn.rtnodes((jn - 1) * R + r, (kn - 1) * R + r);
326 const auto fill_insens = [&](
const std::vector<std::size_t>& sts) {
327 std::vector<char> ins(sts.size(), 0);
328 for (std::size_t k = 0; k < sts.size(); ++k) {
330 ins[k] = (s == qn::SchedStrategy::PS || s == qn::SchedStrategy::LCFSPR) ? 1 : 0;
335 std::vector<long> N(R, 0);
336 for (std::size_t r = 0; r < R; ++r)
337 N[r] = std::isinf(
sn.classes[r].population)
339 :
static_cast<long>(std::llround(
sn.classes[r].population));
345 out.
sol.X.assign(R, zero);
346 out.
sol.C.assign(R, zero);
351 std::vector<std::size_t> sts(M);
352 for (std::size_t i = 0; i < M; ++i) sts[i] = i + 1;
355 fill_service(sts, mu, Cs, c);
356 std::vector<long> refstat(R, -1);
357 for (std::size_t r = 0; r < R; ++r)
358 refstat[r] =
static_cast<long>(
sn.classes[r].refstat) - 1;
360 me::me_cqn(M, R, N, mu, Cs, fill_routing(sts), c, refstat, fill_insens(sts), mopt);
366 for (std::size_t r = 0; r < R; ++r)
369 static_cast<double>(N[r])) /
371 out.
sol.iter =
static_cast<int>(res.
iter);
376 std::size_t sourceIdx = 0;
377 for (std::size_t i = 0; i < M; ++i)
378 if (
sn.stations[i].nodetype == qn::NodeType::Source) sourceIdx = i + 1;
379 if (sourceIdx == 0)
throw UnsupportedError(
"solver_nc_mem: MEM requires a Source node");
381 std::vector<std::size_t> qs;
382 for (std::size_t i = 1; i <= M; ++i)
383 if (i != sourceIdx) qs.push_back(i);
384 const std::size_t Mq = qs.size();
387 fill_service(qs, mu, Cs, c);
390 Matrix<T> lambda0(Mq, R, zero), Ca0(Mq, R, zero);
391 const std::size_t sourceNode =
sn.node_of_station(sourceIdx);
392 for (std::size_t r = 0; r < R; ++r) {
393 if (
sn.disabled[sourceIdx - 1][r] || !(
sn.rates(sourceIdx - 1, r) > zero))
continue;
394 if (!std::isinf(
sn.classes[r].population))
continue;
395 const T extRate =
sn.rates(sourceIdx - 1, r);
397 if (
sn.scv(sourceIdx - 1, r) > zero) caExt =
sn.scv(sourceIdx - 1, r);
398 for (std::size_t k = 0; k < Mq; ++k) {
399 const std::size_t dn =
sn.node_of_station(qs[k]);
400 const T p =
sn.rtnodes((sourceNode - 1) * R + r, (dn - 1) * R + r);
402 lambda0(k, r) = T(extRate * p);
413 std::vector<long> Nbuf(Mq, 0), c_blk(Mq, 1);
414 std::vector<int> blockrule(Mq, 0);
415 for (std::size_t k = 0; k < Mq; ++k) {
416 const std::size_t i = qs[k] - 1;
417 const double nb = detail::buffer_size(
sn, qs[k]);
420 Nbuf[k] = std::isfinite(nb) ?
static_cast<long>(std::llround(nb)) : 0;
421 c_blk[k] = std::isinf(
sn.stations[i].nservers)
423 :
static_cast<long>(std::llround(
sn.stations[i].nservers));
424 const int dr =
sn.stations[i].droprule.empty() ? 0 :
sn.stations[i].droprule[0];
427 std::vector<T> l0(Mq, zero), ca0(Mq, one), mu1(Mq, zero), cs1(Mq, one);
428 for (std::size_t k = 0; k < Mq; ++k) {
429 l0[k] = lambda0(k, 0);
430 ca0[k] = Ca0(k, 0) > zero ? Ca0(k, 0) : one;
436 const std::vector<Matrix<T>> Pall = fill_routing(qs);
437 for (std::size_t a = 0; a < Mq; ++a)
438 for (std::size_t b = 0; b < Mq; ++b) P1(a, b) = Pall[0](a, b);
444 me::me_oqn_blk(Mq, l0, ca0, mu1, cs1, P1, c_blk, Nbuf, blockrule, bopt);
445 for (std::size_t k = 0; k < Mq; ++k) {
446 out.
sol.Q(qs[k] - 1, 0) = br.
Q[k];
447 out.
sol.U(qs[k] - 1, 0) = br.
U[k];
448 out.
sol.R(qs[k] - 1, 0) = br.
W[k];
449 out.
sol.Tp(qs[k] - 1, 0) = br.
T_[k];
455 if (!
sn.disabled[sourceIdx - 1][0] &&
sn.rates(sourceIdx - 1, 0) > zero) {
456 const T x =
sn.rates(sourceIdx - 1, 0);
457 out.
sol.Tp(sourceIdx - 1, 0) = x;
460 for (std::size_t k = 0; k < Mq; ++k)
461 if (l0[k] > zero && blockrule[k] == 0)
462 accepted -= T(l0[k] * br.
PBa[k]);
463 if (accepted > zero) {
465 for (std::size_t k = 0; k < Mq; ++k) q += out.
sol.Q(qs[k] - 1, 0);
466 out.
sol.C[0] = T(q / accepted);
469 out.
sol.iter =
static_cast<int>(br.
iter);
470 out.
sol.method =
"mem.blocking";
477 res =
me::me_oqn(Mq, R, lambda0, Ca0, mu, Cs, fill_routing(qs), c, fill_insens(qs),
480 std::vector<char> openCls(R, 0);
481 std::vector<long> refstat(R, -1);
482 for (std::size_t r = 0; r < R; ++r) {
483 openCls[r] = std::isinf(
sn.classes[r].population) ? 1 : 0;
485 const auto it = std::find(qs.begin(), qs.end(),
sn.classes[r].refstat);
487 refstat[r] =
static_cast<long>(it - qs.begin());
490 res =
me::me_mqn(Mq, R, openCls, lambda0, Ca0, N, mu, Cs, fill_routing(qs), c, refstat,
491 fill_insens(qs), mopt);
494 for (std::size_t k = 0; k < Mq; ++k)
495 for (std::size_t r = 0; r < R; ++r) {
496 out.
sol.Q(qs[k] - 1, r) = res.
L(k, r);
497 out.
sol.U(qs[k] - 1, r) = res.
rho(k, r);
498 out.
sol.R(qs[k] - 1, r) = res.
W(k, r);
499 out.
sol.Tp(qs[k] - 1, r) = res.
lambda(k, r);
504 for (std::size_t k = 0; k < Mq; ++k) {
505 const std::size_t i = qs[k] - 1;
506 if (!std::isfinite(
sn.stations[i].nservers))
continue;
508 for (std::size_t r = 0; r < R; ++r) tot += out.
sol.U(i, r);
510 for (std::size_t r = 0; r < R; ++r) out.
sol.U(i, r) = T(out.
sol.U(i, r) / tot);
513 for (std::size_t r = 0; r < R; ++r) {
514 const bool open = std::isinf(
sn.classes[r].population);
518 const T x = isopen ? (
sn.disabled[sourceIdx - 1][r] ? zero
519 :
sn.rates(sourceIdx - 1, r))
521 if (!(x > zero))
continue;
522 out.
sol.Tp(sourceIdx - 1, r) = x;
525 for (std::size_t k = 0; k < Mq; ++k) q += out.
sol.Q(qs[k] - 1, r);
526 out.
sol.C[r] = T(q / x);
528 out.
sol.X[r] = res.
X[r];
534 out.
sol.iter =
static_cast<int>(res.
iter);