LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sn_predicates.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_API_SN_SN_PREDICATES_H
6#define LINE_API_SN_SN_PREDICATES_H
7
8/**
9 * @file
10 * @ingroup api_sn
11 * Ports of the sn_has_* / sn_is_* predicate family of matlab/src/api/sn.
12 *
13 * The reference keeps each predicate in its own one-line .m file; collecting
14 * them in one header keeps the port readable and, more importantly, keeps the
15 * DEFINITIONS in one place. Several of these predicates gate solver dispatch,
16 * so a predicate that quietly differs from the reference does not raise an
17 * error -- it routes the model to a different algorithm and returns a
18 * different number.
19 *
20 * NetworkStruct carries member functions with some of these names. Where both
21 * exist the free function here is the reference-faithful one and the member is
22 * a convenience used inside the struct's own refresh; sn_has_product_form is
23 * the case where the two DISAGREE, see the note on that function.
24 * sn_has_blocking and sn_is_mm1k_loss are the other way round: the SOLVERS read
25 * them through NetworkStruct::has_product_form, so the body lives on the struct
26 * and these forward to it rather than carrying a second copy.
27 *
28 * ARITHMETIC: field. Every test is a comparison; nothing here is
29 * transcendental, so the whole family instantiates under Rational.
30 */
31
32#include <cmath>
33#include <cstddef>
34#include <limits>
35#include <vector>
36
40#include "line/num/number.h"
41
42namespace line {
43namespace api {
44
45namespace detail {
46
47/** True when some station runs `s`, the shape shared by the sn_has_<sched> family. */
48template <class T>
49bool sn_any_sched(const qn::NetworkStruct<T>& sn, qn::SchedStrategy s) {
50 for (std::size_t i = 0; i < sn.stations.size(); ++i)
51 if (sn.stations[i].sched == s) return true;
52 return false;
53}
54
55} // namespace detail
56
57// ---- the sn_has_<discipline> family ------------------------------------
58
59template <class T>
61 return detail::sn_any_sched(sn, qn::SchedStrategy::DPS);
62}
63template <class T>
65 return detail::sn_any_sched(sn, qn::SchedStrategy::DPSPRIO);
66}
67template <class T>
69 return detail::sn_any_sched(sn, qn::SchedStrategy::FCFS);
70}
71template <class T>
73 return detail::sn_any_sched(sn, qn::SchedStrategy::GPS);
74}
75template <class T>
77 return detail::sn_any_sched(sn, qn::SchedStrategy::GPSPRIO);
78}
79template <class T>
81 return detail::sn_any_sched(sn, qn::SchedStrategy::HOL);
82}
83template <class T>
85 return detail::sn_any_sched(sn, qn::SchedStrategy::INF);
86}
87template <class T>
89 return detail::sn_any_sched(sn, qn::SchedStrategy::LCFS);
90}
91template <class T>
93 return detail::sn_any_sched(sn, qn::SchedStrategy::LCFSPI);
94}
95template <class T>
97 return detail::sn_any_sched(sn, qn::SchedStrategy::LCFSPR);
98}
99template <class T>
101 return detail::sn_any_sched(sn, qn::SchedStrategy::LEPT);
102}
103template <class T>
105 return detail::sn_any_sched(sn, qn::SchedStrategy::LJF);
106}
107template <class T>
109 return detail::sn_any_sched(sn, qn::SchedStrategy::LPS);
110}
111template <class T>
113 return detail::sn_any_sched(sn, qn::SchedStrategy::POLLING);
114}
115template <class T>
117 return detail::sn_any_sched(sn, qn::SchedStrategy::PS);
118}
119template <class T>
121 return detail::sn_any_sched(sn, qn::SchedStrategy::PSPRIO);
122}
123template <class T>
125 return detail::sn_any_sched(sn, qn::SchedStrategy::SEPT);
126}
127template <class T>
129 return detail::sn_any_sched(sn, qn::SchedStrategy::SETF);
130}
131template <class T>
133 return detail::sn_any_sched(sn, qn::SchedStrategy::SIRO);
134}
135template <class T>
137 return detail::sn_any_sched(sn, qn::SchedStrategy::SJF);
138}
139template <class T>
141 return detail::sn_any_sched(sn, qn::SchedStrategy::SRPT);
142}
143
144/**
145 * Port of sn_has_homogeneous_scheduling.
146 *
147 * The reference is `length(findstring(sn.sched, strategy)) == sn.nstations`,
148 * and findstring compares STRINGS: against the numeric sched vector its strcmp
149 * is false everywhere, so it returns the sentinel -1, whose length is 1. The
150 * predicate therefore reduces to nstations == 1 whatever the disciplines are.
151 * Reproduced and not corrected: this is the branch every codebase takes.
152 */
153template <class T>
155 return sn.nstations == 1;
156}
157
158// ---- class and chain counts -------------------------------------------
159
160template <class T>
162 for (std::size_t k = 0; k < sn.classes.size(); ++k)
163 if (std::isinf(sn.classes[k].population)) return true;
164 return false;
165}
166
167template <class T>
169 for (std::size_t k = 0; k < sn.classes.size(); ++k)
170 if (std::isfinite(sn.classes[k].population)) return true;
171 return false;
172}
173
174template <class T>
176 std::size_t n = 0;
177 for (std::size_t k = 0; k < sn.classes.size(); ++k)
178 if (std::isfinite(sn.classes[k].population)) ++n;
179 return n > 1;
180}
181
182template <class T>
186
187template <class T>
189 return sn.nchains > 1;
190}
191template <class T>
193 return sn.nchains == 1;
194}
195template <class T>
197 return sn.nclasses > 1;
198}
199template <class T>
201 return sn.nclasses == 1;
202}
203template <class T>
205 return sn.nclasses != sn.nchains;
206}
207
208/** `all(isinf(sn.njobs))`: EVERY class is open, which a mixed model fails. */
209template <class T>
211 for (std::size_t k = 0; k < sn.classes.size(); ++k)
212 if (!std::isinf(sn.classes[k].population)) return false;
213 return true;
214}
215
216/** `all(isfinite(sn.njobs))`: EVERY class is closed. */
217template <class T>
219 for (std::size_t k = 0; k < sn.classes.size(); ++k)
220 if (!std::isfinite(sn.classes[k].population)) return false;
221 return true;
222}
223
224template <class T>
228
229// ---- structural predicates --------------------------------------------
230
231template <class T>
233 for (std::size_t k = 0; k < sn.classes.size(); ++k)
234 if (sn.classes[k].prio > 0) return true;
235 return false;
236}
237
238template <class T>
240 for (std::size_t i = 0; i < sn.stations.size(); ++i)
241 if (std::isfinite(sn.stations[i].nservers) && sn.stations[i].nservers > 1.0) return true;
242 return false;
243}
244
245/** `any(sn.njobs ~= round(sn.njobs))`; an infinite population rounds to itself. */
246template <class T>
248 for (std::size_t k = 0; k < sn.classes.size(); ++k) {
249 const double n = sn.classes[k].population;
250 if (std::isfinite(n) && n != std::floor(n + 0.5)) return true;
251 }
252 return false;
253}
254
255/** `size(sn.lldscaling,2)>0`: some station carries a limited-load scaling row. */
256template <class T>
258 for (std::size_t i = 0; i < sn.stations.size(); ++i)
259 if (!sn.stations[i].lldscaling.empty()) return true;
260 return false;
261}
262
263/**
264 * `any(sn.fj(:) > 0)`: the model has a Fork closed by a Join.
265 *
266 * `sn.fj` is the (fork, join) pair list here, so non-empty is the predicate.
267 * It is populated on the UN-augmented struct too, exactly as MATLAB's fj
268 * matrix is, so this agrees with the reference before and after fj_tag.
269 */
270template <class T>
272 return !sn.fj.empty();
273}
274
275/**
276 * `sn_has_sd_routing`: some node dispatches on the STATE of the network.
277 *
278 * RROBIN, WRROBIN, JSQ and SQ. RROBIN matters even though the refresh spreads
279 * its probabilities uniformly: the uniform matrix is the right MEAN and the
280 * wrong higher moments, so a round-robin model is not product form and must
281 * not be handed to exact MVA or to the convolution algorithm.
282 */
283template <class T>
285 for (std::size_t a = 0; a < sn.nodes.size(); ++a)
286 for (std::size_t r = 0; r < sn.nodes[a].routing.size(); ++r) {
287 const qn::RoutingStrategy s = sn.nodes[a].routing[r];
288 if (s == qn::RoutingStrategy::RROBIN || s == qn::RoutingStrategy::WRROBIN ||
289 s == qn::RoutingStrategy::JSQ || s == qn::RoutingStrategy::SQ ||
290 s == qn::RoutingStrategy::SDR)
291 return true;
292 }
293 return false;
294}
295
296/** `sum(sn.rates(i,:)>0)>1` at some FCFS station: more than one class is served there. */
297template <class T>
299 const T zero = num_traits<T>::from_int(0);
300 for (std::size_t i = 0; i < sn.nstations; ++i) {
301 if (sn.stations[i].sched != qn::SchedStrategy::FCFS) continue;
302 std::size_t n = 0;
303 for (std::size_t r = 0; r < sn.nclasses; ++r) {
304 if (!sn.disabled.empty() && sn.disabled[i][r]) continue;
305 if (sn.rates(i, r) > zero) ++n;
306 }
307 if (n > 1) return true;
308 }
309 return false;
310}
311
312/**
313 * `sn_has_multi_class_heter_fcfs`: an FCFS station whose per-class rates differ.
314 *
315 * MATLAB filters the row with `isfinite(rates) & ~isnan(rates)`, which drops
316 * BOTH the NaN of a disabled pair and the Inf of an Immediate service. The
317 * disabled flag covers the first; the isfinite test below covers the second,
318 * without which an Immediate-served class would make every FCFS station look
319 * heterogeneous and take product form away from the model.
320 */
321template <class T>
323 for (std::size_t i = 0; i < sn.nstations; ++i) {
324 if (sn.stations[i].sched != qn::SchedStrategy::FCFS) continue;
325 bool any = false;
326 double lo = 0.0, hi = 0.0;
327 for (std::size_t r = 0; r < sn.nclasses; ++r) {
328 if (!sn.disabled.empty() && sn.disabled[i][r]) continue;
329 const double v = num_traits<T>::to_double(sn.rates(i, r));
330 if (!std::isfinite(v)) continue;
331 if (!any) {
332 lo = hi = v;
333 any = true;
334 } else {
335 if (v < lo) lo = v;
336 if (v > hi) hi = v;
337 }
338 }
339 if (any && hi - lo > 0.0) return true;
340 }
341 return false;
342}
343
344/** An FCFS station with heterogeneous rates whose SCVs are all one (exponential). */
345template <class T>
347 for (std::size_t i = 0; i < sn.nstations; ++i) {
348 if (sn.stations[i].sched != qn::SchedStrategy::FCFS) continue;
349 // MATLAB tests `range(sn.rates(i,:)) > 0`, and range's max and min skip
350 // NaN but keep Inf -- unlike sn_has_multi_class_heter_fcfs, which
351 // filters on isfinite. The two predicates really do differ there.
352 bool any = false;
353 double lo = 0.0, hi = 0.0;
354 for (std::size_t r = 0; r < sn.nclasses; ++r) {
355 if (!sn.disabled.empty() && sn.disabled[i][r]) continue;
356 const double v = num_traits<T>::to_double(sn.rates(i, r));
357 if (std::isnan(v)) continue;
358 if (!any) {
359 lo = hi = v;
360 any = true;
361 } else {
362 if (v < lo) lo = v;
363 if (v > hi) hi = v;
364 }
365 }
366 if (!(any && hi - lo > 0.0)) continue;
367 // max/min over the scv row also skip NaN, so a disabled pair neither
368 // qualifies nor disqualifies the station; an all-disabled row leaves
369 // max([]) empty and the reference's `if` false, hence `seen`.
370 bool allone = true, seen = false;
371 for (std::size_t r = 0; r < sn.nclasses && allone; ++r) {
372 if (!sn.disabled.empty() && sn.disabled[i][r]) continue;
373 const double c = num_traits<T>::to_double(sn.scv(i, r));
374 if (std::isnan(c)) continue;
375 seen = true;
376 if (!(c < 1.0 + qn::GlobalConstants::FineTol && c > 1.0 - qn::GlobalConstants::FineTol))
377 allone = false;
378 }
379 if (seen && allone) return true;
380 }
381 return false;
382}
383
384/** The disciplines the reference admits in sn_has_product_form. */
385template <class T>
387 for (std::size_t i = 0; i < sn.stations.size(); ++i) {
388 const qn::SchedStrategy s = sn.stations[i].sched;
389 if (!(s == qn::SchedStrategy::INF || s == qn::SchedStrategy::PS ||
390 s == qn::SchedStrategy::FCFS || s == qn::SchedStrategy::LCFSPR ||
391 s == qn::SchedStrategy::LCFS || s == qn::SchedStrategy::EXT))
392 return false;
393 }
394 return true;
395}
396
397/** Defined below, after sn_is_mm1k_loss, which it exempts. */
398template <class T>
400
401/**
402 * Port of sn_has_product_form.
403 *
404 * NOTE. `NetworkStruct::has_product_form` omits the fork-join and the
405 * state-dependent-routing conjuncts the reference carries, so it answers TRUE
406 * on a model with a surviving Fork or a round-robin dispatcher. That is not a
407 * cosmetic difference: the predicate gates exact MVA, the NC convolution and
408 * the AUTO solver tree, so the omission silently hands a non-product-form
409 * model to an exact algorithm. This free function is the reference-faithful
410 * one and callers in the api layer must use it.
411 */
412template <class T>
414 // BCMP asks for infinite buffers. Nothing here read sn.cap/sn.classcap/
415 // sn.droprule, so a BAS-blocked station or any binding finite buffer passed the
416 // gate and the network read as product form while its truncation couples the
417 // station occupancies.
420 !sn_has_blocking(sn) && sn.has_exponential_fcfs();
421}
422
423/**
424 * Port of sn_has_product_form_not_het_fcfs: LCFS is out, and every enabled
425 * FCFS pair with a finite positive SCV must have that SCV equal to one, with
426 * the service means agreeing across the classes served there.
427 *
428 * CHECK_MEANS drops that second half; pass false only for an algorithm that
429 * models class-dependent FCFS itself (ab, schmidt, schmidt-ext).
430 */
431template <class T>
432bool sn_has_product_form_not_het_fcfs(const qn::NetworkStruct<T>& sn, bool check_means = true) {
433 for (std::size_t i = 0; i < sn.stations.size(); ++i) {
434 const qn::SchedStrategy s = sn.stations[i].sched;
435 if (!(s == qn::SchedStrategy::INF || s == qn::SchedStrategy::PS ||
436 s == qn::SchedStrategy::FCFS || s == qn::SchedStrategy::LCFSPR ||
437 s == qn::SchedStrategy::EXT))
438 return false;
439 }
440 if (sn_has_priorities(sn) || sn_has_fork_join(sn) || sn_has_sd_routing(sn)) return false;
441 for (std::size_t i = 0; i < sn.nstations; ++i) {
442 if (sn.stations[i].sched != qn::SchedStrategy::FCFS) continue;
443 // BCMP type 1 asks the FCFS service to be exponential AND
444 // class-independent, so the means are checked alongside the SCVs: with
445 // unequal means the product-form solve returns a wait proportional to
446 // each class's own demand where FCFS makes every class wait behind the
447 // same queue. The mean comparison is between CHAIN service times
448 // (visit-weighted over the classes that actually visit the station): a
449 // class that never visits cannot break product form, and within-chain
450 // heterogeneity is invisible to both the product-form and the qd
451 // branch, which deaggregate a chain result proportionally to each
452 // class's own demand, so only between-chain heterogeneity warrants the
453 // divert. LN layers carry seeded rates for classes with zero visits,
454 // which a raw per-class comparison mistakes for heterogeneity.
455 for (std::size_t r = 0; r < sn.nclasses; ++r) {
456 if (!sn.disabled.empty() && sn.disabled[i][r]) continue;
457 const double v = num_traits<T>::to_double(sn.scv(i, r));
458 if (std::isfinite(v) && v > 0.0 &&
460 return false;
461 }
462 if (!check_means || sn.visits.empty()) continue;
463 const std::size_t isf = sn.stateful_of_station(i + 1) - 1;
464 double stmin = 0.0, stmax = 0.0;
465 bool anyserved = false;
466 for (std::size_t c = 0; c < sn.nchains && c < sn.visits.size(); ++c) {
467 double num = 0.0, den = 0.0;
468 for (std::size_t r = 0; r < sn.nclasses; ++r) {
469 if (!sn.chains.empty() && !sn.chains[c][r]) continue;
470 if (!sn.disabled.empty() && sn.disabled[i][r]) continue;
471 const double w = num_traits<T>::to_double(sn.visits[c](isf, r));
472 const double rate = num_traits<T>::to_double(sn.rates(i, r));
473 if (w > qn::GlobalConstants::Zero && std::isfinite(rate) && rate > 0.0) {
474 num += w / rate;
475 den += w;
476 }
477 }
478 if (den > 0.0) {
479 const double st = num / den;
480 if (!anyserved) {
481 stmin = stmax = st;
482 anyserved = true;
483 } else {
484 stmin = std::min(stmin, st);
485 stmax = std::max(stmax, st);
486 }
487 }
488 }
489 if (anyserved && stmax - stmin > qn::GlobalConstants::CoarseTol * stmax) return false;
490 }
491 return true;
492}
493
494/**
495 * `sn_is_population_model`: every station is population dependent only, which
496 * is what lets the fluid and mean-field limits close on the queue lengths.
497 */
498template <class T>
500 for (std::size_t i = 0; i < sn.stations.size(); ++i) {
501 const qn::SchedStrategy s = sn.stations[i].sched;
502 if (!(s == qn::SchedStrategy::INF || s == qn::SchedStrategy::PS ||
503 s == qn::SchedStrategy::PSPRIO || s == qn::SchedStrategy::DPS ||
504 s == qn::SchedStrategy::GPS || s == qn::SchedStrategy::GPSPRIO ||
505 s == qn::SchedStrategy::DPSPRIO || s == qn::SchedStrategy::EXT))
506 return false;
507 }
509}
510
511/**
512 * `sn_is_bas_model`: a single-class closed model with blocking-after-service.
513 */
514template <class T>
516 if (sn.nclasses != 1) return false;
517 double closed = 0.0;
518 for (std::size_t k = 0; k < sn.classes.size(); ++k) {
519 if (std::isinf(sn.classes[k].population)) return false; // open class present
520 closed += sn.classes[k].population;
521 }
522 if (!(closed > 0.0)) return false;
523 // `sn.droprule`, the REFRESHED (nstations x nclasses) table, not the
524 // per-station user declaration: refresh_bas_blocking propagates a
525 // destination-declared BAS rule into the table, and reading the
526 // declaration would miss exactly those models
527 for (std::size_t i = 0; i < sn.droprule.size(); ++i)
528 for (std::size_t r = 0; r < sn.droprule[i].size(); ++r)
529 if (sn.droprule[i][r] == qn::DropStrategy::BAS) return true;
530 return false;
531}
532
533/**
534 * `sn_is_mm1k_loss`: the three-node Source/Queue/Sink model of an M/M/1/K with
535 * loss, the shape MVA answers in closed form instead of iterating.
536 */
537template <class T>
539 return sn.is_mm1k_loss();
540}
541
542/**
543 * Port of sn_has_blocking: some station can REFUSE a job.
544 *
545 * Either its own buffer binds -- Kendall's K below the population that can
546 * reach it, whatever the drop rule (WAITQ, DROP, BAS, BBS, RSRD) -- or a finite
547 * capacity region caps a set of stations jointly. Such a network is not product
548 * form: the truncation couples the station occupancies, so no BCMP
549 * factorization of the equilibrium distribution exists.
550 *
551 * Only a buffer that can actually BIND counts, which is what
552 * sn_get_buffer_size decides; refresh_capacity derives a finite classcap (the
553 * chain population) at every station of every closed model, so a plain
554 * finiteness test would call every closed model blocking.
555 *
556 * A Cache is exempt: it builds its own capped retrieval queues (classcap 1),
557 * which the cache analyzers solve rather than treat as a buffer constraint. So
558 * is the single-station M/M/1/K loss system, whose truncated geometric
559 * distribution is a product form over its one station.
560 */
561template <class T>
563 return sn.has_blocking();
564}
565
566} // namespace api
567} // namespace line
568
569#endif // LINE_API_SN_SN_PREDICATES_H
A network plus its refreshed NetworkStruct.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
bool sn_has_siro(const qn::NetworkStruct< T > &sn)
bool sn_has_multi_class_heter_fcfs(const qn::NetworkStruct< T > &sn)
sn_has_multi_class_heter_fcfs: an FCFS station whose per-class rates differ.
bool sn_has_hol(const qn::NetworkStruct< T > &sn)
bool sn_has_mixed_classes(const qn::NetworkStruct< T > &sn)
bool sn_has_ljf(const qn::NetworkStruct< T > &sn)
bool sn_has_multi_chain(const qn::NetworkStruct< T > &sn)
bool sn_has_ps(const qn::NetworkStruct< T > &sn)
bool sn_has_multi_class_fcfs(const qn::NetworkStruct< T > &sn)
sum(sn.rates(i,:)>0)>1 at some FCFS station: more than one class is served there.
bool sn_is_open_model(const qn::NetworkStruct< T > &sn)
all(isinf(sn.njobs)): EVERY class is open, which a mixed model fails.
bool sn_has_lcfs_pi(const qn::NetworkStruct< T > &sn)
bool sn_has_closed_classes(const qn::NetworkStruct< T > &sn)
bool sn_has_product_form(const qn::NetworkStruct< T > &sn)
Port of sn_has_product_form.
bool sn_has_ps_prio(const qn::NetworkStruct< T > &sn)
bool sn_has_sept(const qn::NetworkStruct< T > &sn)
bool sn_has_homogeneous_scheduling(const qn::NetworkStruct< T > &sn, qn::SchedStrategy)
Port of sn_has_homogeneous_scheduling.
bool sn_has_dps(const qn::NetworkStruct< T > &sn)
bool sn_has_load_dependence(const qn::NetworkStruct< T > &sn)
size(sn.lldscaling,2)>0: some station carries a limited-load scaling row.
bool sn_has_single_class(const qn::NetworkStruct< T > &sn)
bool sn_has_multiple_closed_classes(const qn::NetworkStruct< T > &sn)
bool sn_has_srpt(const qn::NetworkStruct< T > &sn)
bool sn_has_lps(const qn::NetworkStruct< T > &sn)
bool sn_has_sd_routing(const qn::NetworkStruct< T > &sn)
sn_has_sd_routing: some node dispatches on the STATE of the network.
bool sn_is_population_model(const qn::NetworkStruct< T > &sn)
sn_is_population_model: every station is population dependent only, which is what lets the fluid and ...
bool sn_has_class_switching(const qn::NetworkStruct< T > &sn)
bool sn_is_closed_model(const qn::NetworkStruct< T > &sn)
all(isfinite(sn.njobs)): EVERY class is closed.
bool sn_is_mixed_model(const qn::NetworkStruct< T > &sn)
bool sn_has_product_form_not_het_fcfs(const qn::NetworkStruct< T > &sn, bool check_means=true)
Port of sn_has_product_form_not_het_fcfs: LCFS is out, and every enabled FCFS pair with a finite posi...
bool sn_has_sjf(const qn::NetworkStruct< T > &sn)
bool sn_is_bas_model(const qn::NetworkStruct< T > &sn)
sn_is_bas_model: a single-class closed model with blocking-after-service.
bool sn_has_lcfs_pr(const qn::NetworkStruct< T > &sn)
bool sn_has_fractional_populations(const qn::NetworkStruct< T > &sn)
any(sn.njobs ~= round(sn.njobs)); an infinite population rounds to itself.
bool sn_has_lcfs(const qn::NetworkStruct< T > &sn)
bool sn_has_multi_server(const qn::NetworkStruct< T > &sn)
bool sn_has_multi_class(const qn::NetworkStruct< T > &sn)
bool sn_has_priorities(const qn::NetworkStruct< T > &sn)
bool sn_has_dps_prio(const qn::NetworkStruct< T > &sn)
bool sn_has_multi_class_heter_exp_fcfs(const qn::NetworkStruct< T > &sn)
An FCFS station with heterogeneous rates whose SCVs are all one (exponential).
bool sn_has_fork_join(const qn::NetworkStruct< T > &sn)
any(sn.fj(:) > 0): the model has a Fork closed by a Join.
bool sn_has_blocking(const qn::NetworkStruct< T > &sn)
Defined below, after sn_is_mm1k_loss, which it exempts.
bool sn_has_setf(const qn::NetworkStruct< T > &sn)
bool sn_has_lept(const qn::NetworkStruct< T > &sn)
bool sn_has_gps_prio(const qn::NetworkStruct< T > &sn)
bool sn_has_inf(const qn::NetworkStruct< T > &sn)
bool sn_has_polling(const qn::NetworkStruct< T > &sn)
bool sn_has_single_chain(const qn::NetworkStruct< T > &sn)
bool sn_has_open_classes(const qn::NetworkStruct< T > &sn)
bool sn_has_fcfs(const qn::NetworkStruct< T > &sn)
bool sn_has_gps(const qn::NetworkStruct< T > &sn)
bool sn_sched_is_product_form(const qn::NetworkStruct< T > &sn)
The disciplines the reference admits in sn_has_product_form.
bool sn_is_mm1k_loss(const qn::NetworkStruct< T > &sn)
sn_is_mm1k_loss: the three-node Source/Queue/Sink model of an M/M/1/K with loss, the shape MVA answer...
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Definition lang_types.h:181
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
Definition lang_types.h:389
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Physical buffer size of a station, in jobs, the one in service included.
static constexpr double FineTol
Definition lang_types.h:668
static constexpr double Zero
Definition lang_types.h:670
static constexpr double CoarseTol
Definition lang_types.h:669