5#ifndef LINE_SOLVERS_NC_SOLVER_NC_OI_H
6#define LINE_SOLVERS_NC_SOLVER_NC_OI_H
73inline void oi_lattice(
const std::vector<int>& N, std::vector<std::size_t>& shp,
74 std::vector<std::size_t>& stride, std::size_t& total) {
75 const std::size_t R = N.size();
79 for (std::size_t d = 0; d < R; ++d) shp[d] = static_cast<std::size_t>(N[d]) + 1;
80 for (std::size_t d = 1; d < R; ++d) stride[d] = stride[d - 1] * shp[d - 1];
81 for (std::size_t d = 0; d < R; ++d) total *= shp[d];
85inline std::vector<int> oi_sub(std::size_t i,
const std::vector<std::size_t>& shp) {
86 std::vector<int> n(shp.size(), 0);
88 for (std::size_t d = 0; d < shp.size(); ++d) {
89 n[d] =
static_cast<int>(li % shp[d]);
96inline std::size_t oi_idx(
const std::vector<int>& n,
const std::vector<std::size_t>& stride) {
98 for (std::size_t d = 0; d < n.size(); ++d) i += stride[d] *
static_cast<std::size_t
>(n[d]);
106inline std::vector<std::size_t> oi_microstate(
const std::vector<int>& n) {
107 std::vector<std::size_t> c;
108 for (std::size_t r = 0; r < n.size(); ++r)
109 for (
int a = 0; a < n[r]; ++a) c.push_back(r + 1);
114const std::size_t OI_RANK_RATE_MEMO_LIMIT = 1u << 17;
129 const std::function<T(
const std::vector<std::size_t>&)>& f) {
130 std::shared_ptr<std::map<std::vector<int>, T> > memo(
new std::map<std::vector<int>, T>());
131 return [f, memo](
const std::vector<int>& n) -> T {
132 const typename std::map<std::vector<int>, T>::const_iterator it = memo->find(n);
133 if (it != memo->end())
return it->second;
134 const T v = f(oi_microstate(n));
135 if (memo->size() < OI_RANK_RATE_MEMO_LIMIT) (*memo)[n] = v;
145std::vector<T> oi_phi(
const std::function<T(
const std::vector<int>&)>& oirate,
146 const std::vector<int>& N) {
147 std::vector<std::size_t> shp, stride;
148 std::size_t total = 0;
149 oi_lattice(N, shp, stride, total);
150 const std::size_t R = shp.size();
151 std::vector<T> Phi(total, num_traits<T>::from_int(0));
152 for (std::size_t i = 0; i < total; ++i) {
153 const std::vector<int> n = oi_sub(i, shp);
155 for (
int v : n) tot += v;
157 Phi[i] = num_traits<T>::from_int(1);
160 T s = num_traits<T>::from_int(0);
161 for (std::size_t r = 0; r < R; ++r)
162 if (n[r] > 0) s += Phi[i - stride[r]];
163 Phi[i] = T(s / oirate(n));
175std::vector<T> oi_ld_table(
const std::vector<T>& Dq,
double c,
176 const std::vector<std::size_t>& shp, std::size_t total) {
177 const std::size_t R = shp.size();
178 std::vector<T> W(total, num_traits<T>::from_int(0));
179 const double cc = (std::isfinite(c) && c > 0.0) ? c : 1.0;
180 for (std::size_t i = 0; i < total; ++i) {
181 const std::vector<int> n = oi_sub(i, shp);
183 for (
int v : n) tot += v;
184 double logf = std::lgamma(
static_cast<double>(tot) + 1.0);
186 for (std::size_t r = 0; r < R; ++r)
188 const double d = num_traits<T>::to_double(Dq[r]);
193 logf += n[r] * std::log(d) - std::lgamma(
static_cast<double>(n[r]) + 1.0);
196 for (
int k = 1; k <= tot; ++k)
197 logf -= std::log(std::min(
static_cast<double>(k), cc));
198 W[i] = num_traits<T>::from_double(std::exp(logf));
205std::vector<T> oi_conv(
const std::vector<T>& A,
const std::vector<T>& B,
206 const std::vector<std::size_t>& shp,
207 const std::vector<std::size_t>& stride, std::size_t total) {
208 const std::size_t R = shp.size();
209 std::vector<std::vector<int>> subs(total);
210 for (std::size_t i = 0; i < total; ++i) subs[i] = oi_sub(i, shp);
211 std::vector<T> C(total, num_traits<T>::from_int(0));
212 for (std::size_t i = 0; i < total; ++i) {
213 T acc = num_traits<T>::from_int(0);
214 for (std::size_t j = 0; j <= i; ++j) {
216 for (std::size_t d = 0; d < R && le; ++d)
217 if (subs[j][d] > subs[i][d]) le =
false;
220 for (std::size_t d = 0; d < R; ++d)
221 off += stride[d] *
static_cast<std::size_t
>(subs[i][d] - subs[j][d]);
222 acc += T(A[j] * B[off]);
231T oi_fnc_mean(
const std::vector<T>& Psi,
const std::vector<T>& Gfull,
232 const std::vector<std::size_t>& shp,
const std::vector<std::size_t>& stride,
234 const T zero = num_traits<T>::from_int(0);
236 for (std::size_t i = 0; i < total; ++i) {
237 if (Psi[i] == zero)
continue;
238 const std::vector<int> b = oi_sub(i, shp);
240 for (std::size_t d = 0; d < shp.size(); ++d)
241 off += stride[d] * (shp[d] - 1 -
static_cast<std::size_t
>(b[d]));
242 val += T(Psi[i] * Gfull[off]);
249Matrix<T> oi_visits(
const qn::NetworkStruct<T>& sn) {
250 const T zero = num_traits<T>::from_int(0);
251 const std::size_t M = sn.nstations, K = sn.nclasses;
252 Matrix<T> V(M, K, zero);
253 for (std::size_t r = 0; r < K; ++r) {
254 std::size_t chain = 0;
255 for (std::size_t c = 0; c < sn.nchains; ++c)
256 if (sn.chains[c][r]) chain = c + 1;
257 if (chain == 0)
continue;
258 for (std::size_t i = 0; i < M; ++i)
259 V(i, r) = sn.visits[chain - 1](sn.stateful_of_station(i + 1) - 1, r);
260 const T vref = V(sn.classes[r].refstat - 1, r);
262 for (std::size_t i = 0; i < M; ++i) V(i, r) = T(V(i, r) / vref);
269bool class_dependent_fcfs_rate(
const qn::NetworkStruct<T>& sn, std::size_t i) {
270 double lo = 0.0, hi = 0.0;
272 for (std::size_t r = 0; r < sn.nclasses; ++r) {
273 if (!(sn.classes[r].population > 0.0))
continue;
274 const double v = num_traits<T>::to_double(sn.rates(i, r));
275 if (!std::isfinite(v))
continue;
280 lo = std::min(lo, v);
281 hi = std::max(hi, v);
284 return any && (hi - lo) > 1e-9 * hi;
302 for (std::size_t i = 0; i <
sn.nstations; ++i) {
304 if (s == SchedStrategy::INF)
continue;
305 if (s == SchedStrategy::PAS || s == SchedStrategy::OI) {
308 }
else if (s == SchedStrategy::PS || s == SchedStrategy::LCFSPR ||
309 s == SchedStrategy::SIRO || s == SchedStrategy::FCFS) {
310 if ((s == SchedStrategy::FCFS || s == SchedStrategy::SIRO) &&
311 detail::class_dependent_fcfs_rate(
sn, i))
333 if (
sn.nstations != 2)
return false;
334 for (std::size_t i = 0; i <
sn.nstations; ++i) {
336 if (s != SchedStrategy::PAS && s != SchedStrategy::OI)
return false;
337 if (!
sn.stations[i].svc_rate_fun)
return false;
356 "solver_nc_oi_analyzer: the BCMP weight table is formed as exp(lgamma(...)) and lG as "
357 "log(G); this backend has no transcendental arithmetic");
360 const std::size_t M =
sn.nstations, K =
sn.nclasses;
364 for (std::size_t c = 0; c <
sn.nchains; ++c)
365 if (
sn.inchain[c].size() > 1)
367 "solver_nc_oi: requires one class per chain (no class switching)");
368 std::vector<int> N(K, 0);
369 for (std::size_t r = 0; r < K; ++r) {
370 if (std::isinf(
sn.classes[r].population))
372 N[r] =
static_cast<int>(std::llround(
sn.classes[r].population));
375 std::vector<bool> isOI(M,
false), isINF(M,
false), isQ(M,
false);
376 for (std::size_t i = 0; i < M; ++i) {
378 if (s == SchedStrategy::INF) {
380 }
else if (s == SchedStrategy::PAS || s == SchedStrategy::OI) {
383 "solver_nc_oi: supports OI stations only (PAS with a non-empty swap graph "
384 "is not order-independent)");
386 if (!
sn.stations[i].svc_rate_fun)
388 "solver_nc_oi: an OI station has no service rate function; set it via "
389 "setServiceRateFunction");
390 }
else if (s == SchedStrategy::PS || s == SchedStrategy::LCFSPR ||
391 s == SchedStrategy::FCFS || s == SchedStrategy::SIRO) {
393 if ((s == SchedStrategy::FCFS || s == SchedStrategy::SIRO) &&
394 detail::class_dependent_fcfs_rate(
sn, i))
396 "solver_nc_oi: a station has class-dependent FCFS/SIRO rates and is not "
397 "product form; class-independent rates are required");
400 "solver_nc_oi: supports only INF (delay), OI, PS, LCFS-PR, SIRO and "
401 "class-independent FCFS stations");
408 std::vector<T> Z(K, zero);
410 for (std::size_t i = 0; i < M; ++i)
411 for (std::size_t r = 0; r < K; ++r) {
413 if (!std::isfinite(mu) || mu == 0.0)
continue;
414 const T st = T(V(i, r) /
sn.rates(i, r));
415 if (isINF[i]) Z[r] += st;
416 if (isQ[i]) D(i, r) = st;
419 for (std::size_t i = 0; i < M; ++i)
421 for (std::size_t r = 0; r < K; ++r)
425 "solver_nc_oi: requires unit per-class visits at every OI station");
427 std::vector<std::size_t> oiList, qList;
428 for (std::size_t i = 0; i < M; ++i) {
429 if (isOI[i]) oiList.push_back(i + 1);
430 if (isQ[i]) qList.push_back(i + 1);
432 std::vector<pfqn::OiRate<T>> rates;
433 for (std::size_t m = 0; m < oiList.size(); ++m) {
434 const std::function<T(
const std::vector<std::size_t>&)> f =
435 sn.stations[oiList[m] - 1].svc_rate_fun;
437 [f](
const std::vector<int>& n) {
return f(detail::oi_microstate(n)); });
440 std::vector<std::size_t> shp, stride;
441 std::size_t total = 0;
442 detail::oi_lattice(N, shp, stride, total);
445 std::vector<T> Gfull(total, zero);
446 for (std::size_t i = 0; i < total; ++i)
449 for (std::size_t i : qList) {
450 std::vector<T> Dq(K, zero);
451 for (std::size_t r = 0; r < K; ++r) Dq[r] = D(i - 1, r);
452 const std::vector<T> Wq =
453 detail::oi_ld_table(Dq,
sn.stations[i - 1].nservers, shp, total);
454 Gfull = detail::oi_conv(Gfull, Wq, shp, stride, total);
457 const T G = Gfull[total - 1];
460 std::vector<T> X(K, zero);
461 for (std::size_t r = 0; r < K; ++r)
463 std::vector<int> Nr = N;
465 X[r] = T(Gfull[detail::oi_idx(Nr, stride)] / G);
468 Matrix<T> Q(M, K, zero), Tp(M, K, zero), R(M, K, zero), U(M, K, zero);
470 for (std::size_t m = 0; m < oiList.size(); ++m) {
471 const std::size_t i = oiList[m];
472 const std::vector<T> Phi = detail::oi_phi<T>(rates[m], N);
473 for (std::size_t r = 0; r < K; ++r) {
474 if (N[r] == 0)
continue;
476 Phi, N, [r](
const std::vector<int>& n) {
480 T(detail::oi_fnc_mean(fr.
Psi, Gfull, shp, stride, total) / G - one);
483 for (std::size_t i : qList) {
484 std::vector<T> Dq(K, zero);
485 for (std::size_t r = 0; r < K; ++r) Dq[r] = D(i - 1, r);
486 const std::vector<T> Wq =
487 detail::oi_ld_table(Dq,
sn.stations[i - 1].nservers, shp, total);
488 for (std::size_t r = 0; r < K; ++r) {
489 if (N[r] == 0)
continue;
491 Wq, N, [r](
const std::vector<int>& n) {
495 T(detail::oi_fnc_mean(fr.
Psi, Gfull, shp, stride, total) / G - one);
498 for (std::size_t i = 0; i < M; ++i)
500 for (std::size_t r = 0; r < K; ++r) {
502 if (!std::isfinite(mu) || mu == 0.0)
continue;
503 Q(i, r) = T(X[r] * V(i, r) /
sn.rates(i, r));
506 for (std::size_t i = 0; i < M; ++i)
507 for (std::size_t r = 0; r < K; ++r) Tp(i, r) = T(X[r] * V(i, r));
508 for (std::size_t i = 0; i < M; ++i)
510 for (std::size_t r = 0; r < K; ++r) U(i, r) = Q(i, r);
511 for (std::size_t i : qList) {
512 const double c_raw =
sn.stations[i - 1].nservers;
513 const double c = (std::isfinite(c_raw) && c_raw > 0.0) ? c_raw : 1.0;
514 for (std::size_t r = 0; r < K; ++r)
517 for (std::size_t m = 0; m < oiList.size(); ++m) {
524 const std::size_t i = oiList[m];
525 const double s_raw =
sn.stations[i - 1].nservers;
526 const double s = (std::isfinite(s_raw) && s_raw > 0.0) ? s_raw : 1.0;
527 const std::vector<T> Phi = detail::oi_phi<T>(rates[m], N);
529 for (std::size_t r = 0; r < K; ++r) {
530 if (N[r] == 0)
continue;
532 Phi, N, [&ins, &stride, r](
const std::vector<int>& n) {
533 return ins.
g(detail::oi_idx(n, stride), r);
536 T((detail::oi_fnc_mean(fr.
Psi, Gfull, shp, stride, total) / G - one) /
540 for (std::size_t i = 0; i < M; ++i)
541 for (std::size_t r = 0; r < K; ++r)
542 if (Tp(i, r) > zero) R(i, r) = T(Q(i, r) / Tp(i, r));
544 std::vector<T> C(K, zero);
545 for (std::size_t r = 0; r < K; ++r)
555 out.
sol.method =
"oi";
576 "solver_nc_pas_is_analyzer: the auto-normalized importance sampler draws from a "
577 "continuous proposal and reports lG = log(G); this backend has no transcendental "
581 const std::size_t M =
sn.nstations, K =
sn.nclasses;
583 for (std::size_t c = 0; c <
sn.nchains; ++c)
584 if (
sn.inchain[c].size() > 1)
586 "solver_nc_pas_is: requires one class per chain (no class switching)");
589 throw UnsupportedError(
"solver_nc_pas_is: requires a closed queueing network");
592 "solver_nc_pas_is: models a two-station pass-and-swap tandem");
593 std::vector<int> N(K, 0);
594 for (std::size_t r = 0; r < K; ++r)
595 N[r] =
static_cast<int>(std::llround(
sn.classes[r].population));
597 std::vector<std::function<T(
const std::vector<std::size_t>&)>> svc(M);
598 std::vector<Matrix<T>> swapG(M);
599 for (std::size_t i = 0; i < M; ++i) {
601 if (s != SchedStrategy::PAS && s != SchedStrategy::OI)
603 "solver_nc_pas_is: requires both stations to be OI/PAS");
604 svc[i] =
sn.stations[i].svc_rate_fun;
607 "solver_nc_pas_is: an OI/PAS station has no service rate function; set it via "
608 "setServiceRateFunction");
612 std::vector<pfqn::OiRateFun<T>> mu(M);
613 for (std::size_t i = 0; i < M; ++i) mu[i] = detail::oi_rank_rate<T>(svc[i]);
624 std::vector<pfqn::PasRateFun<T>> murates(M);
625 for (std::size_t i = 0; i < M; ++i) {
626 const std::function<T(
const std::vector<std::size_t>&)> f = svc[i];
627 murates[i] = [f](
const std::vector<int>& c) {
628 std::vector<std::size_t>
mc(c.size());
629 for (std::size_t a = 0; a < c.size(); ++a)
630 mc[a] =
static_cast<std::size_t
>(c[a]);
636 for (std::size_t a = 0; a < H.
rows(); ++a)
637 for (std::size_t b = 0; b < H.
cols(); ++b)
641 for (std::size_t i = 0; i < M; ++i)
642 for (std::size_t r = 0; r < K; ++r)
645 "solver_nc_pas_is: requires unit per-class visits at both stations");
647 const std::size_t samples =
opt.samples > 0 ?
opt.samples : 10000;
648 const unsigned seed =
opt.seed > 0 ?
static_cast<unsigned>(
opt.seed) : 23456u;
654 Matrix<T> Q(M, K, zero), Tp(M, K, zero), R(M, K, zero), U(M, K, zero);
655 for (std::size_t i = 0; i < M; ++i)
656 for (std::size_t r = 0; r < K; ++r) Q(i, r) = res.
Q(i, r);
661 std::vector<T> X(K, zero);
662 for (std::size_t r = 0; r < K; ++r)
664 std::vector<int> Nr = N;
672 if (res.
G > zero) X[r] = T(gr.
G / res.
G);
675 for (std::size_t i = 0; i < M; ++i)
676 for (std::size_t r = 0; r < K; ++r) Tp(i, r) = T(X[r] * V(i, r));
677 for (std::size_t i = 0; i < M; ++i) {
678 const double s_raw =
sn.stations[i].nservers;
679 const double s = (std::isfinite(s_raw) && s_raw > 0.0) ? s_raw : 1.0;
680 for (std::size_t r = 0; r < K; ++r) {
681 if (N[r] == 0)
continue;
682 std::vector<int> er(K, 0);
684 const T muR = mu[i](er);
689 for (std::size_t i = 0; i < M; ++i)
690 for (std::size_t r = 0; r < K; ++r)
691 if (Tp(i, r) > zero) R(i, r) = T(Q(i, r) / Tp(i, r));
693 std::vector<T> C(K, zero);
694 for (std::size_t r = 0; r < K; ++r)
704 out.
sol.method =
"is";
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
The exception types the port throws.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
bool nc_is_oi_model(const qn::NetworkStruct< T > &sn)
Port of nc_is_oi_model.m: a closed network with at least one OI station and nothing but BCMP product-...
NcSolution< T > solver_nc_pas_is_analyzer(const qn::NetworkStruct< T > &sn, const NcSolverOptions &opt)
Port of solver_nc_pas_is_analyzer.m.
bool nc_is_pas_model(const qn::NetworkStruct< T > &sn)
Port of nc_is_pas_model.m: a closed two-station OI / P&S tandem.
NcSolution< T > solver_nc_oi_analyzer(const qn::NetworkStruct< T > &sn, const NcSolverOptions &opt)
Port of solver_nc_oi_analyzer.m.
std::mt19937_64 McRng
The generator type every Monte Carlo entry point in this tree accepts.
NcResult< T > pfqn_ncoi(const std::vector< T > &Z, const std::vector< int > &N, const std::vector< OiRate< T > > &mu, const Matrix< T > &visits)
Normalizing constant of a closed network of ORDER-INDEPENDENT (OI) / pass-and-swap stations with empt...
PasIsResult< T > pfqn_pas_is(const std::vector< int > &N, const std::vector< OiRateFun< T > > &mu, const Matrix< int > &H, std::size_t samples, McRng &rng, bool want_qlen=true)
Importance-sampling estimate of the normalizing constant of a single communicating class of a cyclic ...
Matrix< T > pas_swap2order(const std::vector< Matrix< T > > &swap, const std::vector< PasRateFun< T > > &listRate, const std::vector< int > &N0=std::vector< int >())
Placement-order DAG of a two-station pass-and-swap tandem.
OiFncResult< T > pfqn_oi_fnc(const std::vector< T > &Phi, const std::vector< int > &N, const std::function< T(const std::vector< int > &)> &f)
Order-independent (OI) functional server: the balance function Psi and the rate mu_f of an auxiliary ...
OiInsvcResult< T > pfqn_oi_insvc(const std::function< T(const std::vector< int > &)> &oirate, const std::vector< int > &N)
Conditional mean number of IN-SERVICE jobs per class at an order-independent station.
std::function< T(const std::vector< int > &)> OiRateFun
The OI rank rate of a station as a function of the per-class COUNT vector: the svcRateFun of an OI / ...
Matrix< T > station_swap_graph(const NetworkStruct< T > &sn, std::size_t ist)
The swap graph of a PAS / OI station, with the defaults refreshLocalVars.m installs applied.
bool station_swap_graph_is_zero(const NetworkStruct< T > &sn, std::size_t ist)
True when the station's materialized swap graph is entirely zero.
Controls and result shape shared by the normalizing-constant analyzers.
A queueing network and its refreshed NetworkStruct.
Global placement-order DAG of a closed two-station pass-and-swap tandem.
Normalizing constant of a closed network of ORDER-INDEPENDENT (OI) / pass-and-swap stations with empt...
Order-independent (OI) functional server: the balance function Psi and the rate mu_f of an auxiliary ...
Conditional mean number of IN-SERVICE jobs per class at an order-independent station.
Importance-sampling estimate of the normalizing constant of a single communicating class of a cyclic ...
The [Q,U,R,T,C,X,lG] of the reference, plus the algorithm that ran.
mva::MvaSolution< T > sol
Controls, defaulting to SolverOptions('NC') in the reference.
Return value of pfqn_oi_fnc, mirroring [muf, Psi, mu] flattened.
std::vector< T > Psi
column-major over the lattice
Return value of pfqn_oi_insvc, mirroring [g, Xi, Phi].
Matrix< T > g
(prod(N+1) x R) E[sir_r | n]
Return value of pfqn_pas_is / pfqn_oi_is, mirroring [G, lG, Q].
T G
estimate of the communicating-class normalizing constant
Matrix< T > Q
(2 x R) mean per-class queue length, Q(1,:) = N - Q(0,:)
One job class of the network.
double population
infinite for an open class