72 const std::vector<double>& cap_tot,
73 const std::vector<int>& nj_u,
74 const std::vector<std::size_t>& b_unit_of,
75 const std::vector<double>& m_rem_u,
76 const std::vector<std::vector<double>>& log_ph_per = {},
77 const std::vector<std::vector<int>>& lev_per = {}) {
78 const std::size_t Kb = b_unit_of.size();
79 const std::size_t U = nj_u.size();
80 std::vector<std::size_t> dims(U);
81 std::size_t nstate = 1;
82 for (std::size_t u = 0; u < U; ++u) { dims[u] =
static_cast<std::size_t
>(nj_u[u]) + 1; nstate *= dims[u]; }
83 const double NEG = -std::numeric_limits<double>::infinity();
84 auto idx_of = [&](
const std::vector<int>& v) {
85 std::size_t ix = 0, mult = 1;
86 for (std::size_t u = 0; u < U; ++u) { ix +=
static_cast<std::size_t
>(v[u]) * mult; mult *= dims[u]; }
89 auto sub_of = [&](std::size_t ix) {
90 std::vector<int> v(U);
91 for (std::size_t u = 0; u < U; ++u) { v[u] =
static_cast<int>(ix % dims[u]); ix /= dims[u]; }
94 std::vector<double> L(nstate, NEG);
95 L[idx_of(nj_u)] = 0.0;
96 for (std::size_t a = 0; a < caps_per.size(); ++a) {
97 std::vector<double> Ln(nstate, NEG);
100 const bool by_level = a < lev_per.size() && lev_per[a].size() == Kb;
101 std::vector<int> ulev;
104 std::sort(ulev.begin(), ulev.end());
105 ulev.erase(std::unique(ulev.begin(), ulev.end()), ulev.end());
107 for (std::size_t si = 0; si < nstate; ++si) {
108 if (!std::isfinite(L[si]))
continue;
109 const std::vector<int> rem = sub_of(si);
110 std::vector<int> av(Kb);
111 for (std::size_t k = 0; k < Kb; ++k) av[k] = std::min(caps_per[a][k], rem[b_unit_of[k]]);
112 std::vector<int> m(Kb, 0);
115 for (std::size_t k = 0; k < Kb; ++k) t += m[k];
116 if (!(std::isfinite(cap_tot[a]) && t > cap_tot[a])) {
119 v += std::lgamma(
static_cast<double>(t) + 1.0);
120 for (std::size_t k = 0; k < Kb; ++k) v -= std::lgamma(
static_cast<double>(m[k]) + 1.0);
126 std::vector<double> lc;
127 for (std::size_t c = 0; c < Kb; ++c) {
128 if (m[c] <= 0)
continue;
129 std::vector<int> mc = m;
132 for (std::size_t k = 0; k < Kb; ++k) x -= std::lgamma(
static_cast<double>(mc[k]) + 1.0);
135 for (std::size_t k = 0; k < Kb; ++k)
136 if (lev_per[a][k] == l) tl += mc[k];
137 x += std::lgamma(
static_cast<double>(tl) + 1.0);
142 for (
double x : lc) top = std::max(top, x);
144 for (
double x : lc) acc += std::exp(x - top);
145 v += top + std::log(acc);
147 if (a < log_ph_per.size())
148 for (std::size_t k = 0; k < Kb && k < log_ph_per[a].size(); ++k)
149 v += m[k] * log_ph_per[a][k];
151 std::vector<int> nx = rem;
153 for (std::size_t k = 0; k < Kb; ++k) nx[b_unit_of[k]] -= m[k];
154 for (std::size_t u = 0; u < U; ++u)
if (nx[u] < 0) fits =
false;
156 const std::size_t di = idx_of(nx);
157 if (!std::isfinite(Ln[di])) Ln[di] = v;
158 else {
const double mx = std::max(Ln[di], v);
159 Ln[di] = mx + std::log(std::exp(Ln[di] - mx) + std::exp(v - mx)); }
163 while (pos < Kb && m[pos] == av[pos]) { m[pos] = 0; ++pos; }
164 if (pos == Kb)
break;
170 std::vector<double> terms;
171 for (std::size_t si = 0; si < nstate; ++si) {
172 if (!std::isfinite(L[si]))
continue;
173 const std::vector<int> rem = sub_of(si);
175 bool stranded =
false;
176 for (std::size_t u = 0; u < U; ++u) {
177 const double r = rem[u];
178 if (m_rem_u[u] >= 1.0)
179 v += std::lgamma(r + m_rem_u[u]) - std::lgamma(r + 1.0) - std::lgamma(m_rem_u[u]);
183 if (stranded)
continue;
186 if (terms.empty())
return NEG;
187 double top = terms[0];
188 for (
double v : terms) top = std::max(top, v);
190 for (
double v : terms) acc += std::exp(v - top);
191 return top + std::log(acc);
212 const std::size_t M =
sn.nstations;
213 const std::size_t K =
sn.nclasses;
214 if (M == 0 || K == 0)
return 0.0;
218 double cutoff =
opt.cutoff;
219 if (!(cutoff > 0.0) || !std::isfinite(cutoff))
220 cutoff = std::ceil(std::pow(6000.0, 1.0 /
static_cast<double>(M * K)));
225 const auto is_share = [](SchedStrategy s) {
226 return s == SchedStrategy::INF || s == SchedStrategy::PS || s == SchedStrategy::DPS ||
227 s == SchedStrategy::GPS || s == SchedStrategy::PSPRIO ||
228 s == SchedStrategy::DPSPRIO || s == SchedStrategy::GPSPRIO ||
229 s == SchedStrategy::LPS;
231 std::vector<bool> is_buffered(K,
true);
233 for (std::size_t k = 0; k < K; ++k) {
234 if (k <
sn.issignal.size() &&
sn.issignal[k]) is_buffered[k] =
false;
235 if (is_buffered[k]) ++Kb;
243 const auto is_preempt = [](SchedStrategy s) {
244 return s == SchedStrategy::FCFSPR || s == SchedStrategy::FCFSPI ||
245 s == SchedStrategy::LCFSPR || s == SchedStrategy::LCFSPI ||
246 s == SchedStrategy::FCFSPRPRIO || s == SchedStrategy::FCFSPIPRIO ||
247 s == SchedStrategy::LCFSPRPRIO || s == SchedStrategy::LCFSPIPRIO;
249 const auto is_ordered = [&](std::size_t i) {
250 const SchedStrategy s =
sn.stations[i].sched;
251 return is_preempt(s) || (Kb > 1 && !(s == SchedStrategy::EXT || is_share(s)));
253 const auto log_phases = [&](std::size_t i, std::size_t k) {
254 const double p =
static_cast<double>(
sn.phasessz_of(i + 1, k + 1));
255 return (std::isfinite(p) && p > 1.0) ? std::log(p) : 0.0;
257 std::size_t n_ord = 0;
258 for (std::size_t i = 0; i < M; ++i)
259 if (is_ordered(i)) ++n_ord;
261 double log_nstates = 0.0;
262 std::vector<double> nk_eff(K, 0.0);
263 const std::vector<double>& njobs =
sn.njobs();
264 for (std::size_t k = 0; k < K; ++k)
265 nk_eff[k] = (k < njobs.size() && std::isfinite(njobs[k])) ? njobs[k] : cutoff;
267 const auto place = [&](
double nk,
double ms) {
268 return std::lgamma(1.0 + nk + ms - 1.0) - std::lgamma(1.0 + ms - 1.0) -
269 std::lgamma(1.0 + nk);
277 const auto disabled_at = [&](std::size_t i, std::size_t k) {
278 return i <
sn.classcap.size() && k <
sn.classcap[i].size() &&
sn.classcap[i][k] == 0.0;
294 const double kVisitZeroTol = 1.0e-12;
295 std::vector<std::vector<bool>> never(M, std::vector<bool>(K,
false));
296 if (!
sn.visits.empty() && !
sn.chains.empty()) {
297 for (std::size_t i = 0; i < M; ++i) {
299 const std::size_t nd =
sn.node_of_station(i + 1);
300 if (nd == 0)
continue;
301 const std::size_t isf1 =
sn.stateful_index(nd);
302 if (isf1 == 0)
continue;
303 const std::size_t isf = isf1 - 1;
304 for (std::size_t k = 0; k < K; ++k) {
305 std::size_t c =
sn.nchains;
306 for (std::size_t ci = 0; ci <
sn.chains.size(); ++ci)
307 if (k <
sn.chains[ci].size() &&
sn.chains[ci][k]) { c = ci;
break; }
308 if (c >=
sn.visits.size())
continue;
309 const auto& V =
sn.visits[c];
310 if (isf >= V.rows() || k >= V.cols())
continue;
312 if (std::isfinite(v) && std::fabs(v) < kVisitZeroTol) never[i][k] =
true;
316 const auto admits = [&](std::size_t i, std::size_t k) {
317 return !disabled_at(i, k) && !never[i][k];
319 const auto admitting_count = [&](std::size_t k) {
321 for (std::size_t i = 0; i < M; ++i)
322 if (admits(i, k)) ++n;
325 const auto admitting_all = [&](std::size_t k) {
326 const std::size_t n = admitting_count(k);
327 return n < 1 ? std::size_t(1) : n;
329 const auto admitting_rem = [&](std::size_t k) {
331 for (std::size_t i = 0; i < M; ++i) {
332 if (is_ordered(i))
continue;
333 if (admits(i, k)) ++n;
340 for (std::size_t k = 0; k < K; ++k)
341 if (nk_eff[k] > 0.0 && admitting_count(k) == 0) nk_eff[k] = 0.0;
347 const auto placement_term = [&](std::size_t k,
double n) {
348 const bool is_open = !(k < njobs.size() && std::isfinite(njobs[k]));
349 if (!is_open)
return place(n,
static_cast<double>(admitting_all(k)));
350 std::size_t mopen = 0;
351 for (std::size_t i = 0; i < M; ++i)
352 if (
sn.stations[i].sched != SchedStrategy::EXT && admits(i, k)) ++mopen;
353 const double mo =
static_cast<double>(mopen);
354 return std::lgamma(1.0 + n + mo) - std::lgamma(1.0 + mo) - std::lgamma(1.0 + n);
364 std::vector<std::size_t> unit_of(K, 0);
365 std::vector<std::vector<std::size_t>> unit_cls;
366 for (std::size_t ci = 0; ci <
sn.chains.size(); ++ci) {
367 std::vector<std::size_t> cls;
368 for (std::size_t k = 0; k < K && k <
sn.chains[ci].size(); ++k)
369 if (
sn.chains[ci][k] && k < njobs.size() && std::isfinite(njobs[k]) && admitting_count(k) > 0)
371 if (cls.size() > 1) {
373 for (std::size_t k : cls) ntot += njobs[k];
374 unit_cls.push_back(cls);
375 for (std::size_t k : cls) { nk_eff[k] = ntot; unit_of[k] = unit_cls.size(); }
378 for (std::size_t k = 0; k < K; ++k)
379 if (unit_of[k] == 0) { unit_cls.push_back(std::vector<std::size_t>{k}); unit_of[k] = unit_cls.size(); }
381 const auto unit_closed = [&](std::size_t u) {
382 const std::size_t k = unit_cls[u][0];
383 return k < njobs.size() && std::isfinite(njobs[k]);
387 const auto placement_block = [&](
const std::vector<double>& nk) ->
double {
390 for (std::size_t u = 0; u < unit_cls.size(); ++u) {
391 const std::vector<std::size_t>& cls = unit_cls[u];
392 if (cls.size() == 1) {
lp += placement_term(cls[0], nk[cls[0]]);
continue; }
393 const double n = nk[cls[0]];
395 for (std::size_t k : cls) P +=
static_cast<double>(admitting_count(k));
396 P = std::max(P, 1.0);
397 lp += std::lgamma(n + P) - std::lgamma(P) - std::lgamma(n + 1.0);
401 for (std::size_t k = 0; k < K; ++k)
402 if (!is_buffered[k])
lp += placement_term(k, nk[k]);
403 std::vector<int> njb;
404 std::vector<std::size_t> bunits;
405 std::vector<std::size_t> b_unit_of;
406 for (std::size_t k = 0; k < K; ++k) {
407 if (!is_buffered[k])
continue;
408 njb.push_back(
static_cast<int>(std::floor(nk[k])));
409 const std::size_t u = unit_of[k] - 1;
411 while (bu < bunits.size() && bunits[bu] != u) ++bu;
412 if (bu == bunits.size()) bunits.push_back(u);
413 b_unit_of.push_back(bu);
415 std::vector<int> nj_u;
416 std::vector<double> m_rem_u;
418 for (std::size_t u : bunits) {
419 const int n =
static_cast<int>(std::floor(nk[unit_cls[u][0]]));
421 grid *=
static_cast<double>(n) + 1.0;
423 for (std::size_t k : unit_cls[u]) mr +=
static_cast<double>(admitting_rem(k));
424 m_rem_u.push_back(mr);
427 std::vector<std::vector<int>> caps_per;
428 std::vector<std::vector<double>> log_ph_per;
429 std::vector<std::vector<int>> lev_per;
430 std::vector<double> cap_tot;
431 for (std::size_t i = 0; i < M; ++i) {
432 if (!is_ordered(i))
continue;
433 std::vector<double> lph;
434 for (std::size_t k = 0; k < K; ++k)
435 if (is_buffered[k]) lph.push_back(is_preempt(
sn.stations[i].sched) ? log_phases(i, k) : 0.0);
436 log_ph_per.push_back(lph);
437 std::vector<int> lev;
438 if (
sn.stations[i].sched == SchedStrategy::LCFSPRIO)
439 for (std::size_t k = 0; k < K; ++k)
440 if (is_buffered[k]) lev.push_back(k <
sn.classes.size() ?
sn.classes[k].prio : 0);
441 lev_per.push_back(lev);
442 std::vector<int> per;
444 for (std::size_t k = 0; k < K; ++k)
445 if (is_buffered[k]) {
447 if (i <
sn.classcap.size() && k <
sn.classcap[i].size() &&
448 std::isfinite(
sn.classcap[i][k]))
449 c = std::min(c,
static_cast<int>(std::floor(
sn.classcap[i][k])));
450 if (!admits(i, k)) c = 0;
454 caps_per.push_back(per);
455 const double c =
sn.stations[i].cap;
456 cap_tot.push_back(std::isfinite(c) && c >= 0
457 ? std::floor(c) : std::numeric_limits<double>::infinity());
459 return lp +
log_ordered_joint(caps_per, cap_tot, nj_u, b_unit_of, m_rem_u, log_ph_per, lev_per);
462 for (
int c : njb) total += c;
463 const std::size_t nb = njb.size();
465 const double lkb = std::log(
static_cast<double>(nb));
466 lp +=
static_cast<double>(n_ord) *
467 ((total + 1.0) * lkb - std::log(
static_cast<double>(nb) - 1.0) +
468 std::log1p(-std::exp(-(total + 1.0) * lkb)));
470 for (std::size_t i = 0; i < M; ++i)
471 if (is_preempt(
sn.stations[i].sched))
472 for (std::size_t k = 0; k < K; ++k)
473 if (is_buffered[k])
lp += nk[k] * log_phases(i, k);
474 for (std::size_t k = 0; k < K; ++k)
475 if (is_buffered[k]) {
476 const std::size_t mk = admitting_rem(k);
477 if (mk >= 1)
lp += place(nk[k],
static_cast<double>(mk));
486 std::vector<std::vector<std::size_t>> transit;
487 for (
const auto& kv :
sn.nodeparam) {
488 const std::size_t ind = kv.first;
490 if (kv.second.nitems == 0)
continue;
491 std::vector<std::size_t> cls;
492 const std::size_t isf1 =
sn.stateful_index(ind);
493 if (isf1 > 0 && !
sn.visits.empty()) {
494 for (std::size_t k = 0; k < K; ++k) {
495 std::size_t c =
sn.chains.size();
496 for (std::size_t ci = 0; ci <
sn.chains.size(); ++ci)
497 if (k <
sn.chains[ci].size() &&
sn.chains[ci][k]) { c = ci;
break; }
498 if (c >=
sn.visits.size())
continue;
499 const auto& V =
sn.visits[c];
500 if (isf1 - 1 >= V.rows() || k >= V.cols())
continue;
504 transit.push_back(cls);
507 std::vector<std::vector<std::pair<std::size_t, double>>> topts;
509 for (
const auto& cls : transit) {
510 std::vector<std::pair<std::size_t, double>> o{{0, 1.0}};
511 for (std::size_t k : cls) {
512 const std::size_t u1 = unit_of[k];
515 if (e.first == u1) { e.second += 1.0; found =
true; }
516 if (!found) o.push_back({u1, 1.0});
518 combos *=
static_cast<double>(o.size());
521 if (transit.empty() || combos > 4096.0) {
522 log_nstates += placement_block(nk_eff);
523 for (
const auto& cls : transit) log_nstates += std::log(1.0 +
static_cast<double>(cls.size()));
525 std::vector<double> vals;
526 std::vector<std::size_t> pick(topts.size(), 0);
528 std::vector<double> nk = nk_eff;
531 for (std::size_t t = 0; t < topts.size(); ++t) {
532 const std::size_t u1 = topts[t][pick[t]].first;
533 if (u1 == 0)
continue;
534 lw += std::log(topts[t][pick[t]].second);
535 const std::size_t u = u1 - 1;
536 if (unit_closed(u)) {
537 for (std::size_t k : unit_cls[u]) nk[k] -= 1.0;
538 if (nk[unit_cls[u][0]] < 0.0) ok =
false;
541 if (ok) vals.push_back(lw + placement_block(nk));
543 while (pos < topts.size() && pick[pos] + 1 == topts[pos].size()) { pick[pos] = 0; ++pos; }
544 if (pos == topts.size())
break;
547 double top = vals[0];
548 for (
double v : vals) top = std::max(top, v);
550 for (
double v : vals) acc += std::exp(v - top);
551 log_nstates += top + std::log(acc);
556 for (std::size_t i = 1; i <= M; ++i) {
557 const SchedStrategy sched =
sn.stations[i - 1].sched;
558 const bool share = sched == SchedStrategy::INF || sched == SchedStrategy::PS ||
559 sched == SchedStrategy::DPS || sched == SchedStrategy::GPS ||
560 sched == SchedStrategy::PSPRIO || sched == SchedStrategy::DPSPRIO ||
561 sched == SchedStrategy::GPSPRIO || sched == SchedStrategy::LPS;
562 if (is_preempt(sched))
continue;
563 for (std::size_t r = 1; r <= K; ++r) {
564 const double p =
static_cast<double>(
sn.phasessz_of(i, r));
565 if (!std::isfinite(p) || p <= 1.0 || !admits(i - 1, r - 1))
continue;
567 if (sched == SchedStrategy::EXT)
572 m = std::min(nk_eff[r - 1],
sn.stations[i - 1].nservers);
573 if (!std::isfinite(m)) m = nk_eff[r - 1];
574 log_nstates += std::lgamma(1.0 + m + p - 1.0) - std::lgamma(1.0 + p - 1.0) -
575 std::lgamma(1.0 + m);
583 for (std::size_t ind = 1; ind <=
sn.nodes.size(); ++ind) {
584 const std::vector<RoutingStrategy>& rt_i =
sn.nodes[ind - 1].routing;
586 for (std::size_t r = 0; r < K && r < rt_i.size(); ++r)
587 if (rt_i[r] == RoutingStrategy::RROBIN || rt_i[r] == RoutingStrategy::WRROBIN) ++nrr;
588 if (nrr == 0)
continue;
589 const std::size_t nout =
sn.downstream_stations(ind).size();
590 if (nout <= 1)
continue;
591 log_nstates +=
static_cast<double>(nrr) * std::log(
static_cast<double>(nout));
598 for (
const auto& kv :
sn.nodeparam) {
599 const std::size_t ind = kv.first;
602 if (cp.
nitems == 0)
continue;
603 const double n =
static_cast<double>(cp.
nitems);
605 for (
int c : cp.
itemcap) csum += c;
606 const double C = std::min(csum, n);
607 log_nstates += std::lgamma(n + 1.0) - std::lgamma(n - C + 1.0);
610 const double rest = n - C;
612 std::vector<double> lt;
613 for (
double sv = 0.0; sv <= smax; sv += 1.0)
614 lt.push_back(std::lgamma(rest + 1.0) - std::lgamma(sv + 1.0) - std::lgamma(rest - sv + 1.0));
616 for (
double v : lt) top = std::max(top, v);
618 for (
double v : lt) acc += std::exp(v - top);
619 log_nstates += top + std::log(acc);