5#ifndef LINE_API_SN_SN_TO_QRF_BLOCKING_H
6#define LINE_API_SN_SN_TO_QRF_BLOCKING_H
114 const std::size_t M =
sn.nstations;
119 const std::vector<double> njobs =
sn.njobs();
120 for (std::size_t r = 0; r < njobs.size(); ++r) Nd += njobs[r];
121 if (!(Nd >= 1.0) || std::isinf(Nd)) {
122 out.
msg =
"the QRF bounds need a closed model with a finite population.";
125 const int N =
static_cast<int>(Nd + 0.5);
127 for (std::size_t i = 0; i < M; ++i) {
129 out.
binding[i] = std::isfinite(b);
130 out.
F[i] = (!std::isfinite(b) || b >
static_cast<double>(N)) ? N
131 :
static_cast<int>(b + 0.5);
133 std::ostringstream os;
134 os <<
"station " << (i + 1) <<
" has capacity " << out.
F[i]
135 <<
": the QRF bounds need every queue to be able to hold at least one job.";
148 std::vector<std::vector<int> >
BB;
149 std::vector<std::vector<int> >
MM;
150 std::vector<std::vector<int> >
MM1;
160inline QrfBlocking qrf_empty_blocking(
const std::vector<int>& F,
int f_one_based,
166 b.
BB.assign(1, std::vector<int>(M, 0));
167 b.
MM.assign(1, std::vector<int>(2, 0));
168 b.
MM1.assign(1, std::vector<int>(M, 0));
183 std::vector<bool> declared(M,
false);
185 for (std::size_t i = 0; i < M; ++i) {
186 const std::size_t ind = (i + 1 <=
sn.station_to_node.size()) ?
sn.station_to_node[i] : 0;
187 if (ind >= 1 && ind <=
sn.isbasblocking.size() &&
sn.isbasblocking[ind - 1]) {
195 bool dest_bas =
false;
197 for (std::size_t r = 0; r < sn.
droprule[f].size(); ++r)
198 if (sn.
droprule[f][r] == qn::DropStrategy::BAS) dest_bas =
true;
199 for (std::size_t i = 0; i < M; ++i) {
200 if (i == f || i >= sn.
droprule.size())
continue;
201 bool here_bas =
false;
202 for (std::size_t r = 0; r < sn.
droprule[i].size(); ++r)
203 if (sn.
droprule[i][r] == qn::DropStrategy::BAS) here_bas =
true;
204 if (dest_bas || here_bas) declared[i] =
true;
209 std::vector<std::size_t> out;
210 for (std::size_t i = 0; i < M; ++i) {
211 if (i == f || !declared[i])
continue;
212 if (sn.
stations[i].sched == qn::SchedStrategy::INF)
continue;
216 for (std::size_t r = 0; r < R && !routes; ++r)
217 for (std::size_t s = 0; s < R; ++s)
218 if (sn.
rt(i * R + r, f * R + s) > 0) {
222 if (routes) out.push_back(i);
224 std::sort(out.begin(), out.end());
235inline std::vector<std::vector<std::size_t> > qrf_enumerate_permutations(
236 const std::vector<std::size_t>& blockers,
int ZM) {
237 std::vector<std::vector<std::size_t> > cfg;
238 cfg.push_back(std::vector<std::size_t>());
239 const std::size_t nb = blockers.size();
240 for (
int z = 1; z <= ZM; ++z) {
241 std::vector<bool> pick(nb,
false);
242 for (std::size_t i = 0; i < static_cast<std::size_t>(z) && i < nb; ++i) pick[i] =
true;
244 std::vector<std::vector<std::size_t> > subsets;
245 std::vector<std::size_t> idx(z, 0);
246 for (
int d = 0; d < z; ++d) idx[d] = d;
248 std::vector<std::size_t> members;
249 for (
int d = 0; d < z; ++d) members.push_back(blockers[idx[d]]);
250 subsets.push_back(members);
252 while (d >= 0 && idx[d] == nb - z + d) --d;
255 for (
int e = d + 1; e < z; ++e) idx[e] = idx[e - 1] + 1;
257 for (std::size_t s = 0; s < subsets.size(); ++s) {
258 std::vector<std::size_t> order = subsets[s];
260 cfg.push_back(order);
261 }
while (std::next_permutation(order.begin(), order.end()));
271inline std::string qrf_cfg_key(
const std::vector<std::size_t>& seq) {
272 std::ostringstream os;
273 for (std::size_t i = 0; i < seq.size(); ++i) os << seq[i] <<
',';
279int qrf_total_phases(
const std::vector<std::pair<Matrix<T>, Matrix<T> > >& MAPs) {
281 for (std::size_t i = 0; i < MAPs.size(); ++i)
282 total += std::max<int>(1,
static_cast<int>(MAPs[i].first.rows()));
301 const std::size_t M =
sn.nstations;
304 const std::vector<double> njobs =
sn.njobs();
305 for (std::size_t r = 0; r < njobs.size(); ++r) Nd += njobs[r];
306 const int N =
static_cast<int>(Nd + 0.5);
309 if (!cap.
msg.empty()) {
314 std::vector<std::size_t> fcand;
315 for (std::size_t i = 0; i < M; ++i)
316 if (cap.
binding[i]) fcand.push_back(i);
321 return detail::qrf_empty_blocking(cap.
F, 1, M);
323 if (fcand.size() > 1) {
324 std::ostringstream os;
325 os <<
"'qrf.bas' models a single finite-capacity queue (its f is a scalar), but "
326 << fcand.size() <<
" stations have a binding buffer: ";
327 for (std::size_t c = 0; c < fcand.size(); ++c) {
329 os <<
sn.stations[fcand[c]].name;
331 os <<
". Use 'qrf.rsrd', whose PBB constraint sums over every full queue and therefore "
332 "admits several, or cap only one station.";
336 const std::size_t f = fcand[0];
338 const std::vector<std::size_t> blockers = detail::qrf_blockers(
sn, f, M);
342 int ZM = std::min<int>(
static_cast<int>(blockers.size()), N - cap.
F[f]);
345 QrfBlocking b = detail::qrf_empty_blocking(cap.
F,
static_cast<int>(f) + 1, M);
346 for (std::size_t i = 0; i < blockers.size(); ++i)
347 b.
blockers.push_back(
static_cast<int>(blockers[i]) + 1);
351 const std::vector<std::vector<std::size_t> > cfg =
352 detail::qrf_enumerate_permutations(blockers, ZM);
353 const int MR =
static_cast<int>(cfg.size());
356 const double n_vars =
357 static_cast<double>(MR) * (N + 1) * (N + 1) * Ktot * Ktot + Ktot;
358 if (n_vars > max_vars) {
359 std::ostringstream os;
360 os <<
"the QRF BAS linear program for this model would carry " << n_vars
361 <<
" variables (MR=" << MR <<
" blocking configurations, N=" << N <<
", " << Ktot
362 <<
" service phases in total), above the qrf_maxvars limit of " << max_vars
363 <<
". The enumeration cannot be truncated -- a depth below the reachable maximum ZM="
364 << ZM <<
" excises states the chain visits, and the result would no longer bound. "
365 <<
"Reduce the population, the number of stations feeding " <<
sn.stations[f].name
366 <<
", or the phase counts; or raise the limit deliberately.";
371 out.
f =
static_cast<int>(f) + 1;
375 out.
BB.assign(MR, std::vector<int>(M, 0));
376 out.
MM.assign(MR, std::vector<int>(std::max<std::size_t>(2, blockers.size()), 0));
377 out.
MM1.assign(MR, std::vector<int>(M, 0));
378 out.
ZZ.assign(MR, 0);
379 for (std::size_t i = 0; i < blockers.size(); ++i)
380 out.
blockers.push_back(
static_cast<int>(blockers[i]) + 1);
382 std::map<std::string, int> index;
383 for (
int m = 0; m < MR; ++m) {
384 const std::vector<std::size_t>& seq = cfg[m];
385 out.
ZZ[m] =
static_cast<int>(seq.size());
386 for (std::size_t z = 0; z < seq.size(); ++z) {
387 out.
BB[m][seq[z]] = 1;
389 out.
MM[m][z] =
static_cast<int>(seq[z]) + 1;
391 index[detail::qrf_cfg_key(seq)] = m;
393 for (
int m = 0; m < MR; ++m) {
394 if (out.
ZZ[m] >= ZM)
continue;
395 for (std::size_t b = 0; b < blockers.size(); ++b) {
396 const std::size_t j = blockers[b];
397 if (out.
BB[m][j])
continue;
398 std::vector<std::size_t> succ = cfg[m];
400 const std::map<std::string, int>::const_iterator it =
401 index.find(detail::qrf_cfg_key(succ));
402 if (it != index.end()) out.
MM1[m][j] = it->second + 1;
A network plus its refreshed NetworkStruct.
Matrix< T > rt
sn.rt and sn.rtnodes: the class-expanded routing.
std::vector< Station< T > > stations
stations[k-1] is the k-th station
std::vector< std::vector< DropStrategy > > droprule
QrfBlocking sn_to_qrf_blocking(const qn::NetworkStruct< T > &sn, int Ktot, double max_vars=kQrfDefaultMaxVars)
The QRF BAS blocking tables (f, MR, BB, MM, ZZ, MM1), derived from an sn.
const double kQrfDefaultMaxVars
Variable-count ceiling of the derived LP.
double sn_get_buffer_size(const qn::NetworkStruct< T > &sn, std::size_t ist)
Physical buffer size of a station, in jobs, the one in service included.
QrfCapacity sn_to_qrf_capacity(const qn::NetworkStruct< T > &sn)
F is an OCCUPANCY BOUND, not a declared capacity: the station's buffer where that buffer BINDS,...
A queueing network and its refreshed NetworkStruct.
Physical buffer size of a station, in jobs, the one in service included.
The derived blocking tables, in the reference's 1-based queue indexing.
int ZM
maximum reachable blocking depth
std::vector< int > ZZ
(MR) blocked count per configuration
int f
finite-capacity queue, 1-based
std::vector< std::vector< int > > BB
(MR x M) blocking state
std::vector< std::vector< int > > MM1
(MR x M) successor map, 1-based, 0 = absent
int MR
number of blocking configurations
std::vector< int > blockers
1-based stations that can be blocked behind f
std::vector< std::vector< int > > MM
(MR x .) blocking order, 1-based, 0 = absent
std::vector< int > F
(M) occupancy bounds
std::string msg
empty on success
Per-station occupancy bounds, and which of them bind.
std::vector< int > F
(M) occupancy bound of each station, in jobs
std::string msg
empty on success
std::vector< bool > binding
(M) true where the buffer can refuse a job