5#ifndef LINE_SOLVERS_MAM_SOLVER_MAM_BMAP_H
6#define LINE_SOLVERS_MAM_SOLVER_MAM_BMAP_H
103namespace bmap_detail {
105using qbd_detail::madd;
109Matrix<T> batch_d1(
const std::vector<Matrix<T>>& D) {
110 Matrix<T> tot(D[0].rows(), D[0].cols(), num_traits<T>::from_int(0));
111 for (std::size_t k = 1; k < D.size(); ++k) tot = madd(tot, D[k]);
117Matrix<T> batch_infgen(
const std::vector<Matrix<T>>& D) {
118 return madd(D[0], batch_d1(D));
130T batch_customer_rate(
const std::vector<Matrix<T>>& D) {
131 const T zero = num_traits<T>::from_int(0);
134 phase.D1 = batch_d1(D);
135 const std::vector<T> theta =
map_prob(phase);
137 for (std::size_t k = 1; k < D.size(); ++k) {
138 const std::vector<T> tD =
vecmul(theta, D[k]);
140 for (
const T& v : tD) s += v;
141 rate += T(num_traits<T>::from_int(
static_cast<long>(k)) * s);
148std::size_t check_batch_shape(
const std::vector<Matrix<T>>& D,
const std::string& who,
149 const std::string& what) {
152 " must be given as {D0, D1, ..., DK} with at least D0 and D1");
153 const std::size_t n = D[0].rows();
154 for (std::size_t k = 0; k < D.size(); ++k)
155 if (D[k].rows() != n || D[k].cols() != n)
156 throw InputError(who +
": all " + what +
" matrices must be " + std::to_string(n) +
157 "x" + std::to_string(n) +
", but D{" + std::to_string(k) +
"} is " +
158 std::to_string(D[k].rows()) +
"x" + std::to_string(D[k].cols()));
164void check_zero_rowsums(
const Matrix<T>& Q,
const std::string& msg) {
165 for (std::size_t i = 0; i < Q.rows(); ++i) {
166 T s = num_traits<T>::from_int(0);
167 for (std::size_t j = 0; j < Q.cols(); ++j) s += Q(i, j);
168 if (std::fabs(num_traits<T>::to_double(s)) > 1e-10)
throw InputError(msg);
207 throw InputError(
"mam_detect_mmck: station index " + std::to_string(ist) +
209 const std::size_t i0 = ist - 1;
210 if (arv.
order() != 1)
return out;
213 double lo = 0.0, hi = 0.0;
214 for (std::size_t r = 0; r < L.
nclasses; ++r) {
218 if (!(v > 0.0))
continue;
224 lo = std::min(lo, v);
225 hi = std::max(hi, v);
228 if (!any)
return out;
229 if (hi - lo > 1e-9 * std::max(1.0, hi))
return out;
260 "mam_truncate_renorm: the infinite-buffer marginal comes from MMAP[K]/PH[K]/1 FCFS, "
261 "whose ADDA doubling iteration terminates on a tolerance; rerun this model with "
262 "--arith double or --arith real");
264 if (capK < 1)
throw InputError(
"mam_truncate_renorm: the buffer capacity must be positive");
265 return basic_detail::truncate_renorm(arv, svc, capK);
288 std::vector<Matrix<T>>
Aup;
291 std::vector<Matrix<T>>
Bup;
316 using namespace bmap_detail;
318 b.
ma = check_batch_shape(D,
"solver_mam_bmap_map_1",
"BMAP");
320 b.
ms = service.
D0.rows();
321 if (service.
D0.cols() != b.
ms || service.
D1.rows() != b.
ms || service.
D1.cols() != b.
ms)
322 throw InputError(
"solver_mam_bmap_map_1: the service MAP matrices must be " +
323 std::to_string(b.
ms) +
"x" + std::to_string(b.
ms));
329 for (std::size_t k = 1; k <= b.
K; ++k) b.
Aup.push_back(
kron(D[k], Is));
331 b.
B0 = madd(
kron(D[0], Is),
kron(Ia, madd(service.
D0, service.
D1)));
334 b.
lambda = batch_customer_rate(D);
388 using namespace bmap_detail;
390 b.
ma = arrival.
D0.rows();
391 if (arrival.
D0.cols() != b.
ma || arrival.
D1.rows() != b.
ma || arrival.
D1.cols() != b.
ma)
392 throw InputError(
"solver_mam_map_bmap_1: the arrival MAP matrices must be " +
393 std::to_string(b.
ma) +
"x" + std::to_string(b.
ma));
394 b.
ms = check_batch_shape(D,
"solver_mam_map_bmap_1",
"service BMAP");
398 check_zero_rowsums(madd(arrival.
D0, arrival.
D1),
399 "solver_mam_map_bmap_1: MAP matrices C0 + C1 must have zero row sums");
401 check_zero_rowsums(Dtot,
402 "solver_mam_map_bmap_1: BMAP matrices D0 + D1 + ... + DK must have zero "
408 for (std::size_t k = 1; k <= b.
K; ++k) b.
Adown.push_back(
kron(Ia, D[k]));
411 for (std::size_t k = 1; k <= b.
K; ++k) b.
B1 = madd(b.
B1,
kron(Ia, D[k]));
412 for (std::size_t j = 1; j <= b.
K; ++j) {
414 for (std::size_t k = j; k <= b.
K; ++k) Bj = madd(Bj,
kron(Ia, D[k]));
415 b.
Bto0.push_back(Bj);
419 b.
mu = batch_customer_rate(D);
442namespace bmap_detail {
446void require_double_arith(
const std::string& who) {
447 if (!std::is_same<T, double>::value)
450 ": the ETAQA mean measures run in double precision only. The solve bisects on a "
451 "Perron-Frobenius eigenvalue (LAPACK), evaluates the cyclic reduction at complex "
452 "roots of unity (FFT) and picks the redundant balance equation by a numerical rank "
453 "test (SVD), none of which this tree provides at exact or multiprecision arithmetic. "
454 "The level blocks, the rates and the stability test ARE available at every "
456 who +
"_blocks; rerun the measures with --arith double");
463 for (std::size_t i = 0; i < A.
rows(); ++i)
469inline Matrix<double> hstack(
const std::vector<Matrix<double>>& blk) {
474inline Matrix<double> vstack(
const std::vector<Matrix<double>>& blk) {
493 std::size_t nMoments = 3) {
495 bmap_detail::require_double_arith<T>(
"solver_mam_bmap_map_1");
496 if (nMoments < 1)
throw InputError(
"solver_mam_bmap_map_1: nMoments must be positive");
498 std::vector<Matrix<double>> Ablk, Bblk;
499 Ablk.push_back(bmap_detail::as_double(b.
A0));
500 Ablk.push_back(bmap_detail::as_double(b.
A1));
501 for (std::size_t k = 0; k < b.
Aup.size(); ++k) Ablk.push_back(bmap_detail::as_double(b.
Aup[k]));
502 Bblk.push_back(bmap_detail::as_double(b.
B0));
503 for (std::size_t k = 0; k < b.
Bup.size(); ++k) Bblk.push_back(bmap_detail::as_double(b.
Bup[k]));
513 for (std::size_t n = 1; n <= nMoments; ++n)
520 out.
RN = T(out.
QN / out.
TN);
524 for (std::size_t i = 0; i < G.
rows(); ++i)
525 for (std::size_t j = 0; j < G.
cols(); ++j)
543 bmap_detail::require_double_arith<T>(
"solver_mam_map_bmap_1");
545 std::vector<Matrix<double>> Ablk, Bblk;
546 Ablk.push_back(bmap_detail::as_double(b.
A0));
547 Ablk.push_back(bmap_detail::as_double(b.
A1));
548 for (std::size_t k = 0; k < b.
Adown.size(); ++k)
549 Ablk.push_back(bmap_detail::as_double(b.
Adown[k]));
550 Bblk.push_back(bmap_detail::as_double(b.
B1));
551 for (std::size_t k = 0; k < b.
Bto0.size(); ++k)
552 Bblk.push_back(bmap_detail::as_double(b.
Bto0[k]));
568 out.
RN = T(out.
QN / out.
TN);
572 for (std::size_t i = 0; i < R.
rows(); ++i)
573 for (std::size_t j = 0; j < R.
cols(); ++j)
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::vector< std::vector< Distrib< T > > > service
service[i][r], 0-based station and class; a disabled entry marks a pair never visited.
std::vector< std::vector< bool > > disabled
Matrix< T > rates
(nstations x nclasses) service rates and SCVs, with a PARALLEL disabled flag instead of MATLAB's NaN ...
The exception types the port throws.
ETAQA: the aggregated stationary vector and the queue-length moments of an M/G/1-type and of a GI/M/1...
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
The MMAP[K]/PH[K]/1 FCFS queue: per-class mean number in system and per-class queue-length distributi...
MmckDetection< T > mam_detect_mmck(const qn::NetworkStruct< T > &L, std::size_t ist, const Mmap< T > &arv)
Port of mam_detect_mmck.m: is the exact M/M/c/K closed form legitimate at this station?
BmapMap1Blocks< T > solver_mam_bmap_map_1_blocks(const std::vector< Matrix< T > > &D, const Map< T > &service)
Port of the block assembly and the stability test of solver_mam_bmap_map_1.m.
basic_detail::TruncRenorm< T > mam_truncate_renorm(const Mmap< T > &arv, const std::vector< PhService< T > > &svc, std::size_t capK)
Port of mam_truncate_renorm.m: the finite-buffer marginal of an MMAP[K]/PH[K]/1 FCFS queue,...
Matrix< T > kron(const Matrix< T > &A, const Matrix< T > &B)
Kronecker product.
std::vector< T > map_prob(const Map< T > &m)
Stationary distribution of the phase process, pi (D0 + D1) = 0.
BmapQueueResult< T > solver_mam_bmap_map_1(const std::vector< Matrix< T > > &D, const Map< T > &service, std::size_t nMoments=3)
Port of solver_mam_bmap_map_1.m, mean measures included.
MapBmap1Blocks< T > solver_mam_map_bmap_1_blocks(const Map< T > &arrival, const std::vector< Matrix< T > > &D)
Port of the block assembly, the generator validation and the stability test of solver_mam_map_bmap_1....
BmapQueueResult< T > solver_mam_map_bmap_1(const Map< T > &arrival, const std::vector< Matrix< T > > &D)
Port of solver_mam_map_bmap_1.m, mean measures included.
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Matrix< double > hcat(const Blocks &blk)
Re-assembles a block sequence into the wide [A0 A1 ... Amax].
double mg1_qlen_etaqa(const Matrix< double > &Bin, const Matrix< double > &Ain, const std::vector< double > &pi, std::size_t n, const Matrix< double > &C0in=Matrix< double >())
n-th moment of the level (the queue length) of an M/G/1-type chain from the ETAQA aggregates.
Matrix< double > mg1_g_etaqa(const Matrix< double > &A)
G of an M/G/1-type chain, uniformized first.
double gim1_qlen_etaqa(const Matrix< double > &Bin, const Matrix< double > &Ain, const Matrix< double > &R, const std::vector< double > &pi, std::size_t n, const Matrix< double > &B0in=Matrix< double >())
n-th moment of the level of a GI/M/1-type chain from the ETAQA aggregates.
Matrix< double > gim1_r_etaqa(const Matrix< double > &A)
R of a GI/M/1-type chain, uniformized first.
std::vector< double > mg1_pi_etaqa(const Matrix< double > &Bin, const Matrix< double > &Ain, const Matrix< double > &G, const Matrix< double > &C0in=Matrix< double >())
Aggregated stationary vector [pi0, pi1, pi2+pi3+...] of an M/G/1-type chain.
std::vector< double > gim1_pi_etaqa(const Matrix< double > &Bin, const Matrix< double > &Ain, const Matrix< double > &R, const Matrix< double > &B0in=Matrix< double >())
Aggregated stationary vector [pi0, pi1, pi2+pi3+...] of a GI/M/1-type chain.
Matrix< double > vcat(const Blocks &blk)
Stacks a block sequence vertically, [A0; A1; ...; Amax].
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Matrix< T > eye(std::size_t n)
Identity of order n.
A queueing network and its refreshed NetworkStruct.
Quasi-birth-death processes: the rate matrix R, the fundamental matrix G, the caudal characteristic,...
Port of solver_mam_basic.m, the dec.source analyzer and the default algorithm of SolverMAM.
The level blocks of solver_mam_bmap_map_1.m.
std::vector< Matrix< T > > Bup
Bup[k-1]: level 0 -> +k.
T mu
service completions per unit time
std::size_t ms
service MAP phases
std::vector< Matrix< T > > Aup
Aup[k-1]: level +k, a batch of k.
std::size_t ma
BMAP phases.
T lambda
customers per unit time
std::size_t K
largest batch size
Matrix< T > B0
level 0 local block, at the empty queue
bool stable
The reference WARNS rather than errors when rho >= 1; recorded, not thrown.
Matrix< T > A1
level 0: phase changes only
Matrix< T > A0
level -1: a service completion
What both reference files return once the ETAQA solve has run.
Matrix< T > fund
G for the M/G/1-type solve, R for the GI/M/1-type one.
Matrix< T > piAgg
the ETAQA-aggregated stationary vector
std::vector< T > qlenMoments
Moments 1..nMoments of the queue length; QN is the first of them.
The level blocks of solver_mam_map_bmap_1.m.
std::size_t ms
service BMAP phases
Matrix< T > A0
level +1: an arrival
std::vector< Matrix< T > > Adown
Adown[k-1]: level -k, a batch service of k.
std::vector< Matrix< T > > Bto0
Bto0[j-1]: level j -> level 0, j = 1..K.
std::size_t ma
arrival MAP phases
Matrix< T > B1
level 0 local block, service folded back
std::size_t K
largest service batch
T mu
customers served per unit time
Matrix< T > A1
level 0: phase changes only
A MAP as the pair of matrices (D0, D1).
An MMAP: the underlying MAP plus the per-class arrival matrices.
std::size_t order() const
What mam_detect_mmck returns; muRate is meaningful only when isMmck.
One class's phase-type service law, He's (sigma_k, S_k).