63 const std::vector<double>& cap_tot,
64 const std::vector<int>& njobs, std::size_t m_rem) {
65 const std::size_t Kb = njobs.size();
66 std::vector<std::size_t> dims(Kb);
67 std::size_t nstate = 1;
68 for (std::size_t k = 0; k < Kb; ++k) { dims[k] = njobs[k] + 1; nstate *= dims[k]; }
69 const double NEG = -std::numeric_limits<double>::infinity();
70 auto idx_of = [&](
const std::vector<int>& v) {
71 std::size_t ix = 0, mult = 1;
72 for (std::size_t k = 0; k < Kb; ++k) { ix += v[k] * mult; mult *= dims[k]; }
75 auto sub_of = [&](std::size_t ix) {
76 std::vector<int> v(Kb);
77 for (std::size_t k = 0; k < Kb; ++k) { v[k] =
static_cast<int>(ix % dims[k]); ix /= dims[k]; }
80 std::vector<double> L(nstate, NEG);
81 L[idx_of(njobs)] = 0.0;
82 for (std::size_t a = 0; a < caps_per.size(); ++a) {
83 std::vector<double> Ln(nstate, NEG);
84 for (std::size_t si = 0; si < nstate; ++si) {
85 if (!std::isfinite(L[si]))
continue;
86 const std::vector<int> rem = sub_of(si);
87 std::vector<int> av(Kb);
88 for (std::size_t k = 0; k < Kb; ++k) av[k] = std::min(caps_per[a][k], rem[k]);
89 std::vector<int> m(Kb, 0);
92 for (std::size_t k = 0; k < Kb; ++k) t += m[k];
93 if (!(std::isfinite(cap_tot[a]) && t > cap_tot[a])) {
94 double v = L[si] + std::lgamma(
static_cast<double>(t) + 1.0);
95 for (std::size_t k = 0; k < Kb; ++k) v -= std::lgamma(
static_cast<double>(m[k]) + 1.0);
96 std::vector<int> nx(Kb);
97 for (std::size_t k = 0; k < Kb; ++k) nx[k] = rem[k] - m[k];
98 const std::size_t di = idx_of(nx);
99 if (!std::isfinite(Ln[di])) Ln[di] = v;
100 else {
const double mx = std::max(Ln[di], v);
101 Ln[di] = mx + std::log(std::exp(Ln[di] - mx) + std::exp(v - mx)); }
104 while (pos < Kb && m[pos] == av[pos]) { m[pos] = 0; ++pos; }
105 if (pos == Kb)
break;
111 std::vector<double> terms;
112 for (std::size_t si = 0; si < nstate; ++si) {
113 if (!std::isfinite(L[si]))
continue;
114 const std::vector<int> rem = sub_of(si);
117 for (std::size_t k = 0; k < Kb; ++k) {
118 const double r = rem[k];
119 v += std::lgamma(r +
static_cast<double>(m_rem)) - std::lgamma(r + 1.0) -
120 std::lgamma(
static_cast<double>(m_rem));
123 bool leftover =
false;
124 for (std::size_t k = 0; k < Kb; ++k)
if (rem[k] > 0) leftover =
true;
125 if (leftover)
continue;
129 if (terms.empty())
return NEG;
130 double top = terms[0];
131 for (
double v : terms) top = std::max(top, v);
133 for (
double v : terms) acc += std::exp(v - top);
134 return top + std::log(acc);
155 const std::size_t M =
sn.nstations;
156 const std::size_t K =
sn.nclasses;
157 if (M == 0 || K == 0)
return 0.0;
161 double cutoff =
opt.cutoff;
162 if (!(cutoff > 0.0) || !std::isfinite(cutoff))
163 cutoff = std::ceil(std::pow(6000.0, 1.0 /
static_cast<double>(M * K)));
168 const auto is_share = [](SchedStrategy s) {
169 return s == SchedStrategy::INF || s == SchedStrategy::PS || s == SchedStrategy::DPS ||
170 s == SchedStrategy::GPS || s == SchedStrategy::PSPRIO ||
171 s == SchedStrategy::DPSPRIO || s == SchedStrategy::GPSPRIO ||
172 s == SchedStrategy::LPS;
174 std::vector<bool> is_buffered(K,
true);
176 for (std::size_t k = 0; k < K; ++k) {
177 if (k <
sn.issignal.size() &&
sn.issignal[k]) is_buffered[k] =
false;
178 if (is_buffered[k]) ++Kb;
180 std::size_t n_ord = 0;
182 for (std::size_t i = 0; i < M; ++i) {
183 const SchedStrategy s =
sn.stations[i].sched;
184 if (s == SchedStrategy::EXT || is_share(s))
continue;
189 double log_nstates = 0.0;
190 std::vector<double> nk_eff(K, 0.0);
191 const std::vector<double>& njobs =
sn.njobs();
192 for (std::size_t k = 0; k < K; ++k)
193 nk_eff[k] = (k < njobs.size() && std::isfinite(njobs[k])) ? njobs[k] : cutoff;
195 const auto place = [&](
double nk,
double ms) {
196 return std::lgamma(1.0 + nk + ms - 1.0) - std::lgamma(1.0 + ms - 1.0) -
197 std::lgamma(1.0 + nk);
205 const auto disabled_at = [&](std::size_t i, std::size_t k) {
206 return i <
sn.classcap.size() && k <
sn.classcap[i].size() &&
sn.classcap[i][k] == 0.0;
208 const auto admitting_all = [&](std::size_t k) {
210 for (std::size_t i = 0; i < M; ++i)
211 if (!disabled_at(i, k)) ++n;
212 return n < 1 ? std::size_t(1) : n;
214 const auto admitting_rem = [&](std::size_t k) {
216 for (std::size_t i = 0; i < M; ++i) {
217 const SchedStrategy sc =
sn.stations[i].sched;
218 if (!(sc == SchedStrategy::EXT || is_share(sc)))
continue;
219 if (!disabled_at(i, k)) ++n;
224 for (std::size_t k = 0; k < K; ++k)
225 log_nstates += place(nk_eff[k],
static_cast<double>(admitting_all(k)));
227 const std::size_t m_rem = M - n_ord;
228 for (std::size_t k = 0; k < K; ++k)
230 log_nstates += place(nk_eff[k],
static_cast<double>(admitting_all(k)));
231 std::vector<int> caps;
233 for (std::size_t k = 0; k < K; ++k)
234 if (is_buffered[k]) {
235 caps.push_back(
static_cast<int>(std::floor(nk_eff[k])));
236 grid *= (std::floor(nk_eff[k]) + 1.0);
239 std::vector<std::vector<int>> caps_per;
240 std::vector<double> cap_tot;
241 std::vector<int> njb;
242 for (std::size_t k = 0; k < K; ++k)
243 if (is_buffered[k]) njb.push_back(
static_cast<int>(std::floor(nk_eff[k])));
244 for (std::size_t i = 0; i < M; ++i) {
245 const SchedStrategy sc =
sn.stations[i].sched;
246 if (sc == SchedStrategy::EXT || is_share(sc))
continue;
247 std::vector<int> per;
249 for (std::size_t k = 0; k < K; ++k)
250 if (is_buffered[k]) {
252 if (i <
sn.classcap.size() && k <
sn.classcap[i].size() &&
253 std::isfinite(
sn.classcap[i][k]))
254 c = std::min(c,
static_cast<int>(std::floor(
sn.classcap[i][k])));
258 caps_per.push_back(per);
259 const double c =
sn.stations[i].cap;
260 cap_tot.push_back(std::isfinite(c) && c >= 0
261 ? std::floor(c) : std::numeric_limits<double>::infinity());
266 for (
int c : caps) total += c;
267 const double lkb = std::log(
static_cast<double>(Kb));
268 log_nstates +=
static_cast<double>(n_ord) *
269 ((total + 1.0) * lkb - std::log(
static_cast<double>(Kb) - 1.0) +
270 std::log1p(-std::exp(-(total + 1.0) * lkb)));
271 for (std::size_t k = 0; k < K; ++k)
272 if (is_buffered[k]) {
273 const std::size_t mk = admitting_rem(k);
274 if (mk >= 1) log_nstates += place(nk_eff[k],
static_cast<double>(mk));
281 for (std::size_t i = 1; i <= M; ++i) {
282 const SchedStrategy sched =
sn.stations[i - 1].sched;
283 const bool share = sched == SchedStrategy::INF || sched == SchedStrategy::PS ||
284 sched == SchedStrategy::DPS || sched == SchedStrategy::GPS ||
285 sched == SchedStrategy::PSPRIO || sched == SchedStrategy::DPSPRIO ||
286 sched == SchedStrategy::GPSPRIO || sched == SchedStrategy::LPS;
287 for (std::size_t r = 1; r <= K; ++r) {
288 const double p =
static_cast<double>(
sn.phasessz_of(i, r));
289 if (!std::isfinite(p) || p <= 1.0)
continue;
291 if (sched == SchedStrategy::EXT)
296 m = std::min(nk_eff[r - 1],
sn.stations[i - 1].nservers);
297 if (!std::isfinite(m)) m = nk_eff[r - 1];
298 log_nstates += std::lgamma(1.0 + m + p - 1.0) - std::lgamma(1.0 + p - 1.0) -
299 std::lgamma(1.0 + m);
307 for (std::size_t ind = 1; ind <=
sn.nodes.size(); ++ind) {
308 const std::vector<RoutingStrategy>& rt_i =
sn.nodes[ind - 1].routing;
310 for (std::size_t r = 0; r < K && r < rt_i.size(); ++r)
311 if (rt_i[r] == RoutingStrategy::RROBIN || rt_i[r] == RoutingStrategy::WRROBIN) ++nrr;
312 if (nrr == 0)
continue;
313 const std::size_t nout =
sn.downstream_stations(ind).size();
314 if (nout <= 1)
continue;
315 log_nstates +=
static_cast<double>(nrr) * std::log(
static_cast<double>(nout));