LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_ba_qrf_analyzer.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_BA_SOLVER_BA_QRF_ANALYZER_H
6#define LINE_SOLVERS_BA_SOLVER_BA_QRF_ANALYZER_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * Port of `matlab/src/solvers/BA/solver_ba_qrf_analyzer.m`, the adapter that
12 * bridges the `sn` struct to the QRF (Quadratic Reduction Framework) bounds.
13 *
14 * WHAT THE ADAPTER IS FOR. `qrf_noblo_*` speaks in MAPs, phase counts and a
15 * station-to-station routing matrix, not in a NetworkStruct; the whole of this
16 * file is that translation plus the reconstruction of [Q,U,R,T,C,X] from the
17 * utilizations the optimizer returns. The optimization itself lives in
18 * `api/mapqn/mapqn_qrf_noblo.h` and is not repeated here.
19 *
20 * WHAT IT REFUSES AND WHY. The `qrf_noblo_*` formulation models every station as
21 * ONE server and has no infinite-server notion, so a delay station reaches the
22 * program as an unbounded direction and a c>1 station solved as c=1 is not a
23 * bound in either direction. Both are refused by name, as the reference and the
24 * Python twin do, rather than answered wrongly.
25 *
26 * THE THROUGHPUT COMES FROM A UTILIZATION, NOT FROM THE REFERENCE STATION.
27 * `UN_qrf(i)` is P(n_i >= 1) marginalised over phase, so `U_i = X V_i S_i` is
28 * exact at a single server and any loaded station determines X. Inverting
29 * instead at the reference station assumes R == S there, which holds only for
30 * an infinite server and returned an X above the bottleneck capacity.
31 */
32
33#include <algorithm>
34#include <cmath>
35#include <cstddef>
36#include <string>
37#include <utility>
38#include <vector>
39
49
50namespace line {
51namespace ba {
52
53namespace detail {
54
55/** True for one of the four QRF method names this port serves. */
56inline bool is_qrf_noblo_method(const std::string& m) {
57 return m == "qrf.mmi" || m == "qrf.mem" || m == "qrf.bethe" || m == "qrf.mmi.ld" ||
58 m == "qrf.mmi.linear";
59}
60
61/** True for the two LP blocking bounds, which need `qrf_params`. */
62inline bool is_qrf_lp_method(const std::string& m) { return m == "qrf.bas" || m == "qrf.rsrd"; }
63
64/** True for one of the three nonlinear BAS-blocking bounds. */
65inline bool is_qrf_bas_nlp_method(const std::string& m) {
66 return m == "qrf.bas.mem" || m == "qrf.bas.bethe" || m == "qrf.bas.mmi";
67}
68
69/** mu/v as the per-station K(i) x K(i) blocks the LP bounds take. */
70template <class T>
71void qrf_blocks_from_maps(const std::vector<std::pair<Matrix<T>, Matrix<T> > >& MAPs,
72 const std::vector<int>& K, std::vector<Matrix<T> >* mu,
73 std::vector<Matrix<T> >* v) {
74 const T zero = num_traits<T>::from_int(0);
75 const std::size_t M = MAPs.size();
76 mu->assign(M, Matrix<T>());
77 v->assign(M, Matrix<T>());
78 for (std::size_t i = 0; i < M; ++i) {
79 const std::size_t k = static_cast<std::size_t>(K[i]);
80 (*mu)[i] = Matrix<T>(k, k, zero);
81 (*v)[i] = Matrix<T>(k, k, zero);
82 for (std::size_t h = 0; h < k; ++h)
83 for (std::size_t j = 0; j < k; ++j) {
84 (*mu)[i](h, j) = MAPs[i].second(h, j);
85 // (from, to), as in qrf_extract_mu_v; the diagonal of D0 is the
86 // exit rate, not a background transition.
87 (*v)[i](h, j) = (h == j) ? zero : MAPs[i].first(h, j);
88 }
89 }
90}
91
92/** Build the BAS parameters shared by the LP and entropy objectives. */
93template <class T>
94mapqn::QrBasParams<T> qrf_bas_params(
95 const qn::NetworkStruct<T>& L, const BaOptions& opt,
96 const std::vector<std::pair<Matrix<T>, Matrix<T> > >& MAPs, const std::vector<int>& K,
97 const Matrix<T>& rt, std::size_t N, bool require_capacity) {
98 const std::size_t M = L.nstations;
99 (void)require_capacity;
100 // The blocking tables are DERIVED from sn rather than demanded from the
101 // caller: the model fixes every one of them. The refusal this replaces was
102 // right only while the alternative was to INVENT them -- substituting no
103 // blocking (MR = 1) measured 4.16667 from exact on
104 // sanity_CQN_rm_{fcfs,ps}_1class where real tables sit at 0.133333, i.e.
105 // 31x closer. options.config.qrf_params stays an explicit override.
106 BaOptions::QrfParams derived;
107 if (!opt.qrf_params.supplied) {
108 int Ktot = 0;
109 for (std::size_t i = 0; i < K.size(); ++i) Ktot += std::max(1, K[i]);
110 const sn::QrfBlocking blk = sn::sn_to_qrf_blocking(L, Ktot);
111 if (!blk.msg.empty())
112 throw UnsupportedError("solver_ba_qrf_analyzer: the '" + opt.method +
113 "' method cannot be applied to this model: " + blk.msg +
114 " Supply options.config.qrf_params explicitly to override the "
115 "derivation");
116 derived.supplied = true;
117 derived.f = blk.f;
118 derived.MR = blk.MR;
119 derived.BB = blk.BB;
120 derived.MM = blk.MM;
121 derived.MM1 = blk.MM1;
122 derived.ZZ = blk.ZZ;
123 derived.F = blk.F;
124 }
125 const BaOptions::QrfParams& qp = opt.qrf_params.supplied ? opt.qrf_params : derived;
126
127 std::vector<int> F = qp.F;
128 if (F.empty()) {
129 // F is an OCCUPANCY BOUND, not a declared capacity: sn_to_qrf_capacity
130 // decides binding through sn_get_buffer_size, which folds classcap and
131 // the reachable population in as stations[i].cap alone does not.
132 const sn::QrfCapacity cap = sn::sn_to_qrf_capacity(L);
133 if (!cap.msg.empty())
134 throw UnsupportedError("solver_ba_qrf_analyzer: the '" + opt.method +
135 "' method cannot be applied: " + cap.msg);
136 F = cap.F;
137 }
138 std::vector<Matrix<T> > mu, v;
139 qrf_blocks_from_maps(MAPs, K, &mu, &v);
140
141 mapqn::QrBasParams<T> bp;
142 bp.M = static_cast<int>(M);
143 bp.N = static_cast<int>(N);
144 bp.F = F;
145 bp.K = K;
146 bp.mu = mu;
147 bp.v = v;
148 bp.r = rt;
149 bp.MR = qp.MR;
150 bp.BB = qp.BB;
151 bp.ZZ = qp.ZZ;
152 // qrf_params carries the reference's 1-based queue indices, with 0 for an
153 // absent MM1 entry; the port indexes queues from 0 and marks absence -1.
154 bp.f = qp.f - 1;
155 bp.MM.assign(qp.MM.size(), std::vector<int>());
156 for (std::size_t m = 0; m < qp.MM.size(); ++m)
157 for (std::size_t c = 0; c < qp.MM[m].size(); ++c) bp.MM[m].push_back(qp.MM[m][c] - 1);
158 bp.MM1.assign(qp.MM1.size(), std::vector<int>());
159 for (std::size_t m = 0; m < qp.MM1.size(); ++m)
160 for (std::size_t j = 0; j < qp.MM1[m].size(); ++j) bp.MM1[m].push_back(qp.MM1[m][j] - 1);
161 bp.ZM = 0;
162 for (std::size_t m = 0; m < bp.ZZ.size(); ++m) bp.ZM = std::max(bp.ZM, bp.ZZ[m]);
163 return bp;
164}
165
166/** Run one LP blocking bound and return its utilization vector. */
167template <class T>
168std::vector<T> qrf_lp_utilizations(const qn::NetworkStruct<T>& L, const BaOptions& opt,
169 const std::vector<std::pair<Matrix<T>, Matrix<T> > >& MAPs,
170 const std::vector<int>& K, const Matrix<T>& rt,
171 const Matrix<T>& alpha, std::size_t N) {
172 const std::size_t M = L.nstations;
173 if (opt.method == "qrf.rsrd") {
174 // RS-RD carries NO blocking tables -- QrRsrdParams has no f/MR/BB/MM/
175 // MM1/ZZ member at all -- so it never needed qrf_params, and demanding
176 // them refused a well-formed call. Its PBB constraint sums over every
177 // queue that can be FULL, so it also admits SEVERAL finite buffers
178 // where 'qrf.bas' admits one. What it does need is a truthful F.
179 std::vector<int> F = opt.qrf_params.F;
180 if (F.empty()) {
181 const sn::QrfCapacity cap = sn::sn_to_qrf_capacity(L);
182 if (!cap.msg.empty())
183 throw UnsupportedError("solver_ba_qrf_analyzer: the 'qrf.rsrd' method cannot be "
184 "applied: " + cap.msg);
185 F = cap.F;
186 }
187 std::vector<Matrix<T> > mu, v;
188 qrf_blocks_from_maps(MAPs, K, &mu, &v);
189 mapqn::QrRsrdParams<T> rp;
190 rp.M = static_cast<int>(M);
191 rp.N = static_cast<int>(N);
192 rp.F = F;
193 rp.K = K;
194 rp.mu = mu;
195 rp.v = v;
196 rp.r = rt;
197 if (alpha.rows() == M && alpha.cols() > 0) {
198 rp.alpha.assign(M, std::vector<T>());
199 for (std::size_t i = 0; i < M; ++i)
200 for (std::size_t n = 0; n < alpha.cols(); ++n) rp.alpha[i].push_back(alpha(i, n));
201 }
202 const mapqn::QrRsrdResult<T> res = mapqn::mapqn_qr_bounds_rsrd(rp, 0, mapqn::MapqnSense::Max);
203 if (!res.ok)
204 throw NumericError("solver_ba_qrf_analyzer: the 'qrf.rsrd' linear program did not "
205 "solve (" + res.status + ")");
206 return res.U;
207 }
208
209 const mapqn::QrBasParams<T> bp = qrf_bas_params(L, opt, MAPs, K, rt, N, false);
210 const mapqn::QrBasResult<T> res = mapqn::mapqn_qr_bounds_bas(bp, 0, mapqn::MapqnSense::Max);
211 if (!res.ok)
212 throw NumericError("solver_ba_qrf_analyzer: the 'qrf.bas' linear program did not solve (" +
213 res.status + ")");
214 return res.U;
215}
216
217/**
218 * `derive_qn_from_bounds`: Little's law at the throughput the utilizations imply.
219 *
220 * The M/G/1-like Q = T S / (1 - U) is the reference's estimate; a station at or
221 * above capacity is held at the population instead.
222 */
223template <class T>
224std::vector<T> qrf_qn_from_bounds(const qn::NetworkStruct<T>& L, const std::vector<T>& UN,
225 const std::vector<T>& V, const std::vector<T>& stimes,
226 std::size_t N) {
227 const T zero = num_traits<T>::from_int(0);
228 const T one = num_traits<T>::from_int(1);
229 const std::size_t M = L.nstations;
230 T X = zero;
231 for (std::size_t i = 0; i < M; ++i) {
232 const T c = num_traits<T>::from_double(L.stations[i].nservers);
233 if (stimes[i] > zero && V[i] > zero && UN[i] > zero) {
234 X = T(UN[i] * c / T(V[i] * stimes[i]));
235 break;
236 }
237 }
238 std::vector<T> QN(M, zero);
239 for (std::size_t i = 0; i < M; ++i) {
240 if (!(stimes[i] > zero)) continue;
241 const T Ti = T(X * V[i]);
242 QN[i] = (UN[i] < one) ? T(T(Ti * stimes[i]) / T(one - UN[i]))
243 : num_traits<T>::from_int(static_cast<long>(N));
244 }
245 return QN;
246}
247
248} // namespace detail
249
250/**
251 * Port of `solver_ba_qrf_analyzer`.
252 *
253 * @param L the refreshed struct of a single-class closed model
254 * @param opt the method and, for the load-dependent arms, `qrf_alpha`
255 * @return the [Q,U,R,T,C,X] implied by the QRF utilization bound
256 */
257template <class T>
259 const T zero = num_traits<T>::from_int(0);
260 const std::string& method = opt.method;
261 const std::size_t M = L.nstations;
262
263 if (L.nclasses != 1)
264 throw UnsupportedError("solver_ba_qrf_analyzer: QRF methods support single-class networks "
265 "only");
266 for (double n : L.njobs())
267 if (std::isinf(n))
268 throw UnsupportedError("solver_ba_qrf_analyzer: QRF methods support closed networks "
269 "only");
270 const std::size_t N = static_cast<std::size_t>(L.nclosedjobs());
271 if (N == 0)
272 throw UnsupportedError("solver_ba_qrf_analyzer: QRF methods support closed networks only");
273 // THE LOAD-DEPENDENT ARMS SERVE DELAY, MULTISERVER AND LOAD-DEPENDENT
274 // STATIONS; THE REST STILL CANNOT. alpha(i,n) multiplies every rate out of
275 // station i at population n, which IS the rate law of a delay (alpha = n),
276 // of a c-server station (alpha = min(n,c)) and of limited load dependence,
277 // so `qrf.mmi.ld` and `qrf.mmi.linear` answer the model's OWN chain on all
278 // three rather than an approximation of it. `sn_to_qrf_alpha` derives alpha
279 // and owns the one restriction that survives: a station serving several
280 // jobs at once must be exponential, because the QRF local state carries one
281 // phase per station and that describes one job in service and no more.
282 //
283 // Every other arm builds a population-free q, so it models each station as
284 // one server and a c>1 station solved as c=1 is not a bound in either
285 // direction. They keep refusing, by naming the two arms that do serve the
286 // model. Same gate as the MATLAB, python and JAR twins.
287 const sn::QrfAlpha alpha_sn = sn::sn_to_qrf_alpha(L);
288 const bool method_is_ld = (method == "qrf.mmi.ld" || method == "qrf.mmi.linear");
289 if (alpha_sn.ld && !method_is_ld)
290 throw UnsupportedError(
291 "solver_ba_qrf_analyzer: the '" + method +
292 "' method models every station as a single server: its transition rates carry no "
293 "population index, so it has nowhere to put the rate of a delay, a multiserver or a "
294 "load-dependent station. Use 'qrf.mmi.ld' or 'qrf.mmi.linear', which do");
295 if (!alpha_sn.msg.empty())
296 throw UnsupportedError("solver_ba_qrf_analyzer: the '" + method +
297 "' method cannot be applied: " + alpha_sn.msg);
298
299 // MAPs{i} = {D0, D1}; a station with no service process falls back to the
300 // unit-rate exponential, as the reference does.
301 std::vector<std::pair<Matrix<T>, Matrix<T>>> MAPs(M);
302 std::vector<int> K_phases(M, 1);
303 for (std::size_t i = 0; i < M; ++i) {
304 const mam::Map<T> m = lang::dist_to_map(L.service[i][0]);
305 if (m.D0.rows() == 0) {
307 MAPs[i] = std::make_pair(D0, D1);
308 K_phases[i] = 1;
309 } else {
310 MAPs[i] = std::make_pair(m.D0, m.D1);
311 K_phases[i] = static_cast<int>(m.D0.rows());
312 }
313 }
314
315 // Single class, so the (MK x MK) routing matrix is already station indexed.
316 Matrix<T> rt(M, M, zero);
317 for (std::size_t i = 0; i < M; ++i)
318 for (std::size_t j = 0; j < M; ++j) rt(i, j) = L.rt(i, j);
319
320 Matrix<T> mu, v;
321 mapqn::qrf_extract_mu_v(MAPs, M, K_phases, &mu, &v);
322
323 // options.config.qrf_alpha overrides the derivation, as options.config
324 // .qrf_params does for the blocking tables; absent it, alpha comes from the
325 // model, which is all ones on a load-independent one.
326 Matrix<T> alpha(M, N, zero);
327 if (opt.qrf_alpha.rows() > 0) {
328 if (opt.qrf_alpha.rows() != M || opt.qrf_alpha.cols() != N)
329 throw InputError("solver_ba_qrf_analyzer: qrf_alpha must be (nstations x N)");
330 for (std::size_t i = 0; i < M; ++i)
331 for (std::size_t n = 0; n < N; ++n)
332 alpha(i, n) = num_traits<T>::from_double(opt.qrf_alpha(i, n));
333 } else {
334 for (std::size_t i = 0; i < M; ++i)
335 for (std::size_t n = 0; n < N; ++n)
336 alpha(i, n) = num_traits<T>::from_double(alpha_sn.alpha[i][n]);
337 }
338
340 if (detail::is_qrf_lp_method(method)) {
341 r.UN = detail::qrf_lp_utilizations(L, opt, MAPs, K_phases, rt, alpha, N);
342 } else if constexpr (!num_traits<T>::has_transcendental) {
343 throw UnsupportedError(
344 "solver_ba_qrf_analyzer: the QRF objective is the MEM entropy, the mutual "
345 "information or the tree-reweighted free entropy, all of which take a log, and "
346 "exact arithmetic has no representation for it; use the double or real backend");
347 } else {
348 if (method == "qrf.mmi")
349 r = mapqn::qrf_noblo_mmi(M, K_phases, N, mu, v, rt);
350 else if (method == "qrf.mem")
351 r = mapqn::qrf_noblo_mem(MAPs, N, rt);
352 else if (method == "qrf.bethe")
353 // Same polytope and same phase-1 start as qrf.mmi; the objective is
354 // the tree-reweighted (Bethe) free entropy at the uniform
355 // spanning-tree weight lambda = 1/M, the largest uniform weight at
356 // which the program is convex.
357 r = mapqn::qrf_noblo_bethe(M, K_phases, N, mu, v, rt);
358 else if (method == "qrf.mmi.ld")
359 r = mapqn::qrf_noblo_mmi_ld(M, K_phases, N, mu, v, rt, alpha);
360 else if (method == "qrf.mmi.linear")
361 r = mapqn::qrf_noblo_mmi_linear(MAPs, N, rt, alpha);
362 else if (method == "qrf.bas.mmi")
364 detail::qrf_bas_params(L, opt, MAPs, K_phases, rt, N, true));
365 else if (method == "qrf.bas.bethe")
366 // qrf.bethe's objective over the BAS decision vector: same tables
367 // and same polytope as qrf.bas and qrf.bas.mem, lambda = 1/M.
369 detail::qrf_bas_params(L, opt, MAPs, K_phases, rt, N, true));
370 else if (detail::is_qrf_bas_nlp_method(method))
372 detail::qrf_bas_params(L, opt, MAPs, K_phases, rt, N, true));
373 else
374 throw UnsupportedError("solver_ba_qrf_analyzer: unknown QRF method '" + method + "'");
375 }
376
377 // cellsum(sn.visits): the visits summed over the chains, at station level
378 std::vector<T> V(M, zero);
379 for (std::size_t c = 0; c < L.nchains; ++c)
380 for (std::size_t i = 0; i < M; ++i)
381 V[i] += L.visits[c](L.stateful_of_station(i + 1) - 1, 0);
382
383 std::vector<T> stimes(M, zero);
384 for (std::size_t i = 0; i < M; ++i) {
385 mam::Map<T> m;
386 m.D0 = MAPs[i].first;
387 m.D1 = MAPs[i].second;
388 stimes[i] = mam::map_mean(m);
389 }
390
391 // `derive_qn_from_bounds`: an LP bound returns utilizations only, so the
392 // queue lengths come from Little's law at the throughput those imply.
393 if (r.QN.empty()) r.QN = detail::qrf_qn_from_bounds(L, r.UN, V, stimes, N);
394
395 // Normalize the queue lengths to the population constraint.
396 T qsum = zero;
397 for (const T& q : r.QN) qsum += q;
398 std::vector<T> QN = r.QN;
399 if (qsum > zero)
400 for (T& q : QN) q = T(q / qsum * num_traits<T>::from_int(static_cast<long>(N)));
401
402 // The alpha-free arms and the LP ones leave BN empty, and there
403 // BN = P(n >= 1) = UN: a single server's departure rate is proportional to
404 // the probability that it is busy. Setting it here rather than in each arm
405 // keeps the readout below one formula.
406 if (r.BN.empty()) r.BN = r.UN;
407
408 // X from the ALPHA-WEIGHTED marginal mean BN, the mean number of jobs
409 // actually in service: E[min(n,c)] at a c-server station, E[n] at a delay,
410 // P(n >= 1) at a single server. That is what the departure rate is
411 // proportional to, so T_i = BN_i / stime_i holds exactly at the relaxed
412 // point and X = T_i / V_i. The single-server case is the former
413 // UN[i]*c/(V*stime) unchanged, c being 1 and BN being UN there; a delay no
414 // longer needs the refstat fallback, since alpha = n makes BN = E[n] = QN,
415 // which is what that fallback computed.
416 T X = zero;
417 for (std::size_t i = 0; i < M; ++i) {
418 if (stimes[i] > zero && V[i] > zero && r.BN[i] > zero) {
419 X = T(r.BN[i] / T(V[i] * stimes[i]));
420 break;
421 }
422 }
423 if (!(X > zero)) {
424 const std::size_t rs = L.classes[0].refstat;
425 if (rs >= 1 && rs <= M && stimes[rs - 1] > zero && V[rs - 1] > zero)
426 X = T(QN[rs - 1] / T(V[rs - 1] * stimes[rs - 1]));
427 }
428
430 s.Q = Matrix<T>(M, 1, zero);
431 s.U = Matrix<T>(M, 1, zero);
432 s.R = Matrix<T>(M, 1, zero);
433 s.Tp = Matrix<T>(M, 1, zero);
434 for (std::size_t i = 0; i < M; ++i) {
435 s.Q(i, 0) = QN[i];
436 s.Tp(i, 0) = T(X * V[i]);
437 if (std::isinf(alpha_sn.peak[i]))
438 // Delay (infinite server): U = QN by LINE convention.
439 s.U(i, 0) = QN[i];
440 else
441 // The busy fraction of the station's DECLARED peak capacity, BN
442 // being the mean number of jobs in service. The normalizer is
443 // nservers times the reachable lld peak, LINE's one U = T*S/peak
444 // convention (see sn_to_qrf_alpha); at peak 1 it is r.UN[i], what
445 // the alpha-free arms report directly.
446 s.U(i, 0) = T(r.BN[i] / num_traits<T>::from_double(alpha_sn.peak[i]));
447 if (s.Tp(i, 0) > zero) s.R(i, 0) = T(s.Q(i, 0) / s.Tp(i, 0));
448 }
449 s.X.assign(1, X);
450 s.C.assign(1, X > zero ? T(num_traits<T>::from_int(static_cast<long>(N)) / X) : zero);
451 s.lG = std::numeric_limits<double>::quiet_NaN();
452 return s;
453}
454
455} // namespace ba
456} // namespace line
457
458#endif // LINE_SOLVERS_BA_SOLVER_BA_QRF_ANALYZER_H
InputError(const std::string &what)
Definition error.h:39
bool empty() const
Definition matrix.h:92
NumericError(const std::string &what)
Definition error.h:45
UnsupportedError(const std::string &what)
Definition error.h:51
A network plus its refreshed NetworkStruct.
std::size_t stateful_of_station(std::size_t st) const
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
Matrix< T > rt
sn.rt and sn.rtnodes: the class-expanded routing.
std::vector< JobClass > classes
std::vector< double > njobs() const
sn.njobs: the population of each class, infinite for an open one.
std::vector< Matrix< T > > visits
(nchains) each (nstateful x nclasses)
double nclosedjobs() const
sn.nclosedjobs: the total population of the closed classes.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Quadratic-reduction bound on the utilization of one queue of a closed MAP queueing network with a FIN...
Quadratic-reduction bound on the utilization of one queue of a closed MAP queueing network under RS-R...
qrf_bas_mmi, qrf_bas_mem and qrf_bas_bethe: the nonlinear bounds on the BAS-BLOCKING polytope.
The QRF no-blocking nonlinear bounds: qrf_noblo_mmi, qrf_noblo_mem and the load-dependent qrf_noblo_m...
BaSolution< T > solver_ba_qrf_analyzer(const qn::NetworkStruct< T > &L, const BaOptions &opt)
Port of solver_ba_qrf_analyzer.
mam::Map< T > dist_to_map(const Distrib< T > &d)
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
Definition map_moment.h:101
QrfMetrics< T > mapqn_qrf_bas_mmi(const QrBasParams< T > &p, unsigned max_iter=200)
Minimum-mutual-information bound on the BAS-blocking polytope.
QrfMetrics< T > qrf_noblo_mem(const std::vector< std::pair< Matrix< T >, Matrix< T > > > &MAPs, std::size_t N, const Matrix< T > &rt)
qrf_noblo_mem: the same polytope under maximum entropy.
QrfMetrics< T > qrf_noblo_mmi_ld(std::size_t M, const std::vector< int > &K, std::size_t N, const Matrix< T > &mu, const Matrix< T > &v, const Matrix< T > &rt, const Matrix< T > &alpha)
qrf_noblo_mmi_ld: MMI on the LOAD-DEPENDENT polytope.
QrfMetrics< T > mapqn_qrf_bas_bethe(const QrBasParams< T > &p, unsigned max_iter=200)
Tree-reweighted (Bethe) free entropy bound on the BAS-blocking polytope.
QrfMetrics< T > qrf_noblo_mmi(std::size_t M, const std::vector< int > &K, std::size_t N, const Matrix< T > &mu, const Matrix< T > &v, const Matrix< T > &rt)
qrf_noblo_mmi: the no-blocking bound under mutual-information minimization.
QrfMetrics< T > mapqn_qrf_bas_mem(const QrBasParams< T > &p, unsigned max_iter=200)
Maximum-entropy bound on the BAS-blocking polytope.
QrfMetrics< T > qrf_noblo_mmi_linear(const std::vector< std::pair< Matrix< T >, Matrix< T > > > &MAPs, std::size_t N, const Matrix< T > &rt, const Matrix< T > &alpha)
qrf_noblo_mmi_linear: the load-dependent no-blocking bound, under MMI.
QrRsrdResult< T > mapqn_qr_bounds_rsrd(const QrRsrdParams< T > &p, int objective_queue, MapqnSense sense=MapqnSense::Min)
Bound the utilization of one queue over the RS-RD polytope.
QrfMetrics< T > qrf_noblo_bethe(std::size_t M, const std::vector< int > &K, std::size_t N, const Matrix< T > &mu, const Matrix< T > &v, const Matrix< T > &rt)
qrf_noblo_bethe: the same polytope under the tree-reweighted free entropy.
QrBasResult< T > mapqn_qr_bounds_bas(const QrBasParams< T > &p, int objective_queue, MapqnSense sense=MapqnSense::Min)
Bound the utilization of one queue over the BAS polytope.
void qrf_extract_mu_v(const std::vector< std::pair< Matrix< T >, Matrix< T > > > &MAPs, std::size_t M, const std::vector< int > &K, Matrix< T > *mu, Matrix< T > *v)
extract_mu_v_from_maps: the completion and background rates of each MAP.
QrfBlocking sn_to_qrf_blocking(const qn::NetworkStruct< T > &sn, int Ktot, double max_vars=kQrfDefaultMaxVars)
The QRF BAS blocking tables (f, MR, BB, MM, ZZ, MM1), derived from an sn.
QrfCapacity sn_to_qrf_capacity(const qn::NetworkStruct< T > &sn)
F is an OCCUPANCY BOUND, not a declared capacity: the station's buffer where that buffer BINDS,...
QrfAlpha sn_to_qrf_alpha(const qn::NetworkStruct< T > &L)
ld stays TRUE through a refusal: the model IS load dependent, and the caller has to tell "no arm serv...
A queueing network and its refreshed NetworkStruct.
The QRF load-dependent rate scaling alpha(i,n), derived from an sn.
The QRF BAS blocking tables (f, MR, BB, MM, ZZ, MM1), derived from an sn.
Port of matlab/src/solvers/BA/solver_ba_analyzer.m, the bound-analysis handler behind SolverBA.
The options SolverBA reads.
Class-level results, the [Q,U,R,T,C,X] of solver_ba_analyzer.
double lG
-N log X, the reference's approximate normalizing constant.
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
Matrix< T > D1
Definition map_moment.h:55
Matrix< T > D0
Definition map_moment.h:54
The utilizations and queue lengths read off an optimal pair tensor.
std::vector< T > BN
The ALPHA-WEIGHTED diagonal marginal mean: the mean number of jobs actually in service,...
The scaling, the utilization normalizer, and why they may not exist.
bool ld
alpha is not identically 1
std::string msg
empty on success
std::vector< double > peak
(M) utilization normalizer; inf at a delay
std::vector< std::vector< double > > alpha
(M x N) scaling at population n = 1..N