5#ifndef LINE_SOLVERS_SSA_SOLVER_SSA_NRM_H
6#define LINE_SOLVERS_SSA_SOLVER_SSA_NRM_H
123 std::size_t node = 0;
125 std::size_t from = 0;
127 bool is_phase =
false;
128 std::size_t phase_from = 0, phase_to = 0;
129 std::size_t dep_phase = 0;
130 bool is_buf_svc =
false;
134 bool is_cache =
false;
137 std::vector<std::size_t> to_slots;
138 std::vector<double> cdf;
140 std::vector<std::size_t> from_slots;
141 std::size_t nnzP = 0;
143 std::size_t det_dest =
static_cast<std::size_t
>(-1);
161 std::vector<std::size_t> sd_node, sd_class;
163 std::vector<double> sd_weight;
165 std::vector<std::vector<std::size_t>> sd_slots;
166 std::vector<std::vector<double>> sd_pie;
184 : sn_(
sn), opt_(
opt), rng_(
opt.seed) {
189 rr_cursor_.assign(I_ * K_ + K_ + 1, 0);
190 build_dependencies();
191 build_initial_state();
202 const std::vector<SsaCacheRatio>&
cache()
const {
return cache_out_; }
206 using Rx = detail::NrmReaction;
213 std::size_t I_ = 0, K_ = 0, M_ = 0, NS_ = 0, maxnph_ = 1;
216 std::vector<std::vector<std::size_t>> phoff_, nph_;
217 std::vector<std::size_t> slot_node_, slot_class_;
220 std::vector<bool> is_station_;
221 std::vector<std::size_t> to_station_;
222 std::vector<double> mi_;
223 std::vector<std::vector<double>> rate_;
224 std::vector<std::vector<std::vector<double>>> pie_;
225 std::vector<std::vector<bool>> buf_ph_class_;
226 std::vector<bool> buf_ph_node_;
227 std::vector<SchedStrategy> sched_;
228 std::vector<std::vector<double>> lld_;
229 std::vector<std::vector<double>> wnorm_;
230 std::vector<double> classprio_;
231 std::vector<double> nservers_;
236 std::vector<std::vector<std::size_t>> D_;
238 std::vector<std::vector<std::vector<std::size_t>>> dep_rx_;
243 std::vector<std::vector<char>> blk_can_;
244 bool blk_on_ =
false;
247 std::vector<double> nvec0_;
248 std::vector<std::vector<std::size_t>> buffers0_;
249 std::vector<Matrix<double>> svcph0_;
277 std::vector<char> member_node;
278 std::vector<double> ccap;
279 double gcap = std::numeric_limits<double>::infinity();
280 double memcap = std::numeric_limits<double>::infinity();
281 std::vector<double> sz;
283 std::vector<double> b;
284 std::vector<char> waitq;
286 std::vector<FcrRegion> fcr_;
287 bool fcr_any_waitq_ =
false;
298 std::vector<char> is_cache_node_;
299 std::vector<std::vector<char>> cache_read_;
301 std::vector<std::vector<T>> cache_var0_, cache_var_;
304 std::vector<SsaCacheRatio> cache_out_;
305 void cache_fire(
const Rx& x, std::vector<double>& X, std::size_t& dest_pos,
bool& have_dest);
306 void cache_write_back();
314 std::vector<char> is_poll_;
315 std::vector<qn::PollingInfo<T>> poll_info_;
316 std::vector<PollCtrl> poll_ctrl_;
318 PollCtrl poll_land(std::size_t ind, std::size_t q,
int mode,
long budget,
bool raise =
true);
320 std::vector<long> poll_nbuf(
const std::vector<double>& X, std::size_t ind)
const {
321 std::vector<long> nb(K_, 0);
322 for (std::size_t r = 0; r < K_; ++r) nb[r] = std::lround(class_pop(X, ind, r));
328 void build_regions();
329 void build_reactions();
330 void build_dependencies();
331 void build_initial_state();
334 double class_pop(
const std::vector<double>& X, std::size_t ind, std::size_t r)
const {
336 for (std::size_t k = 0; k < nph_[ind][r]; ++k) s += X[phoff_[ind][r] + k];
339 std::vector<double> class_counts(
const std::vector<double>& X, std::size_t ind)
const {
340 std::vector<double> v(K_, 0.0);
341 for (std::size_t r = 0; r < K_; ++r) v[r] = class_pop(X, ind, r);
344 double kir_frac(
const std::vector<double>& X, std::size_t slot, std::size_t ind,
345 std::size_t r)
const {
346 const double nir = class_pop(X, ind, r);
347 return nir <= 0.0 ? 0.0 : X[slot] / nir;
349 double lldfac(std::size_t ist,
double ntot)
const {
350 if (ist == npos || lld_[ist].empty() || ntot < 1.0)
return 1.0;
351 const std::size_t lim = lld_[ist].size();
352 std::size_t k =
static_cast<std::size_t
>(std::llround(ntot));
353 if (k > lim) k = lim;
354 return lld_[ist][k - 1];
356 static double dpsshare(
const std::vector<double>& w,
const std::vector<double>& n,
359 for (std::size_t s = 0; s < n.size(); ++s) den += w[s] * n[s];
360 return den <= 0.0 ? 0.0 : w[r] * n[r] / den;
362 static double gpsshare(
const std::vector<double>& w,
const std::vector<double>& n,
364 if (n[r] <= 0.0)
return 0.0;
366 for (std::size_t s = 0; s < n.size(); ++s) den += n[s] > 0.0 ? w[s] : 0.0;
367 return den <= 0.0 ? 0.0 : w[r] / den;
382 bool urgent(
const std::vector<double>& n, std::size_t r)
const {
385 for (std::size_t s = 0; s < n.size(); ++s)
386 if (n[s] > 0.0 && (!any || classprio_[s] < best)) {
387 best = classprio_[s];
390 return any && classprio_[r] == best;
393 std::vector<double> prio_group(
const std::vector<double>& n, std::size_t r,
394 double* total =
nullptr)
const {
395 std::vector<double> act(n.size(), 0.0);
397 for (std::size_t s = 0; s < n.size(); ++s)
398 if (classprio_[s] == classprio_[r]) {
402 if (total) *total = tot;
405 double psprioshare(
const std::vector<double>& n, std::size_t r,
double c)
const {
407 for (
double v : n) ni += v;
408 if (ni <= 0.0)
return 0.0;
409 if (ni <= c)
return (n[r] / ni) * std::min(ni, c);
410 if (!urgent(n, r))
return 0.0;
412 prio_group(n, r, &niprio);
413 return niprio <= 0.0 ? 0.0 : (n[r] / niprio) * std::min(niprio, c);
415 double dpsprioshare(
const std::vector<double>& w,
const std::vector<double>& n, std::size_t r,
418 for (
double v : n) ni += v;
419 if (ni <= 0.0)
return 0.0;
420 if (ni <= c)
return dpsshare(w, n, r);
421 if (!urgent(n, r))
return 0.0;
422 return dpsshare(w, prio_group(n, r), r);
424 double gpsprioshare(
const std::vector<double>& w,
const std::vector<double>& n, std::size_t r,
427 for (
double v : n) ni += v;
428 if (ni <= 0.0)
return 0.0;
429 if (ni <= c)
return gpsshare(w, n, r);
430 if (!urgent(n, r))
return 0.0;
431 return gpsshare(w, prio_group(n, r), r);
442 double prio_pop(
const std::vector<double>& n, std::size_t r,
double c)
const {
444 for (
double v : n) ni += v;
445 if (ni <= c || !urgent(n, r))
return ni;
447 prio_group(n, r, &niprio);
451 std::vector<double> prio_vec(
const std::vector<double>& n, std::size_t r,
double c)
const {
453 for (
double v : n) ni += v;
454 if (ni <= c || !urgent(n, r))
return n;
455 return prio_group(n, r);
463 void check_peaks()
const {
464 for (std::size_t ist = 0; ist < M_; ++ist) {
465 const auto& st = sn_.stations[ist];
466 const bool on[2] = {
static_cast<bool>(st.cdscaling),
static_cast<bool>(st.jdscaling)};
467 const std::vector<T>* pks[2] = {&st.cdscalingpeak, &st.jdscalingpeak};
468 const char* names[2] = {
"setClassDependence",
"setJointDependence"};
469 for (std::size_t h = 0; h < 2; ++h) {
470 if (!on[h])
continue;
471 bool ok = pks[h]->size() >= K_;
472 for (std::size_t k = 0; ok && k < K_; ++k)
473 ok = num_traits<T>::to_double((*pks[h])[k]) > 0.0;
475 throw InputError(
"SolverSSA(method='nrm'): station '" + st.name +
476 "' declares a dependent scaling with no declared peak rate. "
477 "Utilization there is T*E[S]/peak, so pass the peak to " +
483 double cdfac(std::size_t ist,
const std::vector<double>& n, std::size_t r)
const {
484 if (ist == npos)
return 1.0;
485 const auto& st = sn_.stations[ist];
486 if (!st.cdscaling && !st.jdscaling)
return 1.0;
487 std::vector<T> nt(n.size());
488 for (std::size_t s = 0; s < n.size(); ++s) nt[s] = T(n[s]);
490 for (
const auto* h : {&st.cdscaling, &st.jdscaling}) {
492 const std::vector<T> v = (*h)(nt);
493 f *= num_traits<T>::to_double(v[std::min(r, v.size() - 1)]);
499 double propensity(std::size_t j,
const std::vector<double>& X,
500 const std::vector<std::vector<std::size_t>>& bufs,
501 const std::vector<Matrix<double>>& svc)
const;
502 std::size_t pick_from_buffer(
const std::vector<std::size_t>& buf, std::size_t ist);
503 std::size_t draw_entry_phase(std::size_t ind, std::size_t r);
504 bool capacity_loss(
const std::vector<double>& X, std::size_t slot)
const;
506 const typename qn::NetworkStruct<T>::PasParam& pas_of(std::size_t ist)
const;
508 std::vector<double> pas_increments(std::size_t ist,
const std::vector<std::size_t>& c)
const;
510 double pas_rate(std::size_t ist,
const std::vector<std::size_t>& c, std::size_t r)
const;
512 double pas_in_service(std::size_t ist,
const std::vector<std::size_t>& c, std::size_t r)
const;
514 void pas_tag_started(std::size_t ind,
const std::vector<std::size_t>& cold,
515 const std::vector<std::size_t>& cnew);
517 void pas_depart(std::size_t ind, std::vector<std::size_t>& c, std::size_t r);
518 std::size_t fcr_refusing_region(
const std::vector<double>& X, std::size_t src_node,
519 std::size_t src_class, std::size_t dst_node,
520 std::size_t dst_class)
const;
521 void fcr_outcome(
const std::vector<double>& X,
const Rx& x, std::size_t dst_node,
522 std::size_t dst_class, std::vector<std::vector<std::size_t>>& fcr_buf,
523 bool& lost,
bool& blocked)
const;
524 std::size_t fcr_release_cascade(std::vector<double>& X,
525 std::vector<std::vector<std::size_t>>& bufs,
526 std::vector<std::vector<std::size_t>>& fcr_buf,
527 std::vector<Matrix<double>>& svc,
bool& svc_changed);
528 bool capacity_block(
const std::vector<double>& X, std::size_t slot,
529 std::size_t src_slot)
const;
530 std::size_t pick_preempted(
const std::vector<double>& X,
531 const std::vector<std::size_t>& buf, std::size_t jnd,
532 std::size_t arr_class);
533 void apply_arrival_buffer(std::size_t jnd, std::size_t s,
const std::vector<double>& X,
534 std::vector<std::vector<std::size_t>>& bufs,
535 std::vector<Matrix<double>>& svc,
bool& svc_changed);
536 void update_buffers(std::size_t kfire,
const std::vector<double>& X,
537 std::vector<std::vector<std::size_t>>& bufs, std::size_t dest_pos,
538 bool have_dest, std::vector<Matrix<double>>& svc,
bool& svc_changed);
541 void build_state_dependent_dest(Rx& x);
543 std::size_t resolve_state_dependent_dest(std::size_t kfire,
const std::vector<double>& X);
551 std::vector<std::size_t> rr_cursor_;
554 void tag(std::size_t ind, std::size_t r,
bool preempt) {
555 if (!is_station_[ind] || r >= K_)
return;
556 const std::size_t ist = to_station_[ind];
557 if (ist >= M_)
return;
560 if (sched_[ist] == SchedStrategy::EXT)
return;
561 (preempt ? preempt_cnt_ : start_cnt_)(ist, r) += 1.0;
564 static constexpr std::size_t npos =
static_cast<std::size_t
>(-1);
581void NrmEngine<T>::build_layout() {
586 is_station_.assign(I_,
false);
587 to_station_.assign(I_, npos);
588 for (std::size_t i = 0; i < I_; ++i)
589 if (sn_.
nodes[i].station != 0) {
590 is_station_[i] =
true;
591 to_station_[i] = sn_.
nodes[i].station - 1;
594 sched_.assign(M_, SchedStrategy::FCFS);
595 nservers_.assign(M_, 1.0);
596 for (std::size_t i = 0; i < M_; ++i) {
598 nservers_[i] = sn_.
stations[i].nservers;
603 nph_.assign(I_, std::vector<std::size_t>(K_, 1));
604 pie_.assign(I_, std::vector<std::vector<double>>(K_));
605 for (std::size_t i = 0; i < I_; ++i) {
606 for (std::size_t r = 0; r < K_; ++r) {
607 pie_[i][r].assign(1, 1.0);
608 if (!is_station_[i])
continue;
609 const std::size_t ist = to_station_[i];
612 const std::size_t n = m.D0.rows();
613 if (n <= 1)
continue;
616 std::vector<double> pd(n, 0.0);
618 for (std::size_t k = 0; k < n && k < p.size(); ++k) {
619 pd[k] = num_traits<T>::to_double(p[k]);
620 if (!(pd[k] > 0.0)) pd[k] = 0.0;
624 for (
double& x : pd) x /= tot;
633 phoff_.assign(I_, std::vector<std::size_t>(K_, 0));
636 for (std::size_t i = 0; i < I_; ++i)
637 for (std::size_t r = 0; r < K_; ++r) {
640 maxnph_ = std::max(maxnph_, nph_[i][r]);
642 slot_node_.assign(NS_, 0);
643 slot_class_.assign(NS_, 0);
644 for (std::size_t i = 0; i < I_; ++i)
645 for (std::size_t r = 0; r < K_; ++r)
646 for (std::size_t k = 0; k < nph_[i][r]; ++k) {
647 slot_node_[phoff_[i][r] + k] = i;
648 slot_class_[phoff_[i][r] + k] = r;
653 buf_ph_class_.assign(I_, std::vector<bool>(K_,
false));
654 buf_ph_node_.assign(I_,
false);
655 for (std::size_t i = 0; i < I_; ++i) {
656 if (!is_station_[i] || !detail::sched_is_buf_ph(sched_[to_station_[i]]))
continue;
657 for (std::size_t r = 0; r < K_; ++r)
658 if (nph_[i][r] > 1) {
659 buf_ph_class_[i][r] =
true;
660 buf_ph_node_[i] =
true;
665 const double inf = std::numeric_limits<double>::infinity();
667 rate_.assign(I_, std::vector<double>(K_, 0.0));
668 for (std::size_t i = 0; i < I_; ++i) {
669 if (is_station_[i]) {
670 const std::size_t ist = to_station_[i];
671 mi_[i] = sn_.
stations[ist].nservers;
672 for (std::size_t r = 0; r < K_; ++r)
674 rate_[i][r] = num_traits<T>::to_double(sn_.
rates(ist, r));
676 for (std::size_t r = 0; r < K_; ++r)
681 lld_.assign(M_, std::vector<double>());
682 wnorm_.assign(M_, std::vector<double>(K_, 0.0));
683 for (std::size_t i = 0; i < M_; ++i) {
684 for (
const T& x : sn_.
stations[i].lldscaling)
685 lld_[i].push_back(num_traits<T>::to_double(x));
686 if (!detail::sched_is_weighted(sched_[i]))
continue;
688 for (std::size_t r = 0; r < K_ && r < sn_.
stations[i].schedparam.size(); ++r)
689 tot += num_traits<T>::to_double(sn_.
stations[i].schedparam[r]);
693 " scheduling with non-positive total weight");
697 " at station '" + sn_.
stations[i].name +
698 "' is not supported; the reference's State.afterEventStation rejects it too");
699 for (std::size_t r = 0; r < K_ && r < sn_.
stations[i].schedparam.size(); ++r)
700 wnorm_[i][r] = num_traits<T>::to_double(sn_.
stations[i].schedparam[r]) / tot;
703 classprio_.assign(K_, 0.0);
704 for (std::size_t r = 0; r < K_; ++r) classprio_[r] = sn_.
classes[r].prio;
727void NrmEngine<T>::build_regions() {
728 const double inf = std::numeric_limits<double>::infinity();
730 fcr_any_waitq_ =
false;
731 for (
const typename qn::NetworkStruct<T>::Region& rg : sn_.
regions) {
733 f.member_node.assign(I_, 0);
734 f.ccap.assign(K_, inf);
735 f.sz.assign(K_, 1.0);
736 f.waitq.assign(K_, 0);
737 for (std::size_t i = 0; i < M_ && i < rg.members.size(); ++i) {
738 if (!rg.members[i])
continue;
740 for (std::size_t r = 0; r < K_; ++r)
741 if (rg.cap[i][r] != -1.0) f.ccap[r] = std::min(f.ccap[r], rg.cap[i][r]);
742 if (rg.cap[i][K_] != -1.0) f.gcap = std::min(f.gcap, rg.cap[i][K_]);
743 if (i < rg.maxmem.size() && rg.maxmem[i] != -1.0)
744 f.memcap = std::min(f.memcap, rg.maxmem[i]);
746 for (std::size_t r = 0; r < K_ && r < rg.size.size(); ++r)
747 f.sz[r] = num_traits<T>::to_double(rg.size[r]);
748 for (std::size_t r = 0; r < K_ && r < rg.rule.size(); ++r) {
750 if (d == qn::DropStrategy::BAS || d == qn::DropStrategy::BBS ||
751 d == qn::DropStrategy::RSRD)
753 "solver_ssa_nrm: finite capacity region '" + rg.name +
"' declares a "
754 "blocking rule for class '" + sn_.
classes[r].name +
"'; only DROP and WAITQ "
755 "are region rules in any codebase's NRM");
756 f.waitq[r] = d != qn::DropStrategy::DROP;
757 if (f.waitq[r]) fcr_any_waitq_ =
true;
759 if (rg.lincon_A.rows() > 0) {
760 f.A = Matrix<double>(rg.lincon_A.rows(), rg.lincon_A.cols(), 0.0);
761 for (std::size_t a = 0; a < rg.lincon_A.rows(); ++a)
762 for (std::size_t c = 0; c < rg.lincon_A.cols(); ++c)
763 f.A(a, c) = num_traits<T>::to_double(rg.lincon_A(a, c));
764 for (
const T& v : rg.lincon_b) f.b.push_back(num_traits<T>::to_double(v));
780std::size_t NrmEngine<T>::fcr_refusing_region(
const std::vector<double>& X, std::size_t src_node,
781 std::size_t src_class, std::size_t dst_node,
782 std::size_t dst_class)
const {
783 for (std::size_t f = 0; f < fcr_.size(); ++f) {
784 const FcrRegion& g = fcr_[f];
785 if (!g.member_node[dst_node])
continue;
786 std::vector<double> x(K_, 0.0);
787 for (std::size_t jnd = 0; jnd < I_; ++jnd)
788 if (g.member_node[jnd])
789 for (std::size_t r = 0; r < K_; ++r) x[r] += class_pop(X, jnd, r);
790 if (src_node != npos && g.member_node[src_node]) x[src_class] -= 1.0;
792 double tot = 0.0, mem = 0.0;
794 for (std::size_t r = 0; r < K_; ++r) {
796 mem += x[r] * g.sz[r];
797 if (x[r] > g.ccap[r]) bad =
true;
799 if (tot > g.gcap || mem > g.memcap) bad =
true;
800 for (std::size_t a = 0; !bad && a < g.A.rows() && a < g.b.size(); ++a) {
802 for (std::size_t c = 0; c < K_ && c < g.A.cols(); ++c) lhs += g.A(a, c) * x[c];
803 if (lhs > g.b[a]) bad =
true;
805 if (bad)
return f + 1;
811const typename qn::NetworkStruct<T>::PasParam& NrmEngine<T>::pas_of(std::size_t ist)
const {
812 const auto it = sn_.
pasparam.find(ist + 1);
813 if (it == sn_.
pasparam.end() || !it->second.svc_rate_fun)
815 "' has no service rate function mu(c); set one with set_pas");
820std::vector<double> NrmEngine<T>::pas_increments(std::size_t ist,
821 const std::vector<std::size_t>& c)
const {
822 const auto& mu = pas_of(ist).svc_rate_fun;
823 std::vector<double> inc(c.size(), 0.0);
824 std::vector<std::size_t> prefix;
826 for (std::size_t p = 0; p < c.size(); ++p) {
827 prefix.push_back(c[p] + 1);
828 const double cur = num_traits<T>::to_double(mu(prefix));
836double NrmEngine<T>::pas_rate(std::size_t ist,
const std::vector<std::size_t>& c,
837 std::size_t r)
const {
838 if (c.empty())
return 0.0;
840 std::vector<std::size_t> c1(c.size());
841 for (std::size_t p = 0; p < c.size(); ++p) c1[p] = c[p] + 1;
842 const auto& G = pas_of(ist).swap_graph;
844 for (std::size_t p = 0; p < c.size(); ++p) {
845 if (inc[p] <= 0.0)
continue;
852double NrmEngine<T>::pas_in_service(std::size_t ist,
const std::vector<std::size_t>& c,
853 std::size_t r)
const {
854 if (c.empty())
return 0.0;
857 for (std::size_t p = 0; p < c.size(); ++p)
858 if (inc[p] > 0.0 && c[p] == r) n += 1.0;
863void NrmEngine<T>::pas_tag_started(std::size_t ind,
const std::vector<std::size_t>& cold,
864 const std::vector<std::size_t>& cnew) {
865 const std::size_t ist = to_station_[ind];
868 for (std::size_t p = 0; p < inc_new.size(); ++p) {
869 if (inc_new[p] <= 0.0)
continue;
870 if (p < inc_old.size() && inc_old[p] > 0.0)
continue;
871 tag(ind, cnew[p],
false);
881void NrmEngine<T>::pas_depart(std::size_t ind, std::vector<std::size_t>& c, std::size_t r) {
882 const std::size_t ist = to_station_[ind];
884 std::vector<std::size_t> c1(c.size());
885 for (std::size_t p = 0; p < c.size(); ++p) c1[p] = c[p] + 1;
886 const auto& G = pas_of(ist).swap_graph;
887 std::vector<std::size_t> pos;
888 std::vector<double> w;
890 for (std::size_t p = 0; p < c.size(); ++p) {
891 if (inc[p] <= 0.0)
continue;
897 if (pos.empty())
return;
898 const double u = rng_.
uniform() * tot;
900 std::size_t pick = pos.back();
901 for (std::size_t x = 0; x < pos.size(); ++x) {
909 c.assign(nc.size(), 0);
910 for (std::size_t p = 0; p < nc.size(); ++p) c[p] = nc[p] - 1;
921typename NrmEngine<T>::PollCtrl NrmEngine<T>::poll_land(std::size_t ind, std::size_t q,
int mode,
922 long budget,
bool raise) {
928 if (raise) tag(ind, q - 1,
false);
929 }
else if (mode == 2) {
930 const std::vector<T>& pie = poll_info_[ind].sw_pie[q - 1];
931 std::vector<double> w(pie.size());
932 for (std::size_t k = 0; k < pie.size(); ++k) w[k] = num_traits<T>::to_double(pie[k]);
933 c.swk = rng_.
draw(w);
954void NrmEngine<T>::fcr_outcome(
const std::vector<double>& X,
const Rx& x, std::size_t dst_node,
955 std::size_t dst_class,
956 std::vector<std::vector<std::size_t>>& fcr_buf,
bool& lost,
957 bool& blocked)
const {
958 const std::size_t fref = fcr_refusing_region(X, x.node, x.cls, dst_node, dst_class);
959 if (fref == 0)
return;
960 if (fcr_[fref - 1].waitq[dst_class]) {
962 fcr_buf[fref - 1].push_back(dst_node * K_ + dst_class);
963 }
else if (std::isinf(sn_.
classes[dst_class].population)) {
978std::size_t NrmEngine<T>::fcr_release_cascade(std::vector<double>& X,
979 std::vector<std::vector<std::size_t>>& bufs,
980 std::vector<std::vector<std::size_t>>& fcr_buf,
981 std::vector<Matrix<double>>& svc,
983 std::size_t released = 0;
984 bool progress =
true;
987 for (std::size_t f = 0; f < fcr_buf.size(); ++f) {
988 if (fcr_buf[f].empty())
continue;
989 const std::size_t tok = fcr_buf[f].front();
990 const std::size_t jnd = tok / K_, r = tok % K_;
991 if (fcr_refusing_region(X, npos, r, jnd, r) != 0)
continue;
995 const std::size_t ke = buf_ph_node_[jnd] ? 0 : draw_entry_phase(jnd, r);
996 X[phoff_[jnd][r] + ke] += 1.0;
997 apply_arrival_buffer(jnd, r, X, bufs, svc, svc_changed);
998 fcr_buf[f].erase(fcr_buf[f].begin());
1007void NrmEngine<T>::build_reactions() {
1011 is_cache_node_.assign(I_, 0);
1012 cache_read_.assign(I_, std::vector<char>(K_, 0));
1013 for (std::size_t ind = 0; ind < I_; ++ind) {
1014 if (sn_.
nodes[ind].nodetype != qn::NodeType::Cache)
continue;
1015 const auto ci = sn_.
nodeparam.find(ind + 1);
1017 throw InputError(
"solver_ssa_nrm: Cache node '" + sn_.
nodes[ind].name +
1018 "' has no cache parameters");
1019 const qn::CacheParam<T>& cp = ci->second;
1020 is_cache_node_[ind] = 1;
1021 std::vector<std::size_t> rcl, rci, rco;
1023 for (std::size_t r = 0; r < K_; ++r) {
1024 if (r >= cp.pread.size() || cp.pread[r].empty())
continue;
1025 const bool is_retr = std::find(rcl.begin(), rcl.end(), r + 1) != rcl.end();
1026 const std::size_t hc = r < cp.hitclass.size() ? cp.hitclass[r] : 0;
1027 const std::size_t mc = r < cp.missclass.size() ? cp.missclass[r] : 0;
1028 if (hc == 0 && !is_retr)
continue;
1031 if (hc == r + 1 || mc == r + 1)
1033 "solver_ssa_nrm: class '" + sn_.
classes[r].name +
"' at Cache '" +
1034 sn_.
nodes[ind].name +
"' is its own hit or miss class, so the NRM's cache "
1035 "read would fire again at once; ask for method='serial'");
1036 cache_read_[ind][r] = 1;
1040 for (std::size_t ind = 0; ind < I_; ++ind) {
1041 for (std::size_t r = 0; r < K_; ++r) {
1042 for (std::size_t kk = 0; kk < nph_[ind][r]; ++kk) {
1047 x.is_buf_svc = buf_ph_class_[ind][r];
1051 x.from = buf_ph_class_[ind][r] ? phoff_[ind][r] : phoff_[ind][r] + kk;
1052 x.rate = rate_[ind][r];
1053 x.is_cache = is_cache_node_[ind] && cache_read_[ind][r];
1054 if (is_station_[ind] && nph_[ind][r] > 1) {
1055 const std::size_t ist = to_station_[ind];
1058 for (std::size_t c = 0; c < m.D1.cols(); ++c)
1059 s += num_traits<T>::to_double(m.D1(kk, c));
1066 const std::size_t ndep = rx_.size();
1069 for (std::size_t ind = 0; ind < I_; ++ind) {
1070 if (!is_station_[ind])
continue;
1071 const std::size_t ist = to_station_[ind];
1072 for (std::size_t r = 0; r < K_; ++r) {
1073 if (nph_[ind][r] <= 1 || sn_.
disabled[ist][r])
continue;
1075 for (std::size_t ka = 0; ka < nph_[ind][r]; ++ka)
1076 for (std::size_t kb = 0; kb < nph_[ind][r]; ++kb) {
1077 if (ka == kb)
continue;
1078 const double d = num_traits<T>::to_double(m.D0(ka, kb));
1079 if (!(d > 0.0))
continue;
1090 x.from = buf_ph_class_[ind][r] ? phoff_[ind][r] : phoff_[ind][r] + ka;
1102 is_poll_.assign(I_, 0);
1103 poll_info_.assign(I_, qn::PollingInfo<T>());
1104 for (std::size_t ind = 0; ind < I_; ++ind) {
1105 if (!is_station_[ind] || sched_[to_station_[ind]] != SchedStrategy::POLLING)
continue;
1108 for (std::size_t r = 0; r < K_; ++r)
1109 if (nph_[ind][r] > 1)
1111 "solver_ssa_nrm: polling station '" + sn_.
stations[to_station_[ind]].name +
1112 "' serves class '" + sn_.
classes[r].name +
"' with phase-type service. The "
1113 "NRM polling controller supports exponential service only, as the reference's "
1114 "does; ask for method='serial'");
1116 for (
bool h : poll_info_[ind].has_sw) anysw = anysw || h;
1117 if (!anysw)
continue;
1121 x.from = phoff_[ind][0];
1127 S_ = Matrix<double>(NS_, rx_.size(), 0.0);
1128 for (std::size_t k = 0; k < rx_.size(); ++k) {
1130 if (x.is_sw)
continue;
1133 S_(x.from, k) -= 1.0;
1137 if (!buf_ph_class_[x.node][x.cls]) {
1138 S_(phoff_[x.node][x.cls] + x.phase_from, k) -= 1.0;
1139 S_(phoff_[x.node][x.cls] + x.phase_to, k) += 1.0;
1143 S_(x.from, k) -= 1.0;
1144 for (std::size_t jnd = 0; jnd < I_; ++jnd)
1145 for (std::size_t s = 0; s < K_; ++s) {
1147 num_traits<T>::to_double(sn_.
rtnodes(x.node * K_ + x.cls, jnd * K_ + s));
1148 if (!(p > 0.0))
continue;
1149 if (buf_ph_class_[jnd][s]) {
1153 S_(phoff_[jnd][s], k) += p;
1155 for (std::size_t ke = 0; ke < nph_[jnd][s]; ++ke) {
1156 const double pe = pie_[jnd][s][ke];
1157 if (!(pe > 0.0))
continue;
1158 S_(phoff_[jnd][s] + ke, k) += p * pe;
1168 for (std::size_t k = 0; k < rx_.size(); ++k) {
1170 std::vector<double> Pcol(NS_, 0.0);
1171 for (std::size_t i = 0; i < NS_; ++i) {
1172 const double v = S_(i, k);
1173 Pcol[i] = v < 0.0 ? v + 1.0 : v;
1174 if (v < 0.0) x.from_slots.push_back(i);
1175 if (v > 0.0 && x.det_dest == npos) x.det_dest = i;
1177 for (std::size_t i = 0; i < NS_; ++i)
1178 if (Pcol[i] != 0.0) ++x.nnzP;
1181 for (std::size_t i = 0; i < NS_; ++i)
1182 if (Pcol[i] != 0.0) {
1183 x.to_slots.push_back(i);
1185 x.cdf.push_back(acc);
1188 if (!x.is_phase && !x.is_sw) build_state_dependent_dest(x);
1193 dep_rx_.assign(M_, std::vector<std::vector<std::size_t>>(K_));
1194 for (std::size_t k = 0; k < ndep; ++k)
1195 if (is_station_[rx_[k].node]) dep_rx_[to_station_[rx_[k].node]][rx_[k].cls].push_back(k);
1204 double closed_total = 0.0;
1205 bool any_open =
false;
1206 for (std::size_t r = 0; r < K_; ++r) {
1207 const double nj = sn_.
classes[r].population;
1213 blk_can_.assign(M_, std::vector<char>(K_, 0));
1215 for (std::size_t ist = 0; ist < M_; ++ist)
1216 for (std::size_t r = 0; r < K_; ++r) {
1217 const double nj = sn_.
classes[r].population;
1218 if (std::isinf(nj))
continue;
1219 const double ccap = sn_.
classcap[ist][r];
1220 const bool binds_st =
1221 !std::isinf(sn_.
cap[ist]) && (any_open || sn_.
cap[ist] < closed_total);
1222 const bool binds_cl = ccap > 0.0 && !std::isinf(ccap) && ccap < nj;
1223 if (binds_st || binds_cl) {
1224 blk_can_[ist][r] = 1;
1240void NrmEngine<T>::build_state_dependent_dest(Rx& x) {
1241 const qn::NodeDef& nd = sn_.
nodes[x.node];
1248 for (std::size_t s = 1; s <= K_; ++s)
1249 for (std::size_t j = 1; j <= I_; ++j) {
1250 if (!(num_traits<T>::to_double(sn_.
get_route(x.cls + 1, s, x.node + 1, j)) > 0.0))
1252 x.sd_node.push_back(j - 1);
1253 x.sd_class.push_back(s - 1);
1255 if (x.sd_node.size() < 2) {
1263 x.sd_slots.resize(x.sd_node.size());
1264 x.sd_pie.resize(x.sd_node.size());
1265 for (std::size_t d = 0; d < x.sd_node.size(); ++d) {
1266 const std::size_t jnd = x.sd_node[d], s = x.sd_class[d];
1267 if (buf_ph_class_[jnd][s]) {
1268 x.sd_slots[d].push_back(phoff_[jnd][s]);
1269 x.sd_pie[d].push_back(1.0);
1272 for (std::size_t ke = 0; ke < nph_[jnd][s]; ++ke) {
1273 const double pe = pie_[jnd][s][ke];
1275 x.sd_slots[d].push_back(phoff_[jnd][s] + ke);
1276 x.sd_pie[d].push_back(pe);
1281 const std::map<std::size_t, double>* w =
1282 nd.routing_weights.size() > x.cls ? &nd.routing_weights[x.cls] : NULL;
1284 x.sd_weight.assign(x.sd_node.size(), 0.0);
1286 for (std::size_t d = 0; d < x.sd_node.size(); ++d) {
1287 const std::map<std::size_t, double>::const_iterator it =
1288 w->find(x.sd_node[d] + 1);
1289 x.sd_weight[d] = (it == w->end()) ? 0.0 : it->second;
1290 total += x.sd_weight[d];
1292 if (!(total > 0.0)) {
1295 x.sd_weight.clear();
1298 for (std::size_t d = 0; d < x.sd_weight.size(); ++d) x.sd_weight[d] /= total;
1329std::size_t NrmEngine<T>::resolve_state_dependent_dest(std::size_t kfire,
1330 const std::vector<double>& X) {
1332 const std::size_t nd = x.sd_node.size();
1333 const std::size_t cursor_key = x.node * K_ + x.cls;
1334 std::size_t pick = 0;
1336 std::vector<double> load(nd, 0.0);
1338 for (std::size_t d = 0; d < nd; ++d) {
1339 const std::vector<double> cc = class_counts(X, x.sd_node[d]);
1340 for (
double v : cc) load[d] += v;
1341 if (d == 0 || load[d] < best) best = load[d];
1343 std::vector<std::size_t> amins;
1344 for (std::size_t d = 0; d < nd; ++d)
1345 if (load[d] == best) amins.push_back(d);
1346 pick = amins[std::min(amins.size() - 1,
1347 std::size_t(rng_.
uniform() *
double(amins.size())))];
1349 const std::size_t dsample = std::min<std::size_t>(2, nd);
1351 for (std::size_t t = 0; t < dsample; ++t) {
1352 const std::size_t cand =
1353 std::min(nd - 1, std::size_t(rng_.
uniform() *
double(nd)));
1354 const std::vector<double> cc = class_counts(X, x.sd_node[cand]);
1356 for (
double v : cc) load += v;
1357 if (t == 0 || load < best) {
1365 const std::size_t c = rr_cursor_[cursor_key];
1366 const double u = double(c % nd) / double(nd);
1369 for (std::size_t d = 0; d < nd; ++d) {
1370 acc += x.sd_weight[d];
1371 if (u < acc - 1e-12) {
1376 rr_cursor_[cursor_key] = c + 1;
1378 pick = rr_cursor_[cursor_key] % nd;
1379 rr_cursor_[cursor_key] = rr_cursor_[cursor_key] + 1;
1382 const std::vector<std::size_t>& slots = x.sd_slots[pick];
1383 if (slots.size() == 1)
return slots[0];
1384 const double u = rng_.
uniform();
1386 for (std::size_t k = 0; k + 1 < slots.size(); ++k) {
1387 acc += x.sd_pie[pick][k];
1388 if (u < acc)
return slots[k];
1390 return slots.back();
1403void NrmEngine<T>::build_dependencies() {
1404 const std::size_t nrx = rx_.size();
1405 D_.assign(nrx, std::vector<std::size_t>());
1406 for (std::size_t k = 0; k < nrx; ++k) {
1407 std::vector<bool> touched_node(I_,
false);
1408 for (std::size_t i = 0; i < NS_; ++i)
1409 if (S_(i, k) != 0.0) touched_node[slot_node_[i]] =
true;
1410 std::vector<bool> hit(nrx,
false);
1411 for (std::size_t ind = 0; ind < I_; ++ind) {
1412 if (!touched_node[ind])
continue;
1413 for (std::size_t r = 0; r < K_; ++r)
1414 for (std::size_t p = 0; p < nph_[ind][r]; ++p) {
1415 const std::size_t slot = phoff_[ind][r] + p;
1416 for (std::size_t j = 0; j < nrx; ++j)
1417 if (S_(slot, j) < 0.0) hit[j] =
true;
1420 for (std::size_t j = 0; j < nrx; ++j)
1421 if (hit[j]) D_[k].push_back(j);
1437void NrmEngine<T>::build_initial_state() {
1438 nvec0_.assign(NS_, 0.0);
1442 cache_var0_.assign(I_, std::vector<T>());
1443 for (std::size_t ind = 0; ind < I_; ++ind) {
1444 if (!is_cache_node_[ind])
continue;
1445 const qn::CacheParam<T>& cp = sn_.
nodeparam.find(ind + 1)->second;
1446 std::size_t tcc = 0;
1447 for (
int c : cp.itemcap)
1448 if (c > 0) tcc +=
static_cast<std::size_t
>(c);
1449 std::vector<std::size_t> rcl, rci, rco;
1450 const bool retr = cp.retrieval_capacity > 0 && !cp.retrieval_classes.empty();
1452 std::vector<T>& v = cache_var0_[ind];
1453 if (cp.initstate.size() > K_) {
1454 v.assign(cp.initstate.begin() +
static_cast<std::ptrdiff_t
>(K_), cp.initstate.end());
1456 for (std::size_t c = 0; c < tcc; ++c)
1457 v.push_back(num_traits<T>::from_int(
static_cast<long>(c + 1)));
1459 const std::size_t want = tcc + (retr ? cp.nitems + rcl.size() : 0);
1460 v.resize(want, num_traits<T>::from_int(0));
1462 buffers0_.assign(I_, std::vector<std::size_t>());
1463 svcph0_.assign(I_, Matrix<double>());
1464 for (std::size_t i = 0; i < I_; ++i)
1465 if (buf_ph_node_[i]) svcph0_[i] = Matrix<double>(K_, maxnph_, 0.0);
1467 std::vector<std::vector<double>> nir(I_, std::vector<double>(K_, 0.0));
1468 for (std::size_t r = 0; r < K_; ++r) {
1469 const double pop = sn_.
classes[r].population;
1470 if (std::isinf(pop)) {
1473 "solver_ssa_nrm: the open class '" + sn_.
classes[r].name +
1474 "' has no Source; an open model must carry one for the arrival reaction");
1476 }
else if (pop > 0.0) {
1477 const std::size_t rs = sn_.
classes[r].refstat;
1478 if (rs < 1 || rs > M_)
1480 "' has no reference station");
1485 for (std::size_t ind = 0; ind < I_; ++ind) {
1486 for (std::size_t r = 0; r < K_; ++r) {
1487 const double n = nir[ind][r];
1488 if (n <= 0.0)
continue;
1489 if (nph_[ind][r] <= 1 || buf_ph_class_[ind][r]) {
1490 nvec0_[phoff_[ind][r]] = n;
1493 for (std::size_t ke = 0; ke < nph_[ind][r]; ++ke) {
1494 const double take = (ke + 1 == nph_[ind][r])
1496 : std::min(left, std::round(n * pie_[ind][r][ke]));
1497 nvec0_[phoff_[ind][r] + ke] = take;
1503 if (is_station_[ind] && detail::sched_is_list(sched_[to_station_[ind]]))
1504 for (std::size_t r = 0; r < K_; ++r)
1505 for (std::size_t c = 0; c < static_cast<std::size_t>(nir[ind][r]); ++c)
1506 buffers0_[ind].push_back(r);
1510 if (is_station_[ind] && detail::sched_is_buffered(sched_[to_station_[ind]])) {
1512 for (std::size_t r = 0; r < K_; ++r) total += nir[ind][r];
1513 double waiting = std::max(0.0, total - mi_[ind]);
1514 for (std::size_t r = 0; r < K_ && waiting > 0.0; ++r) {
1515 double take = std::min(waiting, nir[ind][r]);
1516 for (std::size_t c = 0; c < static_cast<std::size_t>(take); ++c)
1517 buffers0_[ind].push_back(r);
1521 if (!buf_ph_node_[ind])
continue;
1522 for (std::size_t r = 0; r < K_; ++r) {
1523 double waiting_r = 0.0;
1524 for (std::size_t c : buffers0_[ind])
1525 if (c == r) waiting_r += 1.0;
1526 const double insvc = std::max(0.0, nir[ind][r] - waiting_r);
1527 if (nph_[ind][r] <= 1) {
1528 svcph0_[ind](r, 0) = insvc;
1530 double left = insvc;
1531 for (std::size_t ke = 0; ke < nph_[ind][r]; ++ke) {
1532 const double take = (ke + 1 == nph_[ind][r])
1534 : std::min(left, std::round(insvc * pie_[ind][r][ke]));
1535 svcph0_[ind](r, ke) = take;
1556double NrmEngine<T>::propensity(std::size_t j,
const std::vector<double>& X,
1557 const std::vector<std::vector<std::size_t>>& bufs,
1558 const std::vector<Matrix<double>>& svc)
const {
1559 const Rx& x = rx_[j];
1560 const std::size_t ind = x.node, r = x.cls;
1561 if (!is_station_[ind])
1562 return x.rate * kir_frac(X, x.from, ind, r) * std::min(1.0, class_pop(X, ind, r));
1564 const std::size_t ist = to_station_[ind];
1570 const PollCtrl& c = poll_ctrl_[ind];
1571 if (c.mode != 2)
return 0.0;
1572 return -num_traits<T>::to_double(poll_info_[ind].sw_d0[c.pos - 1](c.swk, c.swk));
1577 if (buf_ph_class_[ind][r]) {
1578 const std::size_t kk = x.is_phase ? x.phase_from : x.dep_phase;
1579 const std::vector<double> n = class_counts(X, ind);
1581 for (
double v : n) tot += v;
1582 return x.rate * svc[ind](r, kk) * lldfac(ist, tot) * cdfac(ist, n, r);
1585 const double kf = kir_frac(X, x.from, ind, r);
1586 switch (sched_[ist]) {
1587 case SchedStrategy::EXT:
1592 case SchedStrategy::INF:
1593 return x.rate * kf * class_pop(X, ind, r) * cdfac(ist, class_counts(X, ind), r);
1594 case SchedStrategy::PS:
1595 case SchedStrategy::LPS: {
1597 return x.rate * kf * std::min(mi_[ind], class_pop(X, ind, r)) *
1598 lldfac(ist, class_pop(X, ind, r)) * cdfac(ist, class_counts(X, ind), r);
1599 const std::vector<double> n = class_counts(X, ind);
1601 for (
double v : n) tot += v;
1602 return x.rate * kf * (n[r] / (eps + tot)) * std::min(mi_[ind], eps + tot) *
1603 lldfac(ist, tot) * cdfac(ist, n, r);
1605 case SchedStrategy::DPS: {
1606 const std::vector<double> n = class_counts(X, ind);
1608 for (
double v : n) tot += v;
1609 return x.rate * kf * dpsshare(wnorm_[ist], n, r) * lldfac(ist, tot) *
1612 case SchedStrategy::GPS: {
1613 const std::vector<double> n = class_counts(X, ind);
1615 for (
double v : n) tot += v;
1616 return x.rate * kf * gpsshare(wnorm_[ist], n, r) * lldfac(ist, tot) *
1624 case SchedStrategy::PSPRIO: {
1625 const std::vector<double> n = class_counts(X, ind);
1627 for (
double v : n) tot += v;
1628 return x.rate * kf * psprioshare(n, r, mi_[ind]) * lldfac(ist, tot) *
1631 case SchedStrategy::DPSPRIO: {
1632 const std::vector<double> n = class_counts(X, ind);
1633 return x.rate * kf * dpsprioshare(wnorm_[ist], n, r, mi_[ind]) *
1634 lldfac(ist,
prio_pop(n, r, mi_[ind])) *
1635 cdfac(ist, prio_vec(n, r, mi_[ind]), r);
1637 case SchedStrategy::GPSPRIO: {
1638 const std::vector<double> n = class_counts(X, ind);
1639 return x.rate * kf * gpsprioshare(wnorm_[ist], n, r, mi_[ind]) *
1640 lldfac(ist,
prio_pop(n, r, mi_[ind])) *
1641 cdfac(ist, prio_vec(n, r, mi_[ind]), r);
1643 case SchedStrategy::FCFS:
1644 case SchedStrategy::LCFS:
1645 case SchedStrategy::SIRO:
1646 case SchedStrategy::HOL:
1647 case SchedStrategy::SEPT:
1648 case SchedStrategy::LEPT:
1649 case SchedStrategy::LCFSPR: {
1652 double waiting = 0.0;
1653 for (std::size_t c : bufs[ind])
1654 if (c == r) waiting += 1.0;
1655 const std::vector<double> n = class_counts(X, ind);
1657 for (
double v : n) tot += v;
1658 return x.rate * kf * std::max(0.0, n[r] - waiting) * lldfac(ist, tot) *
1661 case SchedStrategy::POLLING: {
1665 const PollCtrl& c = poll_ctrl_[ind];
1666 if (c.mode != 1 || c.pos != r + 1)
return 0.0;
1667 const std::vector<double> n = class_counts(X, ind);
1669 for (
double v : n) tot += v;
1670 return x.rate * lldfac(ist, tot) * cdfac(ist, n, r);
1672 case SchedStrategy::PAS:
1673 case SchedStrategy::OI:
1677 return pas_rate(ist, bufs[ind], r);
1680 "solver_ssa_nrm: the scheduling policy '" +
1682 sn_.
stations[ist].name +
"' has no NRM rate law in this port");
1692std::size_t NrmEngine<T>::pick_from_buffer(
const std::vector<std::size_t>& buf, std::size_t ist) {
1693 switch (sched_[ist]) {
1694 case SchedStrategy::FCFS:
1695 return buf.size() - 1;
1696 case SchedStrategy::LCFS:
1697 case SchedStrategy::LCFSPR:
1699 case SchedStrategy::SIRO:
1700 return rng_.
index(buf.size());
1701 case SchedStrategy::HOL: {
1704 double best = std::numeric_limits<double>::infinity();
1705 for (std::size_t c : buf) best = std::min(best, classprio_[c]);
1706 for (std::size_t p = buf.size(); p-- > 0;)
1707 if (classprio_[buf[p]] == best)
return p;
1708 return buf.size() - 1;
1710 case SchedStrategy::SEPT:
1711 case SchedStrategy::LEPT: {
1715 double best = std::numeric_limits<double>::infinity();
1716 for (std::size_t c : buf)
1717 best = std::min(best, num_traits<T>::to_double(sn_.
stations[ist].schedparam[c]));
1718 for (std::size_t p = buf.size(); p-- > 0;)
1719 if (num_traits<T>::to_double(sn_.
stations[ist].schedparam[buf[p]]) == best)
1721 return buf.size() - 1;
1726 "' is not a buffered policy this port promotes from");
1732std::size_t NrmEngine<T>::draw_entry_phase(std::size_t ind, std::size_t r) {
1733 if (nph_[ind][r] <= 1)
return 0;
1734 return rng_.
draw(pie_[ind][r]);
1747bool NrmEngine<T>::capacity_loss(
const std::vector<double>& X, std::size_t slot)
const {
1748 const std::size_t jnd = slot_node_[slot], dst = slot_class_[slot];
1749 if (!is_station_[jnd])
return false;
1750 const std::size_t ist = to_station_[jnd];
1752 if (dr == qn::DropStrategy::WAITQ)
return false;
1753 if (dr == qn::DropStrategy::BAS || dr == qn::DropStrategy::BBS ||
1754 dr == qn::DropStrategy::RSRD)
1756 "' declares the blocking drop rule for class '" +
1758 "'; blocking-after-service is not ported to the C++ NRM");
1759 if (!std::isinf(sn_.
classes[dst].population))
return false;
1760 const std::vector<double> cc = class_counts(X, jnd);
1762 for (
double v : cc) tot += v;
1763 if (!std::isinf(sn_.
cap[ist]) && tot >= sn_.
cap[ist])
return true;
1764 const double ccap = sn_.
classcap[ist][dst];
1765 return ccap > 0.0 && !std::isinf(ccap) && cc[dst] >= ccap;
1794bool NrmEngine<T>::capacity_block(
const std::vector<double>& X, std::size_t slot,
1795 std::size_t src_slot)
const {
1796 if (!blk_on_)
return false;
1797 const std::size_t jnd = slot_node_[slot], dst = slot_class_[slot];
1798 if (!is_station_[jnd])
return false;
1799 const std::size_t ist = to_station_[jnd];
1800 if (!blk_can_[ist][dst])
return false;
1801 const bool same_node = src_slot != npos && slot_node_[src_slot] == jnd;
1802 const std::vector<double> cc = class_counts(X, jnd);
1803 if (!std::isinf(sn_.
cap[ist])) {
1805 for (
double v : cc) tot += v;
1806 if (same_node) tot -= 1.0;
1807 if (tot >= sn_.
cap[ist])
return true;
1809 const double ccap = sn_.
classcap[ist][dst];
1810 if (ccap > 0.0 && !std::isinf(ccap)) {
1811 double pop = cc[dst];
1812 if (same_node && slot_class_[src_slot] == dst) pop -= 1.0;
1813 if (pop >= ccap)
return true;
1820std::size_t NrmEngine<T>::pick_preempted(
const std::vector<double>& X,
1821 const std::vector<std::size_t>& buf, std::size_t jnd,
1822 std::size_t arr_class) {
1823 std::vector<double> insvc = class_counts(X, jnd);
1824 for (std::size_t r = 0; r < K_; ++r) {
1825 for (std::size_t c : buf)
1826 if (c == r) insvc[r] -= 1.0;
1827 if (r == arr_class) insvc[r] -= 1.0;
1828 if (insvc[r] < 0.0) insvc[r] = 0.0;
1831 for (
double v : insvc) tot += v;
1832 if (tot <= 0.0)
return npos;
1833 const double u = rng_.
uniform() * tot;
1835 std::size_t last = npos;
1836 for (std::size_t r = 0; r < K_; ++r) {
1837 if (!(insvc[r] > 0.0))
continue;
1840 if (u < acc)
return r;
1847void NrmEngine<T>::apply_arrival_buffer(std::size_t jnd, std::size_t s,
1848 const std::vector<double>& X,
1849 std::vector<std::vector<std::size_t>>& bufs,
1850 std::vector<Matrix<double>>& svc,
bool& svc_changed) {
1851 if (is_station_[jnd] && detail::sched_is_list(sched_[to_station_[jnd]])) {
1855 const std::vector<std::size_t> old = bufs[jnd];
1856 bufs[jnd].push_back(s);
1861 if (!is_poll_.empty() && is_poll_[jnd])
return;
1862 if (!is_station_[jnd] || !detail::sched_is_buffered(sched_[to_station_[jnd]])) {
1865 if (is_station_[jnd]) tag(jnd, s,
false);
1868 const std::vector<double> cc = class_counts(X, jnd);
1870 for (
double v : cc) total += v;
1871 bool entered =
false;
1872 if (total > mi_[jnd]) {
1873 if (sched_[to_station_[jnd]] == SchedStrategy::LCFSPR) {
1877 const std::size_t c = pick_preempted(X, bufs[jnd], jnd, s);
1879 bufs[jnd].insert(bufs[jnd].begin(), c);
1884 bufs[jnd].insert(bufs[jnd].begin(), s);
1889 if (entered) tag(jnd, s,
false);
1890 if (entered && buf_ph_node_[jnd]) {
1891 svc[jnd](s, draw_entry_phase(jnd, s)) += 1.0;
1917void NrmEngine<T>::cache_fire(
const Rx& x, std::vector<double>& X, std::size_t& dest_pos,
1919 const std::size_t ind = x.node, r = x.cls;
1920 const qn::CacheParam<T>& cp = sn_.
nodeparam.find(ind + 1)->second;
1921 std::vector<T> row(K_, num_traits<T>::from_int(0));
1922 row[r] = num_traits<T>::from_int(1);
1923 row.insert(row.end(), cache_var_[ind].begin(), cache_var_[ind].end());
1924 const qn::EventOutcome<T> o =
1926 std::vector<double> w(o.space.size(), 0.0);
1928 for (std::size_t j = 0; j < o.space.size(); ++j) {
1929 w[j] = num_traits<T>::to_double(o.rate[j]) * num_traits<T>::to_double(o.prob[j]);
1930 if (!(w[j] > 0.0)) w[j] = 0.0;
1935 "' at Cache '" + sn_.
nodes[ind].name +
"' has no enabled outcome");
1936 const std::vector<T>& nx = o.space[rng_.
draw(w)];
1938 cache_var_[ind].assign(nx.begin() +
static_cast<std::ptrdiff_t
>(K_), nx.end());
1940 std::vector<std::size_t> rcl, rci, rco;
1942 const bool from_retr = std::find(rcl.begin(), rcl.end(), r + 1) != rcl.end();
1943 double produced = 0.0;
1944 for (std::size_t c = 0; c < K_; ++c) produced += num_traits<T>::to_double(nx[c]);
1945 if (produced == 0.0) {
1946 cache_dly_(ind, r) += 1.0;
1949 for (std::size_t c = 0; c < K_; ++c) {
1950 const double d = num_traits<T>::to_double(nx[c]);
1951 if (!(d > 0.0))
continue;
1952 const bool begins = !from_retr && std::find(rcl.begin(), rcl.end(), c + 1) != rcl.end();
1954 X[phoff_[ind][c]] += d;
1955 cache_prod_(ind, c) += d;
1957 dest_pos = phoff_[ind][c];
1963 std::size_t tcc = 0;
1964 for (
int m : cp.itemcap)
1965 if (m > 0) tcc +=
static_cast<std::size_t
>(m);
1966 for (std::size_t it = 0; it < cp.retrieval_classes.size(); ++it)
1967 if (r < cp.retrieval_classes[it].size() && cp.retrieval_classes[it][r] == c + 1 &&
1968 tcc + it < cache_var_[ind].size())
1969 cache_var_[ind][tcc + it] = num_traits<T>::from_int(1);
1972 std::vector<std::size_t> slots;
1973 std::vector<double> sw;
1974 for (std::size_t jnd = 0; jnd < I_; ++jnd)
1975 for (std::size_t s = 0; s < K_; ++s) {
1977 num_traits<T>::to_double(sn_.
rtnodes(ind * K_ + c, jnd * K_ + s));
1978 if (!(p > 0.0))
continue;
1979 if (buf_ph_class_[jnd][s]) {
1980 slots.push_back(phoff_[jnd][s]);
1984 for (std::size_t ke = 0; ke < nph_[jnd][s]; ++ke)
1985 if (pie_[jnd][s][ke] > 0.0) {
1986 slots.push_back(phoff_[jnd][s] + ke);
1987 sw.push_back(p * pie_[jnd][s][ke]);
1992 "' at Cache '" + sn_.
nodes[ind].name +
"' routes nowhere");
1993 const std::size_t slot = slots[rng_.
draw(sw)];
2007void NrmEngine<T>::cache_write_back() {
2009 const double nan = std::numeric_limits<double>::quiet_NaN();
2010 for (std::size_t ind = 0; ind < I_; ++ind) {
2011 if (!is_cache_node_[ind])
continue;
2012 const qn::CacheParam<T>& cp = sn_.
nodeparam.find(ind + 1)->second;
2015 cr.hitprob.assign(K_, nan);
2016 cr.missprob.assign(K_, nan);
2017 cr.residt.assign(K_, nan);
2018 std::vector<double> dly(K_, 0.0);
2019 bool any_delayed =
false;
2020 for (std::size_t k = 0; k < K_; ++k) {
2021 if (k >= cp.hitclass.size() || k >= cp.missclass.size())
continue;
2022 const std::size_t h = cp.hitclass[k], mi = cp.missclass[k];
2023 if (h == 0 || mi == 0 || h > K_ || mi > K_)
continue;
2024 const double th = cache_prod_(ind, h - 1), tm = cache_prod_(ind, mi - 1);
2025 const double td = cache_dly_(ind, k);
2026 if (th + tm > 0.0) {
2027 cr.hitprob[k] = std::max(th - td, 0.0) / (th + tm);
2028 cr.missprob[k] = tm / (th + tm);
2029 dly[k] = td / (th + tm);
2030 if (td > 0.0) any_delayed =
true;
2033 if (any_delayed) cr.delayedprob = dly;
2034 cache_out_.push_back(cr);
2040void NrmEngine<T>::update_buffers(std::size_t kfire,
const std::vector<double>& X,
2041 std::vector<std::vector<std::size_t>>& bufs,
2042 std::size_t dest_pos,
bool have_dest,
2043 std::vector<Matrix<double>>& svc,
bool& svc_changed) {
2044 const Rx& x = rx_[kfire];
2045 const std::size_t ind = x.node;
2048 if (buf_ph_node_[ind] && x.is_buf_svc && !x.is_phase) {
2049 svc[ind](x.cls, x.dep_phase) -= 1.0;
2057 if (is_station_[ind] && detail::sched_is_list(sched_[to_station_[ind]]) &&
2058 !bufs[ind].empty() && !x.is_phase) {
2059 const std::vector<std::size_t> old = bufs[ind];
2060 pas_depart(ind, bufs[ind], x.cls);
2062 }
else if (is_station_[ind] && detail::sched_is_buffered(sched_[to_station_[ind]]) &&
2063 !bufs[ind].empty() && !x.is_phase) {
2064 const std::size_t pos = pick_from_buffer(bufs[ind], to_station_[ind]);
2065 const std::size_t promoted = bufs[ind][pos];
2066 bufs[ind].erase(bufs[ind].begin() +
static_cast<std::ptrdiff_t
>(pos));
2067 tag(ind, promoted,
false);
2068 if (buf_ph_node_[ind]) {
2069 svc[ind](promoted, draw_entry_phase(ind, promoted)) += 1.0;
2074 apply_arrival_buffer(slot_node_[dest_pos], slot_class_[dest_pos], X, bufs, svc,
2085 const std::size_t nrx = rx_.size();
2091 out.
CN.assign(K_, 0.0);
2092 out.
XN.assign(K_, 0.0);
2099 if (nrx == 0)
return out;
2101 std::vector<double> nvec = nvec0_;
2102 std::vector<std::vector<std::size_t>> bufs = buffers0_;
2103 std::vector<Matrix<double>> svc = svcph0_;
2105 std::vector<std::vector<std::size_t>> fcr_buf(fcr_.size());
2106 cache_var_ = cache_var0_;
2111 poll_ctrl_.assign(I_, PollCtrl());
2112 for (std::size_t ind = 0; ind < I_; ++ind) {
2113 if (!is_poll_[ind])
continue;
2117 qn::polling_next(poll_info_[ind], 1, poll_nbuf(nvec, ind), K_,
true, q, mode, budget);
2118 poll_ctrl_[ind] = poll_land(ind, q, mode, budget,
false);
2121 std::vector<double> Ak(nrx, 0.0), Pk(nrx, 0.0), Tk(nrx, 0.0), tau(nrx, 0.0);
2122 for (std::size_t k = 0; k < nrx; ++k) {
2123 Ak[k] = propensity(k, nvec, bufs, svc);
2124 Pk[k] = -std::log(rng_.uniform());
2125 tau[k] = Ak[k] > 0.0 ? (Pk[k] - Tk[k]) / Ak[k] : std::numeric_limits<double>::infinity();
2128 double total_time = 0.0;
2131 static_cast<std::size_t
>(opt_.samples));
2132 const std::size_t console_every = std::max<std::size_t>(1, opt_.samples / 20);
2133 for (; n < opt_.samples; ++n) {
2134 if ((n + 1) % console_every == 0)
2136 static_cast<long>((n + 1) / console_every),
2137 "simulated %zu of %zu samples (%.0f%%), simulated time %.4g", n + 1,
2138 static_cast<std::size_t
>(opt_.samples),
2139 100.0 *
static_cast<double>(n + 1) /
static_cast<double>(opt_.samples),
2141 std::size_t kfire = 0;
2142 double dt = std::numeric_limits<double>::infinity();
2143 for (std::size_t k = 0; k < nrx; ++k)
2150 "solver_ssa_nrm: deadlock -- every reaction has propensity zero, so the sample "
2151 "path cannot advance");
2155 for (std::size_t ist = 0; ist < M_; ++ist) {
2156 const std::size_t ind = sn_.station_to_node[ist] - 1;
2157 const std::vector<double> npop = class_counts(nvec, ind);
2158 double totpop = 0.0;
2159 for (
double v : npop) totpop += v;
2172 const bool is_source = sched_[ist] == SchedStrategy::EXT;
2173 for (std::size_t k = 0; k < K_; ++k) {
2175 for (std::size_t jd : dep_rx_[ist][k]) dep += Ak[jd];
2176 out.
TN(ist, k) += dep * dt;
2177 if (is_source)
continue;
2178 out.
QN(ist, k) += npop[k] * dt;
2179 switch (sched_[ist]) {
2180 case SchedStrategy::INF:
2181 out.
UN(ist, k) += npop[k] * dt;
2183 case SchedStrategy::PS:
2184 case SchedStrategy::LPS:
2186 out.
UN(ist, k) += (npop[k] / totpop) *
2187 std::min(nservers_[ist], totpop) / nservers_[ist] *
2190 case SchedStrategy::DPS:
2191 out.
UN(ist, k) += dpsshare(wnorm_[ist], npop, k) / nservers_[ist] * dt;
2193 case SchedStrategy::GPS:
2194 out.
UN(ist, k) += gpsshare(wnorm_[ist], npop, k) / nservers_[ist] * dt;
2201 case SchedStrategy::PSPRIO:
2202 out.
UN(ist, k) += psprioshare(npop, k, nservers_[ist]) / nservers_[ist] * dt;
2204 case SchedStrategy::DPSPRIO:
2206 dpsprioshare(wnorm_[ist], npop, k, nservers_[ist]) / nservers_[ist] * dt;
2208 case SchedStrategy::GPSPRIO:
2210 gpsprioshare(wnorm_[ist], npop, k, nservers_[ist]) / nservers_[ist] * dt;
2213 case SchedStrategy::POLLING:
2214 if (poll_ctrl_[ind].mode == 1 && poll_ctrl_[ind].pos == k + 1)
2215 out.
UN(ist, k) += dt / nservers_[ist];
2219 case SchedStrategy::PAS:
2220 case SchedStrategy::OI:
2221 out.
UN(ist, k) += pas_in_service(ist, bufs[ind], k) / nservers_[ist] * dt;
2223 case SchedStrategy::FCFS:
2224 case SchedStrategy::LCFS:
2225 case SchedStrategy::SIRO:
2226 case SchedStrategy::HOL:
2227 case SchedStrategy::SEPT:
2228 case SchedStrategy::LEPT:
2229 case SchedStrategy::LCFSPR: {
2230 if (sn_.disabled[ist][k])
break;
2231 double waiting = 0.0;
2232 for (std::size_t c : bufs[ind])
2233 if (c == k) waiting += 1.0;
2234 out.
UN(ist, k) += ((npop[k] - waiting) / nservers_[ist]) * dt;
2244 const Rx& x = rx_[kfire];
2245 std::size_t dest_pos = npos;
2246 bool have_dest =
false;
2250 bool blocked =
false;
2252 cache_fire(x, nvec, dest_pos, have_dest);
2253 }
else if (x.nnzP > 1 || !x.sd_node.empty()) {
2255 if (!x.sd_node.empty()) {
2257 slot = resolve_state_dependent_dest(kfire, nvec);
2259 const double u = rng_.uniform();
2260 std::size_t sel = x.cdf.size() - 1;
2261 for (std::size_t i = 0; i < x.cdf.size(); ++i)
2266 slot = x.to_slots[sel];
2271 if (capacity_block(nvec, slot, x.from)) {
2277 bool lost = capacity_loss(nvec, slot);
2280 if (!lost && !fcr_.empty())
2281 fcr_outcome(nvec, x, slot_node_[slot], slot_class_[slot], fcr_buf, lost, blocked);
2283 for (std::size_t s : x.from_slots) nvec[s] -= 1.0;
2291 }
else if (x.det_dest != npos && !x.is_phase &&
2292 capacity_block(nvec, x.det_dest, x.from)) {
2295 bool lost = x.det_dest != npos && !x.is_phase && capacity_loss(nvec, x.det_dest);
2298 if (!lost && x.det_dest != npos && !x.is_phase && !fcr_.empty())
2299 fcr_outcome(nvec, x, slot_node_[x.det_dest], slot_class_[x.det_dest], fcr_buf, lost,
2304 nvec[x.from] -= 1.0;
2306 for (std::size_t i = 0; i < NS_; ++i)
2307 if (S_(i, kfire) != 0.0) nvec[i] += S_(i, kfire);
2308 if (x.det_dest != npos) {
2309 dest_pos = x.det_dest;
2315 bool svc_changed =
false;
2318 if (is_station_[x.node]) block_cnt_(to_station_[x.node], x.cls) += 1.0;
2319 }
else if (x.is_phase && buf_ph_node_[x.node]) {
2322 svc[x.node](x.cls, x.phase_from) -= 1.0;
2323 svc[x.node](x.cls, x.phase_to) += 1.0;
2325 }
else if (!x.is_sw) {
2326 update_buffers(kfire, nvec, bufs, dest_pos, have_dest, svc, svc_changed);
2329 if (x.is_cache) svc_changed =
true;
2336 if (!is_poll_.empty() && !blocked) {
2338 const std::size_t ind = x.node;
2339 PollCtrl& c = poll_ctrl_[ind];
2340 const Matrix<T>& D0 = poll_info_[ind].sw_d0[c.pos - 1];
2341 const std::size_t Ksw = D0.
rows();
2342 std::vector<double> w(Ksw + 1, 0.0);
2343 double rowsum = 0.0;
2344 for (std::size_t kd = 0; kd < Ksw; ++kd) {
2347 if (kd != c.swk && v > 0.0) w[kd] = v;
2349 w[Ksw] = std::max(0.0, -rowsum);
2350 const std::size_t pick = rng_.draw(w);
2351 if (pick < Ksw && pick != c.swk) {
2354 std::size_t q = c.pos;
2357 qn::polling_next(poll_info_[ind], c.pos, poll_nbuf(nvec, ind), K_,
true, q,
2359 c = poll_land(ind, q, mode, budget);
2362 }
else if (is_poll_[x.node] && !x.is_phase) {
2363 const std::size_t ind = x.node;
2364 PollCtrl& c = poll_ctrl_[ind];
2366 const std::vector<long> nb = poll_nbuf(nvec, ind);
2371 goon = nb[c.pos - 1] > 0;
2374 ctrnext = c.ctr - 1;
2378 ctrnext = c.ctr - 1;
2379 goon = ctrnext > 0 && nb[c.pos - 1] > 0;
2383 goon = nb[c.pos - 1] > c.ctr;
2389 c = poll_land(ind, c.pos, 1, ctrnext);
2391 std::size_t q = c.pos;
2395 c = poll_land(ind, q, mode, budget);
2399 if (have_dest && is_poll_[slot_node_[dest_pos]]) {
2400 const std::size_t jnd = slot_node_[dest_pos];
2401 PollCtrl& c = poll_ctrl_[jnd];
2403 std::size_t q = c.pos;
2406 qn::polling_next(poll_info_[jnd], c.pos, poll_nbuf(nvec, jnd), K_,
true, q,
2408 c = poll_land(jnd, q, mode, budget);
2417 if (fcr_any_waitq_ && !blocked &&
2418 fcr_release_cascade(nvec, bufs, fcr_buf, svc, svc_changed) > 0)
2421 for (std::size_t k = 0; k < nrx; ++k) Tk[k] += Ak[k] * dt;
2426 for (std::size_t k = 0; k < nrx; ++k) Ak[k] = propensity(k, nvec, bufs, svc);
2428 for (std::size_t k : D_[kfire]) Ak[k] = propensity(k, nvec, bufs, svc);
2431 Pk[kfire] -= std::log(rng_.uniform());
2432 for (std::size_t k = 0; k < nrx; ++k)
2434 Ak[k] > 0.0 ? (Pk[k] - Tk[k]) / Ak[k] : std::numeric_limits<double>::infinity();
2437 if (total_time > 0.0)
2438 for (std::size_t ist = 0; ist < M_; ++ist)
2439 for (std::size_t k = 0; k < K_; ++k) {
2440 out.
QN(ist, k) /= total_time;
2441 out.
UN(ist, k) /= total_time;
2443 out.
TN(ist, k) = (out.
TN(ist, k) - block_cnt_(ist, k)) / total_time;
2447 out.
StartN(ist, k) = start_cnt_(ist, k) / total_time;
2448 out.
PreemptN(ist, k) = preempt_cnt_(ist, k) / total_time;
2455 for (std::size_t ist = 0; ist < M_; ++ist) {
2456 const auto& st = sn_.stations[ist];
2457 if (!st.cdscaling && !st.jdscaling)
continue;
2458 for (std::size_t k = 0; k < K_; ++k) {
2463 out.
UN(ist, k) = (std::isfinite(rate) && rate > 0.0 && peak > 0.0)
2464 ? out.
TN(ist, k) / rate / peak
2477 for (std::size_t ist = 0; ist < M_; ++ist) {
2478 if (lld_[ist].empty())
continue;
2479 double peak = nservers_[ist];
2480 bool non_unit =
false;
2481 for (
double a : lld_[ist]) {
2482 if (a != 1.0) non_unit =
true;
2483 if (a > peak) peak = a;
2485 if (!non_unit)
continue;
2486 if (sched_[ist] == SchedStrategy::INF || sched_[ist] == SchedStrategy::EXT)
2488 for (std::size_t k = 0; k < K_; ++k) {
2490 out.
UN(ist, k) = (std::isfinite(rate) && rate > 0.0 && peak > 0.0)
2491 ? out.
TN(ist, k) / rate / peak
2496 for (std::size_t k = 0; k < K_; ++k) {
2497 out.
XN[k] = out.
TN(sn_.classes[k].refstat - 1, k);
2498 for (std::size_t ist = 0; ist < M_; ++ist)
2499 out.
RN(ist, k) = out.
TN(ist, k) > 0.0 ? out.
QN(ist, k) / out.
TN(ist, k) : 0.0;
2500 if (out.
XN[k] > 0.0) out.
CN[k] = sn_.classes[k].population / out.
XN[k];
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::size_t sourceIdx
1-based station index of the Source, 0 = none
T get_route(std::size_t r, std::size_t s, std::size_t i, std::size_t j) const
P{r,s}(i,j), AS THE USER SET IT.
std::size_t nof_nodes() const
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
std::vector< std::vector< bool > > disabled
std::map< std::size_t, CacheParam< T > > nodeparam
Cache parameters by 1-based NODE index; only Cache nodes have an entry.
std::map< std::size_t, PasParam > pasparam
std::vector< double > cap
sn.cap and sn.classcap: the total and per-class buffers.
std::vector< JobClass > classes
std::vector< Station< T > > stations
stations[k-1] is the k-th station
Matrix< T > rates
(nstations x nclasses) service rates and SCVs, with a PARALLEL disabled flag instead of MATLAB's NaN ...
std::vector< NodeDef > nodes
every node, in creation order
std::vector< std::vector< double > > classcap
std::vector< std::vector< DropStrategy > > droprule
std::vector< std::size_t > station_to_node
(nstations) 1-based node index
std::vector< Region > regions
std::size_t nreactions() const
The reaction count, likewise.
NrmEngine(const qn::NetworkStruct< T > &sn, const SsaOptions &opt)
std::size_t nstates() const
The state-vector length, for the tests that assert the phase expansion.
const std::vector< SsaCacheRatio > & cache() const
The cache write-back of the last run(), one entry per Cache node.
SsaSolution run()
Run opt.samples firings and return the time-averaged metrics.
The uniform source, MATLAB's rand.
std::size_t draw(const std::vector< double > &p)
Index drawn from the unnormalized nonnegative weights p, the reference's drawFromDist: an all-zero we...
std::size_t index(std::size_t n)
Uniform index in [0, n), the reference's 1 + floor(rand*n).
static void loop(const char *fmt,...)
Announce an iteration loop and reset its reporting budget.
static void iter(long k, const char *fmt,...)
Report iteration k of the current loop.
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Running progress log of a LINE solver run (the "solver console").
Dense matrix and non-owning view.
mam::Map< T > dist_to_map(const Distrib< T > &d)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
DropStrategy
Blocking and loss rules, with the values of MATLAB DropStrategy.
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
@ READ
a cache item is read
@ KLIMITED
serve at most K per visit (K in pollingPar)
@ EXHAUSTIVE
serve until the queue empties
@ GATED
serve exactly the jobs present at the polling instant
@ DECREMENTING
serve until the queue is one shorter than at arrival
std::vector< T > dist_pie(const Distrib< T > &d)
sn.pie: the phase distribution seen by an arriving job.
const char * sched_to_text(SchedStrategy s)
void cache_retrieval_class_map(const CacheParam< T > &cp, std::vector< std::size_t > &rc_list, std::vector< std::size_t > &rc_items, std::vector< std::size_t > &rc_orig)
Port of State.cacheRetrievalClassMap: the canonical order of a cache's retrieval classes,...
void polling_next(const PollingInfo< T > &pi, std::size_t pos, const std::vector< long > &nbuf, std::size_t R, bool arrived, std::size_t &q, int &mode, long &budget)
Port of State.pollingNext: where the server goes from buffer pos.
EventOutcome< T > after_event_cache(const NetworkStruct< T > &sn, std::size_t ind, const std::vector< T > &inspace, EventType event, std::size_t cls)
Port of State.afterEventCache: events at a Cache node.
PrioPop< T > prio_pop(const NetworkStruct< T > &sn, std::size_t ist, const Marginal< T > &m, std::size_t cls, double ni, double S)
Compute the *PRIO effective population; a no-op for every other discipline.
void pas_tag_started(EventOutcome< T > &out, const F &mu_fun, const std::vector< std::size_t > &cold, const std::vector< std::size_t > &cnew)
Tag the successor just appended to OUT with the PAS positions that started service on it: those of CN...
std::pair< std::vector< std::size_t >, std::size_t > pass_and_swap(const std::vector< std::size_t > &c, std::size_t p, const std::vector< std::vector< bool > > &G)
Port of State.passAndSwap: the transition a service completion triggers at a pass-and-swap station (D...
std::vector< double > pas_increments(const F &mu_fun, const std::vector< std::size_t > &c)
Port of State.afterEventStationPAS: events at a pass-and-swap station.
PollingInfo< T > polling_info(const NetworkStruct< T > &sn, std::size_t ind)
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
State.pollingInfo and the controller description it returns.
Controls, results and the random source of SolverSSA.
Port of the event half of MATLAB's +State package: the successor states an event produces at one node...
static constexpr double Immediate
Rate of an Immediate distribution; its mean is 1/Immediate = 1e-8.
static constexpr double Zero
Port of State.pollingInfo: the derived description of a polling controller.
What the cache write-back of solver_ssa_analyzer_serial.m produces (and the NRM's).
std::size_t node
1-based Cache node index
Controls, defaulting to SolverOptions('SSA') in the reference.
What the analyzer returns, in the same shape as the MVA and fluid results.
Matrix< double > StartN
The DERIVED rates, (nstations x nclasses): how often per unit time a class-r service STARTS at statio...
double simulated_time
Simulated time the metrics are averaged over; the reference's totalTime.
std::size_t samples
Reaction firings actually performed.
std::string method
The concrete algorithm, as the reference's method.
Matrix< double > PreemptN