5#ifndef LINE_SOLVERS_NC_SOLVER_NC_LOSSN_H
6#define LINE_SOLVERS_NC_SOLVER_NC_LOSSN_H
138 bool anyFlag =
false;
140 if (b) anyFlag =
true;
142 std::vector<std::size_t> out;
143 for (std::size_t i = 0; i <
sn.nstations; ++i) {
147 }
else if (i < rg.
cap.size()) {
148 for (
double c : rg.
cap[i])
149 if (c >= 0.0) in =
true;
150 if (i < rg.
maxmem.size() && rg.
maxmem[i] >= 0.0) in =
true;
152 if (in) out.push_back(i + 1);
163 std::size_t K,
Matrix<T>& A, std::vector<T>& Cvec) {
166 const std::vector<double>& caps = rg.
cap[st - 1];
168 std::vector<std::vector<T>> rows;
172 if (K < caps.size() && caps[K] >= 0.0) {
173 rows.push_back(std::vector<T>(K, one));
179 if (st - 1 < rg.
maxmem.size() && rg.
maxmem[st - 1] >= 0.0) {
180 std::vector<T> row(K, one);
181 for (std::size_t r = 0; r < K && r < rg.
size.size(); ++r) row[r] = rg.
size[r];
183 rhs.push_back(num_traits<T>::from_double(rg.
maxmem[st - 1]));
188 for (std::size_t r = 0; r < K && r < caps.size(); ++r) {
189 if (caps[r] < 0.0)
continue;
190 std::vector<T> row(K, zero);
193 rhs.push_back(num_traits<T>::from_double(caps[r]));
197 for (std::size_t k = 0; k < rg.
lincon_A.rows(); ++k) {
198 std::vector<T> row(K, zero);
199 for (std::size_t r = 0; r < K && r < rg.
lincon_A.cols(); ++r) row[r] = rg.
lincon_A(k, r);
206 "solver_nc_lossn_analyzer: the finite capacity region declares no bounded constraint, "
207 "so it admits every arrival and is not a loss network; give it a global job cap, a "
208 "memory budget, a per-class cap or an explicit linear constraint");
211 for (std::size_t j = 0; j < rows.size(); ++j)
212 for (std::size_t r = 0; r < K; ++r) A(j, r) = rows[j][r];
217inline std::vector<std::string> lossn_tokens(
const std::string& method) {
218 std::vector<std::string> toks;
220 for (
char ch : method) {
221 if (ch ==
'.' || ch ==
'/') {
225 tok +=
static_cast<char>(std::tolower(
static_cast<unsigned char>(ch)));
232inline bool lossn_has_token(
const std::vector<std::string>& toks,
const char* what) {
233 for (
const std::string& t : toks)
234 if (t == what)
return true;
258 if (
sn.regions.size() != 1)
return false;
259 const std::vector<std::size_t> mem = detail::lossn_region_members(
sn, 0);
260 if (mem.size() != 1)
return false;
261 const std::size_t st = mem[0];
262 if (st == 0 || st >
sn.stations.size())
return false;
263 if (std::isfinite(
sn.stations[st - 1].nservers))
return false;
288 if (
sn.regions[0].rule.size() <
sn.nclasses)
return false;
289 for (std::size_t r = 0; r <
sn.nclasses; ++r)
306 const std::size_t K =
sn.nclasses, M =
sn.nstations;
309 const std::vector<std::size_t> mem = detail::lossn_region_members(
sn, 0);
310 if (
sn.regions.empty())
312 "solver_nc_lossn_analyzer: the model declares no finite capacity region, so it is "
313 "not a loss network");
316 "solver_nc_lossn_analyzer: the finite capacity region holds " +
317 std::to_string(mem.size()) +
318 " stations; the admission rule can only see the occupancy of the region as a "
319 "whole, so the carried load could not be attributed to a station");
320 const std::size_t delayIdx = mem[0];
321 if (
sn.stations[delayIdx - 1].nodetype != qn::NodeType::Delay)
323 "solver_nc_lossn_analyzer: the station inside the finite capacity region is not a "
324 "Delay; a loss network holds admitted jobs at an infinite server, and a queueing "
325 "station would make the response time depend on the population");
331 out.
nu.assign(K, zero);
332 std::vector<T> lambda(K, zero), mu(K, zero);
333 const std::size_t dsf =
sn.stateful_of_station(delayIdx);
334 for (std::size_t r = 0; r < K; ++r) {
335 const std::size_t src =
sn.classes[r].refstat;
336 if (src == 0 || src > M)
continue;
337 if (!
sn.disabled[src - 1][r]) lambda[r] =
sn.rates(src - 1, r);
341 if (
sn.disabled[delayIdx - 1][r])
continue;
342 mu[r] =
sn.rates(delayIdx - 1, r);
345 "solver_nc_lossn_analyzer: class " + std::to_string(r + 1) +
346 " is enabled at the delay with a zero service rate, so its mean holding time "
347 "in the region is unbounded and its offered load is undefined");
350 const std::size_t rsf =
sn.stateful_of_station(src);
351 for (std::size_t c = 0; c <
sn.nchains; ++c) {
352 if (!
sn.chains[c][r])
continue;
353 const T vref =
sn.visits[c](rsf - 1, r);
354 if (vref > zero) V = T(
sn.visits[c](dsf - 1, r) / vref);
357 out.
nu[r] = T(lambda[r] * V / mu[r]);
361 detail::lossn_region_constraints(
sn, 0, delayIdx, K, out.
A, out.
Cvec);
362 const std::size_t J = out.
Cvec.size();
366 const std::vector<std::string> toks = detail::lossn_tokens(
opt.method);
367 bool integral =
true;
368 for (std::size_t j = 0; j < J; ++j) {
370 if (std::fabs(c - std::round(c)) > 1e-9) integral =
false;
371 for (std::size_t r = 0; r < K; ++r) {
373 if (std::fabs(a - std::round(a)) > 1e-9) integral =
false;
378 if (detail::lossn_has_token(toks,
"mci")) {
380 }
else if (detail::lossn_has_token(toks,
"erlangfp")) {
382 }
else if (detail::lossn_has_token(toks,
"rec")) {
384 }
else if (detail::lossn_has_token(toks,
"exact") ||
385 detail::lossn_has_token(toks,
"manjunath") ||
386 detail::lossn_has_token(toks,
"ms")) {
396 chosen = integral ?
"exact" :
"rec";
399 double lG = std::numeric_limits<double>::quiet_NaN();
400 std::vector<T> QLen(K, zero);
401 out.
Loss.assign(K, zero);
405 if (chosen ==
"rec") {
411 method =
"lossn.rec";
412 }
else if (chosen ==
"exact") {
421 method =
"lossn.exact";
422 }
else if (chosen ==
"mci") {
425 "solver_nc_lossn_analyzer: the 'mci' method forms its importance weights in "
426 "log space and reports a confidence interval, so it is unavailable under exact "
427 "arithmetic -- and meaningless there in any case, since the estimate is a "
428 "random variable. Use 'exact', the Manjunath-Sikdar transform, which is "
429 "rational throughout");
435 static_cast<std::uint64_t
>(
opt.seed));
443 niter =
static_cast<int>(mr.
nsamples);
444 method =
"lossn.mci";
449 "solver_nc_lossn_analyzer: the 'erlangfp' method evaluates Erlang's loss "
450 "formula through logs and stops on a tolerance, so it is unavailable under "
451 "exact arithmetic and would not be exact there anyway. Use 'exact', the "
452 "Manjunath-Sikdar transform");
456 "solver_nc_lossn_analyzer: the 'erlangfp' method as ported takes integer "
457 "circuit requirements and an integer link capacity, and this region "
458 "declares fractional ones (a memory budget with fractional class sizes, or "
459 "an explicit linear constraint). The reference evaluates Erlang B through "
460 "factln and accepts them; the port would truncate them and solve a "
461 "different region. Use 'mci', which compares in real arithmetic");
462 std::vector<int> Cint(J, 0);
463 for (std::size_t j = 0; j < J; ++j)
476 method =
"lossn.erlangfp";
486 out.
sol.sol.C.assign(K, zero);
487 out.
sol.sol.X.assign(K, zero);
488 for (std::size_t r = 0; r < K; ++r) {
489 const std::size_t src =
sn.classes[r].refstat;
490 const T Xc = T(lambda[r] * (one - out.
Loss[r]));
491 out.
sol.sol.X[r] = Xc;
492 out.
sol.sol.Tp(delayIdx - 1, r) = Xc;
497 if (src >= 1 && src <= M) out.
sol.sol.Tp(src - 1, r) = Xc;
498 out.
sol.sol.Q(delayIdx - 1, r) = QLen[r];
502 if (mu[r] != zero) out.
sol.sol.R(delayIdx - 1, r) = T(one / mu[r]);
503 out.
sol.sol.U(delayIdx - 1, r) = QLen[r];
506 out.
sol.sol.iter = niter;
507 out.
sol.sol.method = method;
508 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.
ErlangFpResult< T > lossn_erlangfp(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< int > &C, const da::FpiOptions &options=da::FpiOptions())
Erlang fixed-point (reduced-load) approximation for a loss network.
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.
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.
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.
Controls and result shape shared by the normalizing-constant analyzers.
A queueing network and its refreshed NetworkStruct.
Options mirroring the fields MATLAB reads off the options struct.
bool nanstop
stop when the increment norm is not finite
std::vector< T > E
per-link blocking probabilities (the fixed point)
std::vector< T > QLen
carried traffic per class
std::vector< T > Loss
blocking probability per class
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 > 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
std::vector< T > E
(J) per-row blocking, empty outside the fixed point
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