5#ifndef LINE_SOLVERS_NC_SOLVER_NC_LOSSN_H
6#define LINE_SOLVERS_NC_SOLVER_NC_LOSSN_H
135 bool anyFlag =
false;
137 if (b) anyFlag =
true;
139 std::vector<std::size_t> out;
140 for (std::size_t i = 0; i <
sn.nstations; ++i) {
144 }
else if (i < rg.
cap.size()) {
145 for (
double c : rg.
cap[i])
146 if (c >= 0.0) in =
true;
147 if (i < rg.
maxmem.size() && rg.
maxmem[i] >= 0.0) in =
true;
149 if (in) out.push_back(i + 1);
160 std::size_t K,
Matrix<T>& A, std::vector<T>& Cvec) {
163 const std::vector<double>& caps = rg.
cap[st - 1];
165 std::vector<std::vector<T>> rows;
169 if (K < caps.size() && caps[K] >= 0.0) {
170 rows.push_back(std::vector<T>(K, one));
176 if (st - 1 < rg.
maxmem.size() && rg.
maxmem[st - 1] >= 0.0) {
177 std::vector<T> row(K, one);
178 for (std::size_t r = 0; r < K && r < rg.
size.size(); ++r) row[r] = rg.
size[r];
180 rhs.push_back(num_traits<T>::from_double(rg.
maxmem[st - 1]));
185 for (std::size_t r = 0; r < K && r < caps.size(); ++r) {
186 if (caps[r] < 0.0)
continue;
187 std::vector<T> row(K, zero);
190 rhs.push_back(num_traits<T>::from_double(caps[r]));
194 for (std::size_t k = 0; k < rg.
lincon_A.rows(); ++k) {
195 std::vector<T> row(K, zero);
196 for (std::size_t r = 0; r < K && r < rg.
lincon_A.cols(); ++r) row[r] = rg.
lincon_A(k, r);
203 "solver_nc_lossn_analyzer: the finite capacity region declares no bounded constraint, "
204 "so it admits every arrival and is not a loss network; give it a global job cap, a "
205 "memory budget, a per-class cap or an explicit linear constraint");
208 for (std::size_t j = 0; j < rows.size(); ++j)
209 for (std::size_t r = 0; r < K; ++r) A(j, r) = rows[j][r];
214inline std::vector<std::string> lossn_tokens(
const std::string& method) {
215 std::vector<std::string> toks;
217 for (
char ch : method) {
218 if (ch ==
'.' || ch ==
'/') {
222 tok +=
static_cast<char>(std::tolower(
static_cast<unsigned char>(ch)));
229inline bool lossn_has_token(
const std::vector<std::string>& toks,
const char* what) {
230 for (
const std::string& t : toks)
231 if (t == what)
return true;
255 if (
sn.regions.size() != 1)
return false;
256 const std::vector<std::size_t> mem = detail::lossn_region_members(
sn, 0);
257 if (mem.size() != 1)
return false;
258 const std::size_t st = mem[0];
259 if (st == 0 || st >
sn.stations.size())
return false;
260 if (std::isfinite(
sn.stations[st - 1].nservers))
return false;
285 if (
sn.regions[0].rule.size() <
sn.nclasses)
return false;
286 for (std::size_t r = 0; r <
sn.nclasses; ++r)
303 const std::size_t K =
sn.nclasses, M =
sn.nstations;
306 const std::vector<std::size_t> mem = detail::lossn_region_members(
sn, 0);
307 if (
sn.regions.empty())
309 "solver_nc_lossn_analyzer: the model declares no finite capacity region, so it is "
310 "not a loss network");
313 "solver_nc_lossn_analyzer: the finite capacity region holds " +
314 std::to_string(mem.size()) +
315 " stations; the admission rule can only see the occupancy of the region as a "
316 "whole, so the carried load could not be attributed to a station");
317 const std::size_t delayIdx = mem[0];
318 if (
sn.stations[delayIdx - 1].nodetype != qn::NodeType::Delay)
320 "solver_nc_lossn_analyzer: the station inside the finite capacity region is not a "
321 "Delay; a loss network holds admitted jobs at an infinite server, and a queueing "
322 "station would make the response time depend on the population");
328 out.
nu.assign(K, zero);
329 std::vector<T> lambda(K, zero), mu(K, zero);
330 const std::size_t dsf =
sn.stateful_of_station(delayIdx);
331 for (std::size_t r = 0; r < K; ++r) {
332 const std::size_t src =
sn.classes[r].refstat;
333 if (src == 0 || src > M)
continue;
334 if (!
sn.disabled[src - 1][r]) lambda[r] =
sn.rates(src - 1, r);
338 if (
sn.disabled[delayIdx - 1][r])
continue;
339 mu[r] =
sn.rates(delayIdx - 1, r);
342 "solver_nc_lossn_analyzer: class " + std::to_string(r + 1) +
343 " is enabled at the delay with a zero service rate, so its mean holding time "
344 "in the region is unbounded and its offered load is undefined");
347 const std::size_t rsf =
sn.stateful_of_station(src);
348 for (std::size_t c = 0; c <
sn.nchains; ++c) {
349 if (!
sn.chains[c][r])
continue;
350 const T vref =
sn.visits[c](rsf - 1, r);
351 if (vref > zero) V = T(
sn.visits[c](dsf - 1, r) / vref);
354 out.
nu[r] = T(lambda[r] * V / mu[r]);
358 detail::lossn_region_constraints(
sn, 0, delayIdx, K, out.
A, out.
Cvec);
359 const std::size_t J = out.
Cvec.size();
363 const std::vector<std::string> toks = detail::lossn_tokens(
opt.method);
364 bool integral =
true;
365 for (std::size_t j = 0; j < J; ++j) {
367 if (std::fabs(c - std::round(c)) > 1e-9) integral =
false;
368 for (std::size_t r = 0; r < K; ++r) {
370 if (std::fabs(a - std::round(a)) > 1e-9) integral =
false;
375 if (detail::lossn_has_token(toks,
"mci")) {
377 }
else if (detail::lossn_has_token(toks,
"erlangfp")) {
379 }
else if (detail::lossn_has_token(toks,
"rec")) {
381 }
else if (detail::lossn_has_token(toks,
"exact") ||
382 detail::lossn_has_token(toks,
"manjunath") ||
383 detail::lossn_has_token(toks,
"ms")) {
393 chosen = integral ?
"exact" :
"rec";
396 double lG = std::numeric_limits<double>::quiet_NaN();
397 std::vector<T> QLen(K, zero);
398 out.
Loss.assign(K, zero);
402 if (chosen ==
"rec") {
408 method =
"lossn.rec";
409 }
else if (chosen ==
"exact") {
418 method =
"lossn.exact";
419 }
else if (chosen ==
"mci") {
422 "solver_nc_lossn_analyzer: the 'mci' method forms its importance weights in "
423 "log space and reports a confidence interval, so it is unavailable under exact "
424 "arithmetic -- and meaningless there in any case, since the estimate is a "
425 "random variable. Use 'exact', the Manjunath-Sikdar transform, which is "
426 "rational throughout");
432 static_cast<std::uint64_t
>(
opt.seed));
440 niter =
static_cast<int>(mr.
nsamples);
441 method =
"lossn.mci";
446 "solver_nc_lossn_analyzer: the 'erlangfp' method stops on a tolerance, so it "
447 "is unavailable under "
448 "exact arithmetic and would not be exact there anyway. Use 'exact', the "
449 "Manjunath-Sikdar transform");
456 "solver_nc_lossn_analyzer: the 'erlangfp' method is not offered on a "
457 "region with fractional circuit requirements or capacity (a memory budget "
458 "with fractional class sizes, or an explicit linear constraint). Use "
459 "'mci', which compares in real arithmetic, or 'rec', which is exact there");
467 niter =
static_cast<int>(fr.iterations);
468 method =
"lossn.erlangfp";
478 out.
sol.sol.C.assign(K, zero);
479 out.
sol.sol.X.assign(K, zero);
480 for (std::size_t r = 0; r < K; ++r) {
481 const std::size_t src =
sn.classes[r].refstat;
482 const T Xc = T(lambda[r] * (one - out.
Loss[r]));
483 out.
sol.sol.X[r] = Xc;
484 out.
sol.sol.Tp(delayIdx - 1, r) = Xc;
489 if (src >= 1 && src <= M) out.
sol.sol.Tp(src - 1, r) = Xc;
490 out.
sol.sol.Q(delayIdx - 1, r) = QLen[r];
494 if (mu[r] != zero) out.
sol.sol.R(delayIdx - 1, r) = T(one / mu[r]);
495 out.
sol.sol.U(delayIdx - 1, r) = QLen[r];
498 out.
sol.sol.iter = niter;
499 out.
sol.sol.method = method;
500 out.
sol.actualmethod = method;
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
Damped fixed-point iteration, the shared driver of the decomposition algorithms.
The exception types the port throws.
Erlang fixed-point (reduced-load) approximation for a loss network.
Exact analysis of a loss network by the Manjunath-Sikdar transform.
Monte Carlo importance-sampling summation for product-form loss networks.
Exact analysis of a loss network by MDD-rec.
Dense matrix and non-owning view.
LossnManjunathResult< T > lossn_manjunath(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const LossnManjunathOptions &options=LossnManjunathOptions())
Exact normalizing constant, carried load and blocking of a loss network.
ErlangFpResult< T > lossn_erlangfp(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const da::FpiOptions &options=lossn_erlangfp_options())
Erlang fixed-point (reduced-load) approximation for a loss network.
LossnMciResult< T > lossn_mci(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const LossnMciOptions< T > &opt, Rng &gen)
Estimate the normalizing constant and the blocking probabilities.
LossnRecResult< T > lossn_rec(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C)
Exact loss-network analysis by MDD-rec.
da::FpiOptions lossn_erlangfp_options()
The reference's fixed-point settings: iter_max 10000, iter_tol 1e-10, the max-norm of the increment (...
bool nc_is_lossn_model(const qn::NetworkStruct< T > &sn)
True when the model is a loss network: the shape above, with EVERY class dropped at the region.
bool nc_has_lossn_shape(const qn::NetworkStruct< T > &sn)
True when the model has the SHAPE of a loss network – open, one region, one member station,...
NcLossnSolution< T > solver_nc_lossn_analyzer(const qn::NetworkStruct< T > &sn, const NcSolverOptions &opt)
Port of solver_nc_lossn_analyzer.m.
Conservation laws of a layered queueing network, enumerated from its structure.
Controls and result shape shared by the normalizing-constant analyzers.
A queueing network and its refreshed NetworkStruct.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Controls of lossn_manjunath.
Result of lossn_manjunath.
double lG
log of the EXACT normalizing constant g(C)
std::vector< T > QLen
mean carried load E[n_r] per route
std::vector< T > Loss
blocking probability per route
std::size_t iterations
Always 1: the transform is direct, and the field exists for the shared analyzer contract that the ite...
Options of lossn_mci, MATLAB's options struct.
std::vector< T > QLen
mean carried load per route
std::vector< T > Loss
blocking probability per route
double lG
log of the estimated normalizing constant
Carried load, blocking, log normalising constant and walk count.
double lG
log G(C), a double diagnostic.
std::vector< T > Loss
Blocking probability per class.
std::vector< T > QLen
Mean number of class-r calls in progress, the carried load.
int iterations
Number of diagram walks performed, K + 1.
What the loss-network analyzer returns beyond the usual metric table.
std::vector< T > Loss
(K) blocking probability per class
Matrix< T > E
(J x K) blocking of class k on row j, empty outside the fixed point
Matrix< T > A
(J x K) rows of the admission rule A n <= C
std::vector< T > nu
(K) offered load per class
std::vector< T > Cvec
(J) the right-hand side
The [Q,U,R,T,C,X,lG] of the reference, plus the algorithm that ran.
Controls, defaulting to SolverOptions('NC') in the reference.
One job class of the network.
double population
infinite for an open class
FINITE CAPACITY REGIONS, MATLAB's refreshRegions output.
std::vector< T > lincon_b
Matrix< T > lincon_A
optional linear constraint A n <= b
std::vector< std::vector< double > > cap
(nstations x nclasses+1), -1 = unbounded
std::vector< double > maxmem
per member station, -1 = unbounded
std::vector< T > size
per class; size is the memory footprint
std::vector< bool > members
membership, independent of the caps