5#ifndef LINE_SOLVERS_NC_SOLVER_NC_DT_H
6#define LINE_SOLVERS_NC_SOLVER_NC_DT_H
89 std::vector<T> alpha(N, one);
90 const std::vector<T>& lld =
sn.stations[ist].lldscaling;
91 if (lld.empty())
return alpha;
93 for (std::size_t k = 0; k < lld.size(); ++k) {
94 if (!(lld[k] == one)) { varies =
true;
break; }
96 if (!varies)
return alpha;
99 for (std::size_t n = 0; n < N; ++n) {
100 alpha[n] = (n < lld.size()) ? lld[n] : lld[lld.size() - 1];
109 const std::vector<T>& lld =
sn.stations[ist].lldscaling;
110 for (std::size_t k = 0; k < lld.size(); ++k) {
111 if (!(lld[k] == one))
return true;
118std::size_t dt_capacity(
const qn::NetworkStruct<T>& sn, std::size_t ist, std::size_t r) {
119 double cap = sn.stations[ist].cap;
120 if (!sn.classcap.empty() && ist < sn.classcap.size() && r < sn.classcap[ist].size()) {
121 const double cc = sn.classcap[ist][r];
122 if (cc < cap) cap = cc;
124 if (!std::isfinite(cap) || cap <= 0)
return 0;
125 return static_cast<std::size_t
>(cap);
133std::vector<std::size_t> dt_cycle_order(
const qn::NetworkStruct<T>& sn) {
134 const std::size_t M = sn.nstations, R = sn.nclasses;
136 const Matrix<T>& rtst = rt.rtst;
137 const double tol = 1e-8, coarse = 1e-3;
139 std::vector<std::size_t> succ(M, 0);
140 for (std::size_t i = 0; i < M; ++i) {
142 for (std::size_t j = 0; j < M; ++j) {
144 for (std::size_t r = 0; r < R; ++r) {
145 for (std::size_t s = 0; s < R; ++s) {
146 w += num_traits<T>::to_double(rtst(i * R + r, j * R + s));
150 if (std::fabs(w -
static_cast<double>(R)) > coarse && std::fabs(w - 1.0) > coarse) {
151 return std::vector<std::size_t>();
153 if (tgt >= 0)
return std::vector<std::size_t>();
154 tgt =
static_cast<long>(j);
157 if (tgt < 0 ||
static_cast<std::size_t
>(tgt) == i)
return std::vector<std::size_t>();
158 succ[i] =
static_cast<std::size_t
>(tgt);
161 std::vector<bool> visited(M,
false);
162 std::vector<std::size_t> order;
164 for (std::size_t k = 0; k < M; ++k) {
165 if (visited[cur])
return std::vector<std::size_t>();
167 order.push_back(cur);
170 if (cur != 0)
return std::vector<std::size_t>();
171 for (std::size_t i = 0; i < M; ++i) {
172 if (!visited[i])
return std::vector<std::size_t>();
183 const std::size_t M =
sn.nstations, R =
sn.nclasses;
187 for (std::size_t i = 0; i < M; ++i) {
188 for (std::size_t r = 0; r < R; ++r) {
189 const T rate =
sn.rates(i, r);
191 if (
sn.procid(i + 1, r + 1) != qn::ProcessType::GEOMETRIC) {
192 dt.
reason =
"a station serves a class with a non-Geometric process; a "
193 "discrete-time model needs Geometric service and interarrival times";
199 for (std::size_t i = 0; i < M; ++i) {
200 if (
sn.stations[i].cdscaling ||
sn.stations[i].jdscaling) {
201 dt.
reason =
"class- or joint-dependent scaling is not covered by the discrete-time "
207 const std::vector<double> njobs =
sn.njobs();
208 bool is_open =
false;
209 for (std::size_t r = 0; r < njobs.size(); ++r) {
210 if (std::isinf(njobs[r])) { is_open =
true;
break; }
215 dt.
reason =
"the discrete-time single-node route handles one open class";
218 std::size_t src = 0, nsrc = 0, ist = 0, nq = 0;
219 for (std::size_t i = 0; i < M; ++i) {
220 if (
sn.stations[i].sched == qn::SchedStrategy::EXT) { src = i; ++nsrc; }
221 else { ist = i; ++nq; }
224 dt.
reason =
"an open discrete-time model needs exactly one Source";
228 dt.
reason =
"the discrete-time single-node route handles one queueing station";
231 if (
sn.stations[ist].sched != qn::SchedStrategy::FCFS) {
232 dt.
reason =
"a Bernoulli server is a FCFS station";
235 if (std::isfinite(
sn.stations[ist].nservers) &&
sn.stations[ist].nservers != 1.0) {
236 dt.
reason =
"a Bernoulli server is a single-server station; use load dependence for "
237 "the multiserver approximation of example 2.10";
240 const T b =
sn.rates(src, 0), p =
sn.rates(ist, 0);
241 if (!(b > zero) || b > one) {
242 dt.
reason =
"the source arrival probability must lie in (0,1]";
245 if (!(p > zero) || p > one) {
246 dt.
reason =
"the service probability must lie in (0,1]";
249 const std::size_t cap = detail::dt_capacity(
sn, ist, 0);
250 const bool has_lld = detail::dt_has_lld(
sn, ist);
251 if (cap == 0 && has_lld) {
252 dt.
reason =
"a load-dependent Bernoulli server needs a finite capacity to bound the "
256 if (cap == 0 && !(b < p)) {
257 dt.
reason =
"an unbounded discrete-time queue needs an arrival probability below the "
258 "service probability";
264 const std::vector<T> alpha = detail::dt_lld_vector(
sn, ist, cap);
266 for (std::size_t n = 0; n < cap; ++n) {
269 dt.
reason =
"load dependence must keep the service probability inside (0,1]";
274 dt.
kind =
"bernoulli1";
283 for (std::size_t r = 0; r < njobs.size(); ++r) {
284 if (std::isfinite(njobs[r])) Ntot += njobs[r];
286 if (Ntot <= 0 || Ntot != std::floor(Ntot)) {
287 dt.
reason =
"the closed population must be a positive integer";
290 const std::size_t N =
static_cast<std::size_t
>(Ntot);
292 std::vector<T> p(M, zero);
293 for (std::size_t i = 0; i < M; ++i) {
294 if (
sn.stations[i].sched != qn::SchedStrategy::FCFS) {
295 dt.
reason =
"a cycle of Bernoulli servers is FCFS throughout";
298 if (std::isfinite(
sn.stations[i].nservers) &&
sn.stations[i].nservers != 1.0) {
299 dt.
reason =
"a multiserver node inside a cycle of geometrical queues has no product "
304 T lo = zero, hi = zero;
305 for (std::size_t r = 0; r < R; ++r) {
306 const T v =
sn.rates(i, r);
308 if (!seen) { lo = v; hi = v; seen =
true; }
309 else {
if (v < lo) lo = v;
if (v > hi) hi = v; }
313 dt.
reason =
"a station of the cycle serves no class";
317 dt.
reason =
"a station has a class-dependent service probability; the discrete-time "
318 "cycle needs one Bernoulli server per node";
322 if (!(p[i] > zero) || !(p[i] < one)) {
323 dt.
reason =
"the product form of theorem 3.2 needs every service probability in (0,1)";
328 const std::vector<std::size_t> order = detail::dt_cycle_order(
sn);
330 dt.
reason =
"the stations do not form a single deterministic cycle; discrete-time FCFS "
331 "networks of other topologies have no product form";
335 dt.
service.assign(M, std::vector<T>(N, zero));
336 for (std::size_t k = 0; k < M; ++k) {
337 const std::vector<T> alpha = detail::dt_lld_vector(
sn, order[k], N);
338 for (std::size_t n = 0; n < N; ++n) {
339 dt.
service[k][n] = p[order[k]] * alpha[n];
341 dt.
reason =
"load dependence must keep every service probability inside (0,1]";
366 const std::size_t M =
sn.nstations, K =
sn.nclasses;
375 out.
sol.C.assign(K, zero);
376 out.
sol.X.assign(K, zero);
379 if (dt.
kind ==
"bernoulli1") {
380 out.
sol.method =
"dt.bernoulli1";
392 if (ratio > 0 && ratio < 1) {
393 const std::size_t need =
static_cast<std::size_t
>(std::ceil(std::log(1e-18)
395 if (need > L) L = need;
397 if (L > 100000) L = 100000;
411 }
else if (dt.
kind ==
"cycle") {
413 bool state_independent =
true;
414 for (std::size_t k = 0; k < dt.
service.size() && state_independent; ++k) {
415 for (std::size_t n = 1; n < dt.
service[k].size(); ++n) {
416 if (!(dt.
service[k][n] == dt.
service[k][0])) { state_independent =
false;
break; }
419 std::vector<T> Qs(M, zero), Us(M, zero), Ts(M, zero);
420 if (state_independent) {
423 out.
sol.method =
"dt.cycle";
425 for (std::size_t k = 0; k < M; ++k) p[k] = dt.
service[k][0];
427 const T x =
nc.throughput();
428 for (std::size_t k = 0; k < M; ++k) {
429 const T q = one - p[k];
430 T ratio = one, tail = zero;
431 for (std::size_t n = 1; n <= N; ++n) {
432 ratio = ratio * q / p[k];
433 tail = tail + ratio / q *
nc.G1[N - n + 1] /
nc.G;
435 Qs[dt.
order[k]] = tail;
436 Us[dt.
order[k]] = x / p[k];
441 out.
sol.method =
"dt.cycleld";
443 for (std::size_t k = 0; k < M; ++k) {
444 const std::vector<T> marg =
nc.marginal(k);
445 T q = zero, t = zero;
446 for (std::size_t n = 0; n <= N; ++n) {
448 if (n >= 1) t = t + marg[n] * dt.
service[k][n - 1];
451 Us[dt.
order[k]] = one - marg[0];
457 const std::vector<double> njobs =
sn.njobs();
458 for (std::size_t i = 0; i < M; ++i) {
459 for (std::size_t r = 0; r < K; ++r) {
461 std::isfinite(njobs[r]) ? njobs[r] /
static_cast<double>(N) : 0.0);
462 out.
sol.Q(i, r) = Qs[i] * share;
463 out.
sol.U(i, r) = Us[i] * share;
464 out.
sol.Tp(i, r) = Ts[i] * share;
465 if (out.
sol.Tp(i, r) > zero) {
466 out.
sol.R(i, r) = out.
sol.Q(i, r) / out.
sol.Tp(i, r);
470 for (std::size_t r = 0; r < K; ++r) {
471 const std::size_t ref =
sn.classes[r].refstat;
472 if (ref >= 1 && ref <= M) out.
sol.X[r] = out.
sol.Tp(ref - 1, r);
473 if (out.
sol.X[r] > zero && std::isfinite(njobs[r])) {
478 throw InputError(
"solver_nc_dt: the slotted option was requested but the model is not a "
479 "discrete-time product-form model: " + dt.
reason);
483 if (
opt.slotlength != 1.0) {
485 for (std::size_t i = 0; i < M; ++i) {
486 for (std::size_t r = 0; r < K; ++r) {
487 out.
sol.Tp(i, r) = out.
sol.Tp(i, r) / d;
488 out.
sol.R(i, r) = out.
sol.R(i, r) * d;
491 for (std::size_t r = 0; r < K; ++r) {
492 out.
sol.X[r] = out.
sol.X[r] / d;
493 out.
sol.C[r] = out.
sol.C[r] * d;
A network plus its refreshed NetworkStruct.
Normalizing constants of a discrete-time closed cycle of Bernoulli servers.
State dependent Bernoulli server on a discrete time scale.
The exception types the port throws.
SnRtStations< T > sn_rt_stations(const qn::NetworkStruct< T > &sn)
DtNcLdResult< T > dpfqn_ncld(const std::vector< std::vector< T > > &P, std::size_t N)
Theorem 3.2 for a cycle whose service probabilities depend on the local queue length.
DtNcResult< T > dpfqn_nc(const std::vector< T > &p, std::size_t N)
Propositions 3.18 and 3.19 for a cycle with state independent service probabilities.
Bernoulli1Result< T > dqsys_bernoulli1(const std::vector< T > &b, const std::vector< T > &p, std::size_t L)
Finite buffer of L jobs.
NcSolution< T > solver_nc_dt(const qn::NetworkStruct< T > &sn, const NcSolverOptions &opt)
Exact discrete-time analysis of sn.
DtModel< T > nc_is_dt_model(const qn::NetworkStruct< T > &sn)
Classify sn against the two discrete-time product-form families.
Controls and result shape shared by the normalizing-constant analyzers.
A queueing network and its refreshed NetworkStruct.
Port of matlab/src/api/sn/sn_rt_stations.m.
Constants of the state dependent cycle, in one common scale.
Constants of the state independent cycle, in one common scale.
Steady-state quantities of a finite-buffer Bernoulli server.
T throughput
carried departures per slot
T normConst
H of theorem 2.3.
T meanSojournTime
in slots, by Little's law
Classification of a model against the discrete-time product form.
std::vector< std::vector< T > > service
p_j(n), [cycle position][n-1]
std::vector< T > service_single
p(n), n = 1..L (bernoulli1)
std::size_t station
0-based queueing station (bernoulli1)
std::size_t capacity
0 means unbounded
T arrival_prob
offered b (bernoulli1)
std::string reason
why not, when kind is "none"
std::string kind
"bernoulli1", "cycle" or "none"
std::size_t source
0-based Source station (bernoulli1)
std::vector< std::size_t > order
stations in cycle order
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.