LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_nc_mem.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_SOLVERS_NC_SOLVER_NC_MEM_H
6#define LINE_SOLVERS_NC_SOLVER_NC_MEM_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * Port of `solver_nc_mem.m` and `solver_nc_mem_supports.m`: the Maximum Entropy
12 * Method of Kouvatsos (1994).
13 *
14 * WHY MEM IS IN A NORMALIZING-CONSTANT SOLVER AT ALL. It is not a
15 * normalizing-constant algorithm and shares no code with one. It is here
16 * because it answers the question the product-form path cannot: a network whose
17 * arrival or service processes are NOT exponential. The product-form analyzer
18 * silently exponentializes such a model -- it reads only the mean rate -- while
19 * MEM carries the second moment through GE (generalised exponential) building
20 * blocks and reports what the variability does.
21 *
22 * THIS FILE IS A DISPATCHER, NOT AN ALGORITHM. The four algorithms live in
23 * `api/me/`, and three of them were already ported:
24 *
25 * open Section 3.2, GE/GE/1, GE/GE/c and GE/GE/inf blocks -> me_oqn
26 * closed Section 3.3, two-stage pseudo-open plus convolution -> me_cqn
27 * mixed the two composed by product-form-style conditioning -> me_mqn
28 * blocking Section 4.1, censored GE/GE/c/0;N blocks -> me_oqn_blk
29 *
30 * All four are now ported. The blocking one additionally covers TRANSFER
31 * BLOCKING (BAS) through the holding-node expansion of Tahilramani, Manjunath
32 * and Bose (1999); see `api/me/me_oqn_blk.h` for why that expansion is needed
33 * at all.
34 *
35 * THE GATE IS PART OF THE METHOD. `solver_nc_mem_supports` is not a
36 * convenience: MEM's building blocks are defined for particular network shapes,
37 * and a model outside them has no MEM answer at all. The reference returns a
38 * REASON naming the first violated rule, and that reason is carried into the
39 * exception here so a refusal says which rule and which station.
40 *
41 * ARITHMETIC. The GE blocks are transcendental throughout; a non-transcendental
42 * backend is refused by name.
43 */
44
45#include <algorithm>
46#include <cmath>
47#include <cstddef>
48#include <string>
49#include <vector>
50
52#include "line/api/me/me_cqn.h"
53#include "line/api/me/me_mqn.h"
54#include "line/api/me/me_oqn.h"
60#include "line/util/error.h"
61#include "line/util/matrix.h"
62
63namespace line {
64namespace nc {
65
66/** The verdict of `solver_nc_mem_supports`. */
67struct MemSupport {
68 bool supported = false;
69 std::string reason; ///< names the first violated rule; empty when supported
70 bool blocking = false; ///< the model carries a binding finite station buffer
71};
72
73namespace detail {
74
75/**
76 * Kendall's K of a station. Delegates to line::sn::sn_get_buffer_size, which is
77 * the one implementation; kept here as a name the NC code already uses.
78 */
79template <class T>
80double buffer_size(const qn::NetworkStruct<T>& sn, std::size_t ist) {
82}
83
84/** The discipline name, for the reason strings. */
85inline const char* sched_text(qn::SchedStrategy s) {
86 switch (s) {
87 case qn::SchedStrategy::INF: return "INF";
88 case qn::SchedStrategy::FCFS: return "FCFS";
89 case qn::SchedStrategy::PS: return "PS";
90 case qn::SchedStrategy::SIRO: return "SIRO";
91 case qn::SchedStrategy::LCFS: return "LCFS";
92 case qn::SchedStrategy::LCFSPR: return "LCFSPR";
93 case qn::SchedStrategy::EXT: return "EXT";
94 case qn::SchedStrategy::HOL: return "HOL";
95 case qn::SchedStrategy::DPS: return "DPS";
96 case qn::SchedStrategy::GPS: return "GPS";
97 default: return "an unsupported strategy";
98 }
99}
100
101} // namespace detail
102
103/**
104 * Port of `solver_nc_mem_supports.m`.
105 *
106 * @param sn the refreshed struct
107 * @return whether MEM applies, the first violated rule if not, and whether the
108 * model needs the finite-buffer (blocking) algorithm
109 */
110template <class T>
112 MemSupport out;
113 const std::size_t M = sn.nstations, R = sn.nclasses;
114
115 bool anyOpen = false, anyClosed = false;
116 for (const qn::JobClass& c : sn.classes)
117 (std::isinf(c.population) ? anyOpen : anyClosed) = true;
118 const bool isopen = anyOpen && !anyClosed;
119 const bool isclosed = anyClosed && !anyOpen;
120 const bool ismixed = anyOpen && anyClosed;
121
122 // Node types: open models are Source/Queue/Delay/Sink, closed are
123 // Queue/Delay.
124 for (const qn::NodeDef& nd : sn.nodes) {
125 switch (nd.nodetype) {
126 case qn::NodeType::Queue:
127 case qn::NodeType::Delay:
128 break;
129 case qn::NodeType::Source:
130 case qn::NodeType::Sink:
131 if (isclosed) {
132 out.reason = "MEM supports only Queue and Delay nodes in closed models.";
133 return out;
134 }
135 break;
136 default:
137 out.reason = "MEM supports only Source, Queue, Delay and Sink nodes.";
138 return out;
139 }
140 }
141
142 if (sn.has_class_switching()) {
143 out.reason = "MEM does not support class switching.";
144 return out;
145 }
146
147 // An absorbing self-loop makes the routing reducible and the geometric
148 // feedback transform 1/(1-p_ii) degenerate.
149 for (std::size_t ist = 1; ist <= M; ++ist) {
150 const std::size_t nd = sn.node_of_station(ist);
151 for (std::size_t r = 0; r < R; ++r)
152 if (num_traits<T>::to_double(sn.rtnodes((nd - 1) * R + r, (nd - 1) * R + r)) >=
153 1.0 - 1e-9) {
154 out.reason =
155 "MEM does not support absorbing self-loop routing (reducible network).";
156 return out;
157 }
158 }
159
160 // The PR/HOL constraint formulae are not given in the reference.
161 for (std::size_t i = 0; i < M; ++i) {
162 const qn::SchedStrategy s = sn.stations[i].sched;
163 if (s != qn::SchedStrategy::EXT && s != qn::SchedStrategy::INF &&
164 s != qn::SchedStrategy::FCFS && s != qn::SchedStrategy::PS &&
165 s != qn::SchedStrategy::SIRO && s != qn::SchedStrategy::LCFS &&
166 s != qn::SchedStrategy::LCFSPR) {
167 out.reason = std::string("MEM does not support the ") + detail::sched_text(s) +
168 " scheduling strategy.";
169 return out;
170 }
171 }
172
173 if (isopen || ismixed) {
174 bool hasSource = false;
175 for (const qn::NodeDef& nd : sn.nodes)
176 if (nd.nodetype == qn::NodeType::Source) hasSource = true;
177 if (!hasSource) {
178 out.reason = "MEM requires a Source node when open classes are present.";
179 return out;
180 }
181 }
182 if (isclosed || ismixed) {
183 // Closed classes build on G/G/1 and G/G/inf blocks only.
184 for (std::size_t i = 0; i < M; ++i)
185 if (std::isfinite(sn.stations[i].nservers) && sn.stations[i].nservers > 1.0) {
186 out.reason =
187 "MEM does not support multiserver stations in closed or mixed models.";
188 return out;
189 }
190 }
191
192 // Finite buffers: the censored block is single class, so a binding buffer
193 // is admissible only in a single-class open model.
194 std::vector<bool> capped(M, false);
195 bool anyCapped = false;
196 for (std::size_t i = 0; i < M; ++i) {
197 if (sn.stations[i].sched == qn::SchedStrategy::EXT) continue;
198 if (std::isfinite(detail::buffer_size(sn, i + 1))) {
199 capped[i] = true;
200 anyCapped = true;
201 }
202 }
203 if (anyCapped) {
204 if (!isopen) {
205 out.reason = "MEM supports finite station buffers only in open models.";
206 return out;
207 }
208 if (R > 1) {
209 out.reason =
210 "MEM supports finite station buffers only in single-class models: the censored "
211 "GE/GE/c/0;N building block is single class.";
212 return out;
213 }
214 for (std::size_t i = 0; i < M; ++i) {
215 if (!capped[i]) continue;
216 const double c = sn.stations[i].nservers;
217 if (!std::isfinite(c) || c < 1.0) {
218 out.reason = "MEM cannot apply a finite buffer to the infinite-server station " +
219 std::to_string(i + 1) + ".";
220 return out;
221 }
222 if (sn.stations[i].sched != qn::SchedStrategy::FCFS) {
223 out.reason =
224 "MEM supports finite station buffers only under FCFS scheduling; station " +
225 std::to_string(i + 1) + " uses " + detail::sched_text(sn.stations[i].sched) +
226 ".";
227 return out;
228 }
229 const int dr = sn.stations[i].droprule.empty() ? 0 : sn.stations[i].droprule[0];
230 if (dr != 0 && dr != static_cast<int>(lang::DropStrategy::DROP) &&
231 dr != static_cast<int>(lang::DropStrategy::BAS)) {
232 out.reason =
233 "MEM supports the DROP and BAS drop rules at a finite buffer; station " +
234 std::to_string(i + 1) + " uses another rule.";
235 return out;
236 }
237 // The GE distribution is undefined below scv 1, so a hypo-exponential
238 // service is refused rather than approximated.
239 if (!sn.disabled[i][0] &&
240 num_traits<T>::to_double(sn.scv(i, 0)) < 1.0 - 1e-12) {
241 out.reason = "MEM with finite buffers needs a service scv of at least 1 at "
242 "station " + std::to_string(i + 1) +
243 ": the GE distribution is not defined below 1.";
244 return out;
245 }
246 }
247 for (std::size_t i = 0; i < M; ++i)
248 if (sn.stations[i].sched == qn::SchedStrategy::EXT && !sn.disabled[i][0] &&
249 num_traits<T>::to_double(sn.scv(i, 0)) < 1.0 - 1e-12) {
250 out.reason = "MEM with finite buffers needs an external interarrival scv of at "
251 "least 1: the GE distribution is not defined below 1.";
252 return out;
253 }
254 out.blocking = true;
255 }
256
257 out.supported = true;
258 return out;
259}
260
261/**
262 * Port of `solver_nc_mem.m`.
263 *
264 * @param sn the refreshed struct
265 * @param opt solver controls; `mem_tol`, `mem_maxiter` are read
266 */
267template <class T>
269 NcSolution<T> out;
270 if constexpr (!num_traits<T>::has_transcendental) {
271 (void)sn;
272 (void)opt;
273 throw UnsupportedError(
274 "solver_nc_mem: the Maximum Entropy Method is built on GE (generalised exponential) "
275 "blocks whose entropy maximisation is transcendental throughout; this backend has "
276 "none");
277 } else {
278 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
279 const std::size_t M = sn.nstations, R = sn.nclasses;
280
282 if (!sup.supported) throw UnsupportedError("solver_nc_mem: " + sup.reason);
283
284 me::MeOptions mopt;
285 mopt.tol = opt.mem_tol;
286 mopt.maxiter = opt.mem_maxiter;
287
288 bool anyOpen = false, anyClosed = false;
289 for (const qn::JobClass& c : sn.classes)
290 (std::isinf(c.population) ? anyOpen : anyClosed) = true;
291 const bool isclosed = anyClosed && !anyOpen;
292 const bool isopen = anyOpen && !anyClosed;
293
294 // The per-station service rate and scv, zero where a class is not
295 // served (the reference reads NaN there and skips it).
296 const auto fill_service = [&](const std::vector<std::size_t>& sts, Matrix<T>& mu,
297 Matrix<T>& Cs, std::vector<long>& c) {
298 mu = Matrix<T>(sts.size(), R, zero);
299 Cs = Matrix<T>(sts.size(), R, one);
300 c.assign(sts.size(), 1);
301 for (std::size_t k = 0; k < sts.size(); ++k) {
302 const std::size_t i = sts[k] - 1;
303 // me_* takes 0 for an infinite server, where MATLAB takes Inf.
304 c[k] = std::isinf(sn.stations[i].nservers)
305 ? 0
306 : static_cast<long>(std::llround(sn.stations[i].nservers));
307 for (std::size_t r = 0; r < R; ++r) {
308 if (sn.disabled[i][r] || !(sn.rates(i, r) > zero)) continue;
309 mu(k, r) = sn.rates(i, r);
310 if (sn.scv(i, r) > zero) Cs(k, r) = sn.scv(i, r);
311 }
312 }
313 };
314 const auto fill_routing = [&](const std::vector<std::size_t>& sts) {
315 std::vector<Matrix<T>> P(R, Matrix<T>(sts.size(), sts.size(), zero));
316 for (std::size_t r = 0; r < R; ++r)
317 for (std::size_t j = 0; j < sts.size(); ++j) {
318 const std::size_t jn = sn.node_of_station(sts[j]);
319 for (std::size_t k = 0; k < sts.size(); ++k) {
320 const std::size_t kn = sn.node_of_station(sts[k]);
321 P[r](j, k) = sn.rtnodes((jn - 1) * R + r, (kn - 1) * R + r);
322 }
323 }
324 return P;
325 };
326 const auto fill_insens = [&](const std::vector<std::size_t>& sts) {
327 std::vector<char> ins(sts.size(), 0);
328 for (std::size_t k = 0; k < sts.size(); ++k) {
329 const qn::SchedStrategy s = sn.stations[sts[k] - 1].sched;
330 ins[k] = (s == qn::SchedStrategy::PS || s == qn::SchedStrategy::LCFSPR) ? 1 : 0;
331 }
332 return ins;
333 };
334
335 std::vector<long> N(R, 0);
336 for (std::size_t r = 0; r < R; ++r)
337 N[r] = std::isinf(sn.classes[r].population)
338 ? 0
339 : static_cast<long>(std::llround(sn.classes[r].population));
340
341 out.sol.Q = Matrix<T>(M, R, zero);
342 out.sol.U = Matrix<T>(M, R, zero);
343 out.sol.R = Matrix<T>(M, R, zero);
344 out.sol.Tp = Matrix<T>(M, R, zero);
345 out.sol.X.assign(R, zero);
346 out.sol.C.assign(R, zero);
347 out.actualmethod = "mem";
348
349 if (isclosed) {
350 // Every station is a queueing station; there is no Source.
351 std::vector<std::size_t> sts(M);
352 for (std::size_t i = 0; i < M; ++i) sts[i] = i + 1;
353 Matrix<T> mu, Cs;
354 std::vector<long> c;
355 fill_service(sts, mu, Cs, c);
356 std::vector<long> refstat(R, -1);
357 for (std::size_t r = 0; r < R; ++r)
358 refstat[r] = static_cast<long>(sn.classes[r].refstat) - 1;
359 const me::MeResult<T> res =
360 me::me_cqn(M, R, N, mu, Cs, fill_routing(sts), c, refstat, fill_insens(sts), mopt);
361 out.sol.Q = res.L;
362 out.sol.U = res.rho;
363 out.sol.R = res.W;
364 out.sol.Tp = res.lambda;
365 out.sol.X = res.X;
366 for (std::size_t r = 0; r < R; ++r)
367 if (res.X[r] > zero)
368 out.sol.C[r] = T(num_traits<T>::from_double(
369 static_cast<double>(N[r])) /
370 res.X[r]);
371 out.sol.iter = static_cast<int>(res.iter);
372 return out;
373 }
374
375 // Open and mixed both split off the Source and analyze the rest.
376 std::size_t sourceIdx = 0;
377 for (std::size_t i = 0; i < M; ++i)
378 if (sn.stations[i].nodetype == qn::NodeType::Source) sourceIdx = i + 1;
379 if (sourceIdx == 0) throw UnsupportedError("solver_nc_mem: MEM requires a Source node");
380
381 std::vector<std::size_t> qs;
382 for (std::size_t i = 1; i <= M; ++i)
383 if (i != sourceIdx) qs.push_back(i);
384 const std::size_t Mq = qs.size();
385 Matrix<T> mu, Cs;
386 std::vector<long> c;
387 fill_service(qs, mu, Cs, c);
388
389 // External arrivals, spread along the Source's routing.
390 Matrix<T> lambda0(Mq, R, zero), Ca0(Mq, R, zero);
391 const std::size_t sourceNode = sn.node_of_station(sourceIdx);
392 for (std::size_t r = 0; r < R; ++r) {
393 if (sn.disabled[sourceIdx - 1][r] || !(sn.rates(sourceIdx - 1, r) > zero)) continue;
394 if (!std::isinf(sn.classes[r].population)) continue;
395 const T extRate = sn.rates(sourceIdx - 1, r);
396 T caExt = one;
397 if (sn.scv(sourceIdx - 1, r) > zero) caExt = sn.scv(sourceIdx - 1, r);
398 for (std::size_t k = 0; k < Mq; ++k) {
399 const std::size_t dn = sn.node_of_station(qs[k]);
400 const T p = sn.rtnodes((sourceNode - 1) * R + r, (dn - 1) * R + r);
401 if (p > zero) {
402 lambda0(k, r) = T(extRate * p);
403 Ca0(k, r) = caExt;
404 }
405 }
406 }
407
408 if (sup.blocking) {
409 // Finite buffers: the censored GE/GE/c/0;N blocks, with the
410 // holding-node expansion where the drop rule is BAS. Single class
411 // by construction -- the gate above rejects a multiclass model with
412 // a binding buffer, because the censored block is single class.
413 std::vector<long> Nbuf(Mq, 0), c_blk(Mq, 1);
414 std::vector<int> blockrule(Mq, 0);
415 for (std::size_t k = 0; k < Mq; ++k) {
416 const std::size_t i = qs[k] - 1;
417 const double nb = detail::buffer_size(sn, qs[k]);
418 // 0 marks an unbounded buffer for me_oqn_blk, where MATLAB uses
419 // Inf; likewise 0 servers marks an infinite server.
420 Nbuf[k] = std::isfinite(nb) ? static_cast<long>(std::llround(nb)) : 0;
421 c_blk[k] = std::isinf(sn.stations[i].nservers)
422 ? 0
423 : static_cast<long>(std::llround(sn.stations[i].nservers));
424 const int dr = sn.stations[i].droprule.empty() ? 0 : sn.stations[i].droprule[0];
425 blockrule[k] = (dr == static_cast<int>(lang::DropStrategy::BAS)) ? 1 : 0;
426 }
427 std::vector<T> l0(Mq, zero), ca0(Mq, one), mu1(Mq, zero), cs1(Mq, one);
428 for (std::size_t k = 0; k < Mq; ++k) {
429 l0[k] = lambda0(k, 0);
430 ca0[k] = Ca0(k, 0) > zero ? Ca0(k, 0) : one;
431 mu1[k] = mu(k, 0);
432 cs1[k] = Cs(k, 0);
433 }
434 Matrix<T> P1(Mq, Mq, zero);
435 {
436 const std::vector<Matrix<T>> Pall = fill_routing(qs);
437 for (std::size_t a = 0; a < Mq; ++a)
438 for (std::size_t b = 0; b < Mq; ++b) P1(a, b) = Pall[0](a, b);
439 }
440 me::MeBlkOptions bopt;
441 bopt.tol = opt.mem_tol;
442 bopt.maxiter = opt.mem_maxiter;
443 const me::MeBlkResult<T> br =
444 me::me_oqn_blk(Mq, l0, ca0, mu1, cs1, P1, c_blk, Nbuf, blockrule, bopt);
445 for (std::size_t k = 0; k < Mq; ++k) {
446 out.sol.Q(qs[k] - 1, 0) = br.Q[k];
447 out.sol.U(qs[k] - 1, 0) = br.U[k];
448 out.sol.R(qs[k] - 1, 0) = br.W[k];
449 out.sol.Tp(qs[k] - 1, 0) = br.T_[k];
450 }
451 // The Source emits at its NOMINAL rate; the jobs lost at a full
452 // buffer never reach a station, so the carried flow per station is
453 // below it by the loss probability at the entry stations. Only a
454 // LOSS station discards -- under BAS the job is held, not dropped.
455 if (!sn.disabled[sourceIdx - 1][0] && sn.rates(sourceIdx - 1, 0) > zero) {
456 const T x = sn.rates(sourceIdx - 1, 0);
457 out.sol.Tp(sourceIdx - 1, 0) = x;
458 out.sol.X[0] = x;
459 T accepted = x;
460 for (std::size_t k = 0; k < Mq; ++k)
461 if (l0[k] > zero && blockrule[k] == 0)
462 accepted -= T(l0[k] * br.PBa[k]);
463 if (accepted > zero) {
464 T q = zero;
465 for (std::size_t k = 0; k < Mq; ++k) q += out.sol.Q(qs[k] - 1, 0);
466 out.sol.C[0] = T(q / accepted);
467 }
468 }
469 out.sol.iter = static_cast<int>(br.iter);
470 out.sol.method = "mem.blocking";
471 out.actualmethod = "mem.blocking";
472 return out;
473 }
474
475 me::MeResult<T> res;
476 if (isopen) {
477 res = me::me_oqn(Mq, R, lambda0, Ca0, mu, Cs, fill_routing(qs), c, fill_insens(qs),
478 mopt);
479 } else {
480 std::vector<char> openCls(R, 0);
481 std::vector<long> refstat(R, -1);
482 for (std::size_t r = 0; r < R; ++r) {
483 openCls[r] = std::isinf(sn.classes[r].population) ? 1 : 0;
484 if (!openCls[r]) {
485 const auto it = std::find(qs.begin(), qs.end(), sn.classes[r].refstat);
486 if (it != qs.end())
487 refstat[r] = static_cast<long>(it - qs.begin());
488 }
489 }
490 res = me::me_mqn(Mq, R, openCls, lambda0, Ca0, N, mu, Cs, fill_routing(qs), c, refstat,
491 fill_insens(qs), mopt);
492 }
493
494 for (std::size_t k = 0; k < Mq; ++k)
495 for (std::size_t r = 0; r < R; ++r) {
496 out.sol.Q(qs[k] - 1, r) = res.L(k, r);
497 out.sol.U(qs[k] - 1, r) = res.rho(k, r);
498 out.sol.R(qs[k] - 1, r) = res.W(k, r);
499 out.sol.Tp(qs[k] - 1, r) = res.lambda(k, r);
500 }
501
502 // An unstable station's utilization is normalized to sum 1, the LINE
503 // convention shared with the product-form runner.
504 for (std::size_t k = 0; k < Mq; ++k) {
505 const std::size_t i = qs[k] - 1;
506 if (!std::isfinite(sn.stations[i].nservers)) continue;
507 T tot = zero;
508 for (std::size_t r = 0; r < R; ++r) tot += out.sol.U(i, r);
509 if (num_traits<T>::to_double(tot) > 1.0)
510 for (std::size_t r = 0; r < R; ++r) out.sol.U(i, r) = T(out.sol.U(i, r) / tot);
511 }
512
513 for (std::size_t r = 0; r < R; ++r) {
514 const bool open = std::isinf(sn.classes[r].population);
515 if (open) {
516 // The Source reports the external rate; the system response
517 // time follows by Little's law over the queueing stations.
518 const T x = isopen ? (sn.disabled[sourceIdx - 1][r] ? zero
519 : sn.rates(sourceIdx - 1, r))
520 : res.X[r];
521 if (!(x > zero)) continue;
522 out.sol.Tp(sourceIdx - 1, r) = x;
523 out.sol.X[r] = x;
524 T q = zero;
525 for (std::size_t k = 0; k < Mq; ++k) q += out.sol.Q(qs[k] - 1, r);
526 out.sol.C[r] = T(q / x);
527 } else {
528 out.sol.X[r] = res.X[r];
529 if (res.X[r] > zero)
530 out.sol.C[r] =
531 T(num_traits<T>::from_double(static_cast<double>(N[r])) / res.X[r]);
532 }
533 }
534 out.sol.iter = static_cast<int>(res.iter);
535 return out;
536 }
537}
538
539} // namespace nc
540} // namespace line
541
542#endif // LINE_SOLVERS_NC_SOLVER_NC_MEM_H
UnsupportedError(const std::string &what)
Definition error.h:51
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Dense matrix and non-owning view.
Maximum-entropy algorithm for closed multiclass queueing networks.
Maximum-entropy algorithm for mixed open/closed multiclass networks.
Maximum-entropy algorithm for open multiclass queueing networks.
Maximum Entropy algorithm for single-class OPEN networks with FINITE BUFFERS, under loss or transfer ...
The option and result types every MVA analyzer shares.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Definition lang_types.h:181
MeResult< T > me_mqn(std::size_t M, std::size_t R, const std::vector< char > &open_classes, const Matrix< T > &lambda0, const Matrix< T > &Ca0, const std::vector< long > &N, const Matrix< T > &mu, const Matrix< T > &Cs, const std::vector< Matrix< T > > &P, const std::vector< long > &c, const std::vector< long > &refstat, const std::vector< char > &insens, const MeOptions &opt=MeOptions())
Maximum-entropy algorithm for mixed open/closed multiclass networks.
Definition me_mqn.h:78
MeResult< T > me_cqn(std::size_t M, std::size_t R, const std::vector< long > &N, const Matrix< T > &mu, const Matrix< T > &Cs, const std::vector< Matrix< T > > &P, const std::vector< long > &c, const std::vector< long > &refstat_in, const std::vector< char > &insens, const MeOptions &opt=MeOptions())
Maximum-entropy algorithm for closed multiclass queueing networks.
Definition me_cqn.h:463
MeBlkResult< T > me_oqn_blk(std::size_t M, const std::vector< T > &lambda0, const std::vector< T > &Ca0, const std::vector< T > &mu, const std::vector< T > &Cs, const Matrix< T > &P, const std::vector< long > &c, const std::vector< long > &N, const std::vector< int > &blockrule, const MeBlkOptions &opt=MeBlkOptions())
Port of me_oqn_blk.
Definition me_oqn_blk.h:113
MeResult< T > me_oqn(std::size_t M, std::size_t R, const Matrix< T > &lambda0, const Matrix< T > &Ca0, const Matrix< T > &mu, const Matrix< T > &Cs, const std::vector< Matrix< T > > &P, const std::vector< long > &c, const std::vector< char > &insens, const MeOptions &opt=MeOptions())
Maximum-entropy algorithm for open multiclass queueing networks.
Definition me_oqn.h:121
NcSolution< T > solver_nc_mem(const qn::NetworkStruct< T > &sn, const NcSolverOptions &opt)
Port of solver_nc_mem.m.
MemSupport solver_nc_mem_supports(const qn::NetworkStruct< T > &sn)
Port of solver_nc_mem_supports.m.
double sn_get_buffer_size(const qn::NetworkStruct< T > &sn, std::size_t ist)
Physical buffer size of a station, in jobs, the one in service included.
Controls and result shape shared by the normalizing-constant analyzers.
A queueing network and its refreshed NetworkStruct.
Physical buffer size of a station, in jobs, the one in service included.
Port of solver_nc.m: the load-INDEPENDENT normalizing-constant analyzer.
Controls of the blocking fixed point, me_oqn_blk's options struct.
Definition me_oqn_blk.h:73
What me_oqn_blk returns, per station.
Definition me_oqn_blk.h:82
std::vector< T > U
utilization on LINE's convention (see the header)
Definition me_oqn_blk.h:86
std::vector< T > Q
mean number present, the jobs held blocked included
Definition me_oqn_blk.h:83
std::vector< T > W
mean response time, Q / T
Definition me_oqn_blk.h:84
std::vector< T > T_
throughput, the CARRIED flow
Definition me_oqn_blk.h:85
std::vector< T > PBa
probability an arrival finds the station full
Definition me_oqn_blk.h:89
Iteration control, mirroring the MATLAB options struct.
Definition me_types.h:58
Mean-value results shared by the open, closed and mixed algorithms.
Definition me_types.h:157
Matrix< T > rho
utilizations (M x R)
Definition me_types.h:163
Matrix< T > L
mean queue lengths (M x R)
Definition me_types.h:158
Matrix< T > W
mean response times (M x R)
Definition me_types.h:159
std::vector< T > X
class throughputs (R)
Definition me_types.h:164
long iter
fixed-point iterations performed
Definition me_types.h:165
Matrix< T > lambda
per-station throughputs (M x R)
Definition me_types.h:162
The verdict of solver_nc_mem_supports.
std::string reason
names the first violated rule; empty when supported
bool blocking
the model carries a binding finite station buffer
The [Q,U,R,T,C,X,lG] of the reference, plus the algorithm that ran.
Definition nc_types.h:113
mva::MvaSolution< T > sol
Definition nc_types.h:114
std::string actualmethod
Definition nc_types.h:115
Controls, defaulting to SolverOptions('NC') in the reference.
Definition nc_types.h:33
One job class of the network.
double population
infinite for an open class
A node of the network.