5#ifndef LINE_API_FJ_FJ_CODES_H
6#define LINE_API_FJ_FJ_CODES_H
108 std::string lower(s);
109 for (
char& c : lower) c =
static_cast<char>(std::tolower(
static_cast<unsigned char>(c)));
149inline std::vector<double> rdivide_row(
const std::vector<double>& a,
const Matrix<double>& B) {
157 for (std::size_t i = 0; i < A.
rows(); ++i)
158 for (std::size_t j = 0; j < A.
cols(); ++j) {
159 const double x = std::fabs(A(i, j) - B(i, j));
166inline double inf_norm(
const Matrix<double>& A) {
168 for (std::size_t i = 0; i < A.rows(); ++i) {
170 for (std::size_t j = 0; j < A.cols(); ++j) s += std::fabs(A(i, j));
171 if (s > best) best = s;
176inline Matrix<double> madd(
const Matrix<double>& A,
const Matrix<double>& B) {
177 Matrix<double> C(A.rows(), A.cols());
178 for (std::size_t i = 0; i < A.rows(); ++i)
179 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) = A(i, j) + B(i, j);
183inline Matrix<double> msub(
const Matrix<double>& A,
const Matrix<double>& B) {
184 Matrix<double> C(A.rows(), A.cols());
185 for (std::size_t i = 0; i < A.rows(); ++i)
186 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) = A(i, j) - B(i, j);
191inline Matrix<double> block(
const Matrix<double>& A, std::size_t r0, std::size_t r1,
192 std::size_t c0, std::size_t c1) {
193 Matrix<double> B(r1 - r0, c1 - c0, 0.0);
194 for (std::size_t i = r0; i < r1; ++i)
195 for (std::size_t j = c0; j < c1; ++j) B(i - r0, j - c0) = A(i, j);
219 const std::size_t m = S.
rows();
220 const std::size_t ma = D0.
rows();
221 if (ma == 0 || m % ma != 0)
222 throw InputError(
"fj_compute_t_nare: the phase space is not a multiple of the arrival "
224 const std::size_t ms = m / ma;
232 for (std::size_t i = 0; i < m; ++i)
233 for (std::size_t j = 0; j < m; ++j) {
234 H(i, j) = ImsD0(i, j);
235 H(i, m + j) = ImsD1(i, j);
236 H(m + i, j) = -AjIma(i, j);
237 H(m + i, m + j) = -S(i, j);
243 const std::size_t n = 2 * m;
244 std::vector<double> re(n, 0.0);
245 std::vector<int> blk(n, 1);
246 for (std::size_t i = 0; i < n;) {
247 const bool pair = (i + 1 < n) && sc.
T(i + 1, i) != 0.0;
249 const double r = 0.5 * (sc.
T(i, i) + sc.
T(i + 1, i + 1));
261 std::vector<std::size_t> order(n);
262 for (std::size_t i = 0; i < n; ++i) order[i] = i;
263 std::stable_sort(order.begin(), order.end(),
264 [&re](std::size_t a, std::size_t b) { return re[a] < re[b]; });
265 std::vector<double> key(n, 0.0);
266 for (std::size_t i = 0; i < m; ++i) key[order[i]] = 1.0;
267 for (std::size_t i = 0; i + 1 < n; ++i)
268 if (blk[i] == 2 && key[i] != key[i + 1])
270 "fj_compute_t_nare: the m eigenvalues of smallest real part split a complex "
271 "conjugate pair, so the stable invariant subspace of the Riccati pencil is not "
272 "real and the T matrix of this model is not defined");
276 const Matrix<double> Q21 = fjdetail::block(ord.Z, m, 2 * m, 0, m);
279 if (residual !=
nullptr)
280 *residual = fjdetail::inf_norm(
281 fjdetail::madd(fjdetail::madd(
matmul(T, X),
matmul(X, ImsD0)), AjIma));
298 const std::size_t d0 = arrival.
lambda0.rows();
308 const std::size_t ms = sa.
S.
rows();
309 const std::size_t m = ms * d0;
319 const std::size_t max_iter = 500;
321 while (fjdetail::max_abs_diff(Told, Tnew) > 1e-10) {
324 "fj_compute_t: the Sylvester iteration of computeT.m did not reach 1e-10 in " +
325 std::to_string(max_iter) +
326 " steps; solve this model with the NARE route (config.fj_tmode = 'NARE')");
335 fjdetail::madd(fjdetail::madd(
matmul(out.
T, L),
matmul(L, ID0)), Im));
342 for (std::size_t i = 0; i < A_jump_Arr.
rows(); ++i) {
344 for (std::size_t j = 0; j < A_jump_Arr.
cols(); ++j) s += A_jump_Arr(i, j);
362 const std::size_t nd = pi0mat.
rows();
363 if (pi0mat.
cols() != nd || T.
rows() != nd || T.
cols() != nd)
364 throw InputError(
"fj_boundary_solve: the boundary blocks are not conformable");
367 for (std::size_t i = 0; i < nd; ++i) {
368 for (std::size_t j = 0; j < nd; ++j) M(i, j) = pi0mat(i, j) - (i == j ? 1.0 : 0.0);
370 for (std::size_t j = 0; j < nd; ++j) s += iT(i, j);
373 std::vector<double> b(nd + 1, 0.0);
395 const std::size_t
da = arrival.
lambda0.rows();
398 if (service.
choice == 1) {
399 const std::size_t ms = S.
cols();
404 "fj_compute_pi: the not-all-busy space and the all-busy space have different "
405 "dimensions, which the exponential branch of computePi.m assumes they do not");
413 for (
double v : out.
pi0) sp += v;
414 const std::vector<double> pm =
vecmul(out.
pi0, pi0mat);
416 for (
double v : pm) spm += v;
422 const std::size_t dtmat = T.
rows() / arrival.
lambda0.cols();
423 const std::size_t dsexp = srk.
Se.
rows();
424 const std::size_t dnb = dsexp - dtmat;
426 const Matrix<double> Sedash = fjdetail::block(srk.
Se, dtmat, dsexp, dtmat, dsexp);
427 const Matrix<double> Rbusy = fjdetail::block(srk.
R0, dtmat, dsexp, 0, dsexp);
429 for (std::size_t i = 0; i < dnb; ++i) Iidle_small(dtmat + i, i) = 1.0;
438 for (std::size_t i = 0; i < M.
rows(); ++i)
439 for (std::size_t j = 0; j < M.
cols(); ++j) N(i, j) = -M(i, j);
451 for (
double v : out.
pi0) sp += v;
454 std::vector<double> row =
vecmul(out.
pi0, Igral);
455 for (
double& v : row) v = -v;
457 row = fjdetail::rdivide_row(row, Qidle);
460 for (
double v : row) sr += v;
468 const std::vector<double>& sum_Ajump) {
469 const std::size_t ds = phi.size();
471 out.
alfa = fjdetail::rdivide_row(pi0, T);
472 for (
double& v : out.
alfa) v = -v;
475 for (std::size_t i = 0; i < ds; ++i) ap += out.
alfa[i] * phi[i];
476 std::vector<double> rhos(ds, 0.0);
477 for (std::size_t i = 0; i < ds; ++i) rhos[i] = phi[i] * out.
alfa[i] / ap;
480 for (
double v : pi0) sp += v;
482 for (std::size_t i = 0; i < ds; ++i) asum += out.
alfa[i] * sum_Ajump[i];
483 const double En0 = asum / sp;
485 out.
prob_wait = (En0 - 1.0) / (En0 - 1.0 + En1);
490 for (std::size_t i = 0; i < ds; ++i)
491 for (std::size_t j = 0; j < ds; ++j)
510 const std::vector<double>& pers) {
512 const std::size_t m = A.
cols();
513 if (vec.size() != m || A.
rows() != m)
514 throw InputError(
"fj_return_per: the phase-type pair is not conformable");
516 const std::vector<double> negvA = [&] {
518 for (
double& x : v) x = -x;
522 for (
double v : negvA) meanRT += v;
524 throw NumericError(
ErrorKind{
"Degenerate"},
"fj_return_per: the phase-type law has a non-positive mean, so its "
525 "percentiles are not defined");
528 for (std::size_t i = 0; i < m; ++i) c = std::max(c, -A(i, i));
530 throw NumericError(
ErrorKind{
"Degenerate"},
"fj_return_per: the phase-type generator has no negative diagonal");
532 for (std::size_t i = 0; i < m; ++i)
533 for (std::size_t j = 0; j < m; ++j) P(i, j) = A(i, j) / c + (i == j ? 1.0 : 0.0);
536 for (std::size_t i = 0; i < m; ++i)
537 for (std::size_t j = 0; j < m; ++j) ImP(i, j) = (i == j ? 1.0 : 0.0) - P(i, j);
540 for (
double v : vImP) M += v;
543 for (
double v : vec) a0 += v;
545 std::vector<double> ak;
546 std::vector<double> vP(m, 0.0);
547 for (std::size_t i = 0; i < m; ++i) {
549 for (std::size_t j = 0; j < m; ++j) s += P(i, j);
552 while (std::fabs(sum_a - M) >= 1e-10) {
554 for (std::size_t i = 0; i < m; ++i) t += vec[i] * vP[i];
558 if (ak.size() > 2000000)
559 throw NumericError(
ErrorKind{
"NonConvergence"},
"fj_return_per: the uniformized Poisson series did not reach the "
560 "total absorption mass, so the percentile scan cannot terminate");
562 const std::size_t K1 = ak.size();
565 const auto cdf_at = [&](
double t) {
566 double pM = exp(-c * t);
568 for (std::size_t k = 1; k <= K1; ++k) {
569 pM = c * t * pM /
static_cast<double>(k);
575 std::vector<double> out(pers.size(), 0.0);
576 for (std::size_t p = 0; p < pers.size(); ++p) {
577 if (pers[p] < 1.0 - a0)
continue;
578 double MaxTime = 3.0 * meanRT;
580 if (cdf_at(MaxTime) < pers[p]) {
581 MaxTime += 0.5 * meanRT;
587 const std::size_t steps =
static_cast<std::size_t
>(MaxTime / 0.001);
588 for (std::size_t k = 0; k <= steps; ++k) {
589 const double t = MaxTime -
static_cast<double>(k) * 0.001;
590 if (cdf_at(t) < pers[p]) {
598 "fj_return_per: the downward scan reached t = 0 without the response-time CDF "
599 "falling below " + std::to_string(pers[p]) +
600 ", so this percentile has no bracket; the reference silently reports the "
601 "previous percentile here");
614 const std::vector<double>& pers) {
620 std::vector<mam::PhService<double>> svc(1);
621 svc[0].sigma = service.
tau_st;
622 svc[0].S = service.
ST;
626 throw NumericError(
ErrorKind{
"Degenerate"},
"fj_return_rt1: MMAPPH1FCFS returned no sojourn-time law");
654 const std::vector<double>& pers, std::size_t C,
FjTMode mode) {
658 const std::size_t n = ct.
T.
rows();
660 std::vector<double> phi(n, 0.0);
661 for (std::size_t i = 0; i < n; ++i) {
663 for (std::size_t j = 0; j < n; ++j) s += ct.
T(i, j) - ct.
S_Arr(i, j);
672 std::vector<double> pi0n = pi.
pi0;
674 for (
double v : pi0n) sp += v;
675 for (
double& v : pi0n) v /= sp;
677 const std::size_t dim = arrival.
ma * gs.
newdim;
679 const std::size_t dim_service = dim + dim_notbusy;
680 const std::size_t Tr = ST.
rows();
682 if (Tr != dim_service)
683 throw NumericError(
ErrorKind{
"Degenerate"},
"fj_return_rt2: the tagged-job service space and the phase blocks "
686 std::vector<double> notbusy_start(dim_service, 0.0);
687 for (std::size_t i = 0; i < dim; ++i)
688 notbusy_start[i] = (1.0 - w.
prob_wait) * pi0n[i];
693 std::vector<double> busy_start(dim_service, 0.0);
695 const std::vector<double> aTS =
vecmul(w.
alfa, TS);
696 if (aTS.size() != dim)
697 throw NumericError(
ErrorKind{
"Degenerate"},
"fj_return_rt2: the all-busy phase space and the tagged job's "
698 "all-busy service block have different sizes");
700 for (
double v : aTS) s += v;
701 for (std::size_t i = 0; i < dim; ++i) busy_start[i] = w.
prob_wait * aTS[i] / s;
703 for (std::size_t i = 0; i < TS.
rows(); ++i) {
705 for (std::size_t j = 0; j < TS.
cols(); ++j) s += TS(i, j);
706 for (std::size_t j = 0; j < TS.
cols(); ++j) TS(i, j) /= s;
709 const std::vector<double> stat = [&] {
711 for (std::size_t i = 0; i < Tr; ++i)
712 for (std::size_t j = 0; j < Tr; ++j) negST(i, j) = -ST(i, j);
713 return fjdetail::rdivide_row(busy_start, negST);
715 std::vector<bool> nz(Tr,
false);
716 for (std::size_t i = 0; i < Tr; ++i) nz[i] = stat[i] > 0.0;
718 std::vector<double> tr_start(Tr, 0.0);
719 for (std::size_t j = 0; j < Tr; ++j) {
721 for (std::size_t k = 0; k < Tr; ++k) s += ST(j, k);
722 tr_start[j] = -s * stat[j];
726 for (std::size_t i = 0; i < Tr; ++i) {
727 if (!nz[i])
continue;
728 for (std::size_t j = 0; j < Tr; ++j)
729 if (nz[j]) tr_ST(i, j) = ST(j, i) * stat[j] / stat[i];
731 std::vector<double> tr_exit(Tr, 0.0);
732 for (std::size_t i = 0; i < Tr; ++i) {
734 for (std::size_t j = 0; j < Tr; ++j) s += tr_ST(i, j);
740 for (std::size_t i = 0; i < Sc; ++i)
741 for (std::size_t j = 0; j < Sc; ++j) TS2(i, j) = TS(j, i) * w.
alfa[j];
742 for (std::size_t i = 0; i < Sc; ++i) {
744 for (std::size_t j = 0; j < Sc; ++j) s += TS2(i, j);
745 if (std::fabs(s) < 10e-12) s = 1.0;
746 for (std::size_t j = 0; j < Sc; ++j) TS2(i, j) /= s;
750 for (std::size_t i = 0; i < Sc; ++i)
751 for (std::size_t j = 0; j < Sc; ++j) tildeP(i, j) = tr_exit[i] * TS2(i, j);
753 std::vector<std::size_t> keep;
754 for (std::size_t i = 0; i < Tr; ++i)
755 if (nz[i]) keep.push_back(i);
756 const std::size_t m_tr = keep.size();
758 const std::size_t total = Tr + m_tr + Sc;
759 std::vector<double> gamma(total, 0.0);
760 for (std::size_t i = 0; i < Tr; ++i) gamma[i] = notbusy_start[i];
761 for (std::size_t i = 0; i < m_tr; ++i) gamma[Tr + i] = tr_start[keep[i]];
764 for (std::size_t i = 0; i < Tr; ++i)
765 for (std::size_t j = 0; j < Tr; ++j) Cres(i, j) = ST(i, j);
766 for (std::size_t i = 0; i < m_tr; ++i) {
767 for (std::size_t j = 0; j < m_tr; ++j) Cres(Tr + i, Tr + j) = tr_ST(keep[i], keep[j]);
768 for (std::size_t j = 0; j < Sc; ++j) Cres(Tr + i, Tr + m_tr + j) = tildeP(keep[i], j);
770 for (std::size_t i = 0; i < Sc; ++i)
771 for (std::size_t j = 0; j < Sc; ++j)
772 Cres(Tr + m_tr + i, Tr + m_tr + j) = w.
wait_Smat(i, j);
796 const std::vector<double>& pers,
797 const std::vector<std::size_t>& K,
798 const std::vector<std::size_t>& Cs,
FjTMode mode) {
800 if (!(arrival.
lambda / service.
mu < 1.0))
801 throw InputError(
"mainFJ: the system is not stable, the mean arrival rate " +
802 std::to_string(arrival.
lambda) +
803 " is not below the mean service rate " + std::to_string(service.
mu));
804 if (Cs.empty())
throw InputError(
"mainFJ: no truncation level C was given");
805 if (pers.empty())
throw InputError(
"mainFJ: no percentile was requested");
807 std::vector<double> rt1, rt2;
808 for (std::size_t c = 0; c < Cs.size(); ++c) {
813 std::vector<FjCodesPercentiles> out(K.size());
814 for (std::size_t k = 0; k < K.size(); ++k) {
816 out[k].percentiles.resize(pers.size());
817 out[k].RTp.assign(pers.size(), 0.0);
818 for (std::size_t p = 0; p < pers.size(); ++p) {
819 out[k].percentiles[p] = 100.0 * pers[p];
820 out[k].RTp[p] = rt1[p] + (rt2[p] - rt1[p]) * log(
static_cast<double>(K[k])) / log(2.0);
NumericError(const std::string &what)
Eigenvalues and singular values, backed by LAPACK.
The exception types the port throws.
The state-space construction of FJ_codes, the fork-join response-time-tail approximation of Z.
Conversion of a LINE MAP into the arrival or service descriptor of the fork-join response-time-tail a...
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
Least squares for a rectangular system, exact-capable.
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...
std::vector< double > fj_return_rt1(const FjDist< double > &arrival, const FjDist< double > &service, const std::vector< double > &pers)
Port of returnRT1.m: the response-time percentiles of the ONE-node queue, which are exact.
std::vector< double > fj_return_per(const std::vector< double > &vec, const Matrix< double > &A, const std::vector< double > &pers)
Port of returnPer.m: the percentiles of a (possibly defective) phase-type law, by uniformization.
std::vector< FjCodesPercentiles > fj_main(const FjDist< double > &arrival, const FjDist< double > &service, const std::vector< double > &pers, const std::vector< std::size_t > &K, const std::vector< std::size_t > &Cs, FjTMode mode)
Port of mainFJ.m: the response-time percentiles of a K-node fork-join queue, interpolated between the...
FjTMode fj_parse_tmode(const std::string &s)
Parse the reference's T_Mode string, whose default is 'NARE'.
std::vector< double > fj_boundary_solve(const Matrix< double > &pi0mat, const Matrix< double > &T)
The boundary solve both branches of computePi.m end with:
FjCodesSRK fj_construct_srk(std::size_t C, const FjDist< double > &service, const FjCodesServiceH &h, const Matrix< double > &S)
Port of constructSRK.m.
Matrix< double > fj_compute_t_nare(const Matrix< double > &D0, const Matrix< double > &D1, const Matrix< double > &S, const Matrix< double > &A_jump, double *residual)
Port of computeT_NARE.m: the T matrix as the stable invariant subspace of.
FjCodesServiceH fj_build_service_h(const FjDist< double > &service)
Port of build_Service_h.m.
FjCodesGenService fj_generate_service(const FjDist< double > &service, const FjCodesServiceH &h, std::size_t C, const Matrix< double > &S)
Port of generateService.m.
FjCodesT fj_compute_t(const FjDist< double > &arrival, const FjDist< double > &service, const FjCodesServiceH &h, std::size_t C, FjTMode mode)
Port of computeT.m.
FjCodesRT2 fj_return_rt2(const FjDist< double > &arrival, const FjDist< double > &service, const std::vector< double > &pers, std::size_t C, FjTMode mode)
Port of returnRT2.m: the response-time percentiles of the TWO-node fork-join queue,...
FjTMode
Which route computeT.m takes to the T matrix.
Matrix< double > fj_construct_not_all_busy(std::size_t C, const FjDist< double > &service, const FjCodesServiceH &h)
Port of constructNotAllBusy.m.
FjCodesSA fj_build_sa(const FjDist< double > &service, const FjCodesServiceH &h, std::size_t C)
Port of build_SA.m.
FjCodesPi fj_compute_pi(const Matrix< double > &T, const FjDist< double > &arrival, const FjDist< double > &service, const FjCodesServiceH &h, std::size_t C, const Matrix< double > &S, const Matrix< double > &A_jump)
Port of computePi.m: the all-busy boundary vector and E[n1].
FjCodesWait fj_return_wait(double En1, const std::vector< double > &pi0, const Matrix< double > &T, const std::vector< double > &phi, const std::vector< double > &sum_Ajump)
Port of returnWait.m: the stationary waiting time as a phase-type law.
Matrix< T > krons(const Matrix< T > &A, const Matrix< T > &B)
Kronecker sum, MATLAB's krons: kron(A, I_nb) + kron(I_na, B).
Matrix< T > kron(const Matrix< T > &A, const Matrix< T > &B)
Kronecker product.
std::vector< StDistrPh< T > > mmapph1fcfs_stdistr_ph(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc, double precision=1e-14)
Per-class SOJOURN TIME as a continuous phase-type law, BUTools' 'stDistrPH'.
Conservation laws of a layered queueing network, enumerated from its structure.
LstsqResult< T > lstsq(const Matrix< T > &A, const std::vector< T > &b, const T &tol)
Least-squares solution of A x = b, minimum-norm when A is rank deficient.
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
RealSchur schur_reorder(const RealSchur &s, const std::vector< double > &key)
Reorder the diagonal blocks of a real Schur form into DESCENDING key order, stably,...
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
std::vector< T > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
Matrix< double > sylvester_schur(const Matrix< double > &A, const Matrix< double > &B, const Matrix< double > &C)
A X + X B = C by Bartels-Stewart, at double.
Matrix< T > eye(std::size_t n)
Identity of order n.
RealSchur schur_decomposition(const Matrix< double > &A)
Real Schur factorization of a general square matrix (LAPACK dgees, unsorted).
Matrix< double > lyap_schur(const Matrix< double > &A, const Matrix< double > &B, const Matrix< double > &C)
MATLAB lyap(A,B,C) at double via Bartels-Stewart: A X + X B + C = 0.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Outcome of lstsq: the solution and whether the system was rank deficient.
Real Schur factorization A = Z T Z^T, with Z orthogonal and T upper quasi-triangular: 1 x 1 diagonal ...
Matrix< double > T
upper quasi-triangular factor
generateService.m: the service process seen by a tagged job.
Matrix< double > T
(newdim + dim_notbusy) square
std::size_t newdim
the all-busy part
std::size_t dim_notbusy
the not-all-busy part
One line of mainFJ's output cell: the percentiles of a K-node queue.
std::vector< double > percentiles
in PERCENT, as the reference stores them
std::vector< double > RTp
What computePi.m returns.
std::vector< double > pi0
unnormalized, length newdim * ma
double En1
mean number of arrivals in a not-all-busy period
What returnRT2.m produces, plus the waiting-time law it discards.
std::vector< double > RTp
response-time percentiles
std::vector< double > wait_alpha
the waiting-time law, defective
Matrix< double > wait_Smat
double residual
the T-matrix residual of computeT
build_SA.m: the level-constant generator and the head-of-line jump.
Matrix< double > A_jump
a job completes and the next enters service
Matrix< double > S
no job completes, ((C+1) m^2) square
constructSRK.m: the extended generator and the busy/idle projectors.
Matrix< double > R0
not-busy to busy, on an arrival
Matrix< double > Se
busy and not-busy phases together
Matrix< double > Sestar
the busy-to-not-busy block of Se, in place
Matrix< double > Ke
newdim x (newdim + dim_notbusy), the busy rows
Matrix< double > Kc
(newdim + dim_notbusy) x newdim, the busy columns
build_Service_h.m: the two-subtask phase process of one fork-join job.
Matrix< double > T
the all-busy generator, (newdim * ma) square
Matrix< double > S
build_SA's S, newdim square
double residual
inf-norm the reference prints
std::vector< double > sum_Ajump
row sums of kron(A_jump, I_ma)
Matrix< double > S_Arr
kron(S, I_ma)
std::size_t iterations
Sylvester mode only.
Matrix< double > A_jump
build_SA's A_jump, newdim square
What returnWait.m returns: the waiting time as a phase-type law.
std::vector< double > wait_alpha
defective, mass prob_wait
std::vector< double > alfa
-pi0 T^-1, the all-busy occupancy
Matrix< double > wait_Smat
Descriptor of an arrival or a service process.
An MMAP: the underlying MAP plus the per-class arrival matrices.
std::vector< Matrix< T > > Dc
per-class matrices, sum_c Dc = D1
The Sylvester equation A X + X B = C, and MATLAB's lyap(A,B,C).