5#ifndef LINE_API_MAM_MAP_M1PS_H
6#define LINE_API_MAM_MAP_M1PS_H
106 const std::size_t m = C.
rows();
110 for (std::size_t i = 0; i < m; ++i) X(i, i) -= mu;
115 for (std::size_t i = 0; i < m; ++i)
116 for (std::size_t j = 0; j < m; ++j) res(i, j) += t1(i, j) + mu * t2(i, j);
118 for (std::size_t i = 0; i < m; ++i) {
120 for (std::size_t j = 0; j < m; ++j) s +=
num_abs(T(res(i, j)));
121 if (s > worst) worst = s;
138 "map_compute_R requires transcendental arithmetic");
139 const std::size_t m = C.
rows();
141 throw InputError(
"map_compute_R: C and D must be square and of equal order");
144 for (
unsigned it = 0; it < max_iter; ++it) {
146 for (std::size_t i = 0; i < m; ++i)
147 for (std::size_t j = 0; j < m; ++j) X(i, j) = C(i, j) + mu * R(i, j);
148 for (std::size_t i = 0; i < m; ++i) X(i, i) -= mu;
150 for (std::size_t i = 0; i < m; ++i)
151 for (std::size_t j = 0; j < m; ++j) Rn(i, j) = -Rn(i, j);
153 for (std::size_t i = 0; i < m; ++i) {
155 for (std::size_t j = 0; j < m; ++j) s +=
num_abs(T(Rn(i, j) - R(i, j)));
156 if (s > diff) diff = s;
159 if (diff < tol)
break;
161 for (std::size_t i = 0; i < m; ++i)
162 for (std::size_t j = 0; j < m; ++j)
163 if (R(i, j) < zero) R(i, j) = zero;
185 unsigned max_iter,
const T& tol) {
187 "map_compute_R_quadratic requires transcendental arithmetic");
188 const std::size_t m = C.
rows();
190 throw InputError(
"map_compute_R_quadratic: C and D must be square and of equal order");
196 const T a = mu, b = T(C(0, 0) - mu), c = D(0, 0);
198 if (disc < zero)
throw NumericError(
"map_compute_R: no real solution for R");
200 const T sd = T(sqrt(disc));
203 if (r1 >= zero && r1 < one)
205 else if (r2 >= zero && r2 < one)
208 throw NumericError(
"map_compute_R: no valid solution in [0,1) for R");
213 for (std::size_t i = 0; i < m; ++i) CmuI(i, i) -= mu;
215 for (std::size_t i = 0; i < m; ++i)
216 for (std::size_t j = 0; j < m; ++j) R(i, j) = -R(i, j);
219 for (std::size_t i = 0; i < m; ++i)
220 for (std::size_t j = 0; j < m; ++j) muImC(i, j) = -C(i, j);
221 for (std::size_t i = 0; i < m; ++i) muImC(i, i) += mu;
224 for (
unsigned it = 0; it < max_iter; ++it) {
227 for (std::size_t i = 0; i < m; ++i)
228 for (std::size_t j = 0; j < m; ++j) X(i, j) = D(i, j) + mu * R2(i, j);
231 for (std::size_t i = 0; i < m; ++i) {
233 for (std::size_t j = 0; j < m; ++j) s +=
num_abs(T(Rn(i, j) - R(i, j)));
234 if (s > diff) diff = s;
237 if (diff < tol)
break;
239 for (std::size_t i = 0; i < m; ++i)
240 for (std::size_t j = 0; j < m; ++j)
241 if (R(i, j) < zero) R(i, j) = zero;
259 std::size_t N, std::size_t K) {
260 const std::size_t M = C.
rows();
262 throw InputError(
"map_m1ps_h_recursive: C and D must be square and of equal order");
266 for (std::size_t i = 0; i < M; ++i) {
267 const T a =
num_abs(T(C(i, i)));
268 if (a > theta) theta = a;
270 const T theta_plus_mu = theta + mu;
271 if (theta_plus_mu == zero)
throw NumericError(
"map_m1ps_h_recursive: theta + mu is zero");
273 for (std::size_t i = 0; i < M; ++i) thetaIplusC(i, i) += theta;
275 std::vector<std::vector<std::vector<T>>> h(N + 1,
276 std::vector<std::vector<T>>(K + 1, std::vector<T>()));
277 for (std::size_t n = 0; n <= N; ++n) h[n][0] = ones<T>(M);
279 for (std::size_t k = 0; k + 1 <= K; ++k) {
280 for (std::size_t n = 0; n <= N; ++n) {
281 std::vector<T> acc =
mulvec(thetaIplusC, h[n][k]);
285 for (std::size_t i = 0; i < M; ++i) acc[i] += c * h[n - 1][k][i];
288 const std::vector<T> t3 =
mulvec(D, h[n + 1][k]);
289 for (std::size_t i = 0; i < M; ++i) acc[i] += t3[i];
291 for (std::size_t i = 0; i < M; ++i) acc[i] /= theta_plus_mu;
309namespace m1ps_detail {
313T poisson_pmf(
const T& a, std::size_t k) {
326void poisson_window(
const T& a,
const T& eps_prime, std::size_t& L, std::size_t& K) {
332 const double ad = num_traits<T>::to_double(a);
333 const double lo = ad - 10.0 * std::sqrt(ad);
334 L = lo > 0.0 ?
static_cast<std::size_t
>(std::floor(lo)) : 0;
335 std::size_t hi =
static_cast<std::size_t
>(std::ceil(ad + 10.0 * std::sqrt(ad)));
336 const T one = num_traits<T>::from_int(1);
338 T s = num_traits<T>::from_int(0);
339 for (std::size_t k = L; k <= hi; ++k) s += poisson_pmf(a, k);
340 if (s >= one - eps_prime || hi >= 10000)
break;
348std::vector<T> m1ps_pi(
const Matrix<T>& C,
const Matrix<T>& D) {
349 Matrix<T> Q(C.rows(), C.cols());
350 for (std::size_t i = 0; i < C.rows(); ++i)
351 for (std::size_t j = 0; j < C.cols(); ++j) Q(i, j) = C(i, j) + D(i, j);
368 const std::vector<T>& x,
const T& epsilon,
369 const T& epsilon_prime) {
371 "map_m1ps_sojourn requires transcendental arithmetic");
372 const std::size_t M = C.
rows();
374 throw InputError(
"map_m1ps_sojourn: C and D must be square and of equal order");
376 throw InputError(
"map_m1ps_sojourn: the service rate must be positive");
380 const std::vector<T> pi = m1ps_detail::m1ps_pi(C, D);
381 const std::vector<T> e =
ones<T>(M);
384 const std::vector<T> t =
vecmul(pi, D);
385 for (
const T& v : t) lambda += v;
387 if (!(lambda > zero))
throw NumericError(
"map_m1ps_sojourn: zero arrival rate");
388 if (!(T(lambda / mu) < one))
389 throw NumericError(
"map_m1ps_sojourn: the system is unstable, rho >= 1");
392 std::vector<T> pi0 = pi;
394 const std::vector<T> t =
vecmul(pi, R);
395 for (std::size_t i = 0; i < M; ++i) pi0[i] = pi[i] - t[i];
399 std::size_t N_epsilon = 0;
402 std::vector<T> row = pi0;
404 for (std::size_t n = 0; n <= 1000; ++n) {
405 const std::vector<T> t =
vecmul(row, D);
407 for (
const T& v : t) add += v;
409 if (cum > one - epsilon) {
416 if (!found || N_epsilon == 0) N_epsilon = 100;
420 for (std::size_t i = 0; i < M; ++i) {
421 const T a =
num_abs(T(C(i, i)));
422 if (a > theta) theta = a;
424 const T theta_plus_mu = theta + mu;
427 std::vector<std::vector<T>> weights(N_epsilon + 1);
429 std::vector<T> row = pi0;
430 for (std::size_t n = 0; n <= N_epsilon; ++n) {
431 weights[n] =
vecmul(row, D);
439 out.
w_bar.assign(x.size(), zero);
442 for (std::size_t idx = 0; idx < x.size(); ++idx) {
444 if (xv < zero)
throw InputError(
"map_m1ps_sojourn: the evaluation points must be >= 0");
445 std::size_t L = 0, K = 0;
446 m1ps_detail::poisson_window(T(theta_plus_mu * xv), epsilon_prime, L, K);
448 const std::vector<std::vector<std::vector<T>>> h =
451 std::vector<T> pois(K + 1 - L);
452 for (std::size_t k = L; k <= K; ++k)
453 pois[k - L] = m1ps_detail::poisson_pmf(T(theta_plus_mu * xv), k);
455 for (std::size_t n = 0; n <= N_epsilon; ++n) {
456 std::vector<T> sum_k(M, zero);
457 for (std::size_t k = L; k <= K; ++k)
458 for (std::size_t i = 0; i < M; ++i) sum_k[i] += pois[k - L] * h[n][k][i];
460 for (std::size_t i = 0; i < M; ++i) term += weights[n][i] * sum_k[i];
461 out.
w_bar[idx] += term / lambda;
463 for (
const T& v : sum_k) s += v;
474 const std::vector<T>& x) {
495 const std::vector<T>& x,
const T& epsilon,
496 const T& epsilon_prime) {
498 "map_m1ps_cdfrespt requires transcendental arithmetic");
499 const std::size_t M = C.
rows();
501 throw InputError(
"map_m1ps_cdfrespt: C and D must be square and of equal order");
503 throw InputError(
"map_m1ps_cdfrespt: the service rate must be positive");
507 const std::vector<T> pi = m1ps_detail::m1ps_pi(C, D);
510 const std::vector<T> t =
vecmul(pi, D);
511 for (
const T& v : t) lambda += v;
513 if (!(lambda > zero))
throw NumericError(
"map_m1ps_cdfrespt: zero arrival rate");
514 if (!(T(lambda / mu) < one))
515 throw NumericError(
"map_m1ps_cdfrespt: the system is unstable, rho >= 1");
518 std::vector<T> pi0(M);
520 const std::vector<T> t =
vecmul(pi, R);
521 for (std::size_t i = 0; i < M; ++i) pi0[i] = pi[i] - t[i];
526 throw NumericError(
"map_m1ps_cdfrespt: R has spectral radius >= 1");
528 std::size_t N_epsilon;
532 const double est = std::ceil(std::log(e_ * (1.0 - s_)) / std::log(s_));
533 const long n_est =
static_cast<long>(est);
534 N_epsilon =
static_cast<std::size_t
>(n_est < 10 ? 10 : (n_est > 10000 ? 10000 : n_est));
540 for (std::size_t i = 0; i < M; ++i) {
541 const T a =
num_abs(T(C(i, i)));
542 if (a > theta) theta = a;
544 const T theta_plus_mu = theta + mu;
547 std::vector<std::size_t> Lp(x.size(), 0), Kp(x.size(), 0);
548 std::size_t K_global = 0;
549 for (std::size_t idx = 0; idx < x.size(); ++idx) {
550 if (x[idx] < zero)
throw InputError(
"map_m1ps_cdfrespt: the points must be >= 0");
551 m1ps_detail::poisson_window(T(theta_plus_mu * x[idx]), epsilon_prime, Lp[idx], Kp[idx]);
552 if (Kp[idx] > K_global) K_global = Kp[idx];
555 const std::vector<std::vector<std::vector<T>>> h =
560 std::vector<std::vector<T>> weights(N_epsilon + 1);
561 std::size_t N_actual = N_epsilon;
563 std::vector<T> row = pi0;
564 for (std::size_t n = 0; n <= N_epsilon; ++n) {
565 weights[n] =
vecmul(row, D);
567 for (
const T& v : weights[n]) {
571 if (n > 0 && wn < weight_tol) {
581 out.
k_max = K_global;
582 out.
w_bar.assign(x.size(), zero);
585 for (std::size_t idx = 0; idx < x.size(); ++idx) {
586 const std::size_t L = Lp[idx], K = Kp[idx];
587 std::vector<T> pois(K + 1 - L);
588 for (std::size_t k = L; k <= K; ++k)
589 pois[k - L] = m1ps_detail::poisson_pmf(T(theta_plus_mu * x[idx]), k);
590 for (std::size_t n = 0; n <= N_actual; ++n) {
591 std::vector<T> sum_k(M, zero);
592 for (std::size_t k = L; k <= K; ++k)
593 for (std::size_t i = 0; i < M; ++i) sum_k[i] += pois[k - L] * h[n][k][i];
595 for (std::size_t i = 0; i < M; ++i) term += weights[n][i] * sum_k[i];
596 out.
w_bar[idx] += term / lambda;
598 for (
const T& v : sum_k) s += v;
608 const std::vector<T>& x) {
NumericError(const std::string &what)
Steady-state distribution of a continuous-time Markov chain.
The exception types the port throws.
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.
MapM1psResult< T > map_m1ps_sojourn(const Matrix< T > &C, const Matrix< T > &D, const T &mu, const std::vector< T > &x, const T &epsilon, const T &epsilon_prime)
Complementary sojourn time distribution of a MAP/M/1-PS queue (map_m1ps_sojourn.m).
std::vector< std::vector< std::vector< T > > > map_m1ps_h_recursive(const Matrix< T > &C, const Matrix< T > &D, const T &mu, std::size_t N, std::size_t K)
The vectors h_{n,k} of Theorem 1 (map_m1ps_h_recursive.m).
Matrix< T > map_compute_R(const Matrix< T > &C, const Matrix< T > &D, const T &mu, unsigned max_iter, const T &tol)
Rate matrix R of a MAP/M/1 queue, the minimal nonnegative solution of D + R (C - mu I) + mu R^2 = 0,...
T qbd_caudal(const Matrix< T > &R, unsigned iter_max, const T &tol)
Caudal characteristic eta = sp(R), the decay rate of the queue-length tail.
T map_compute_R_residual(const Matrix< T > &C, const Matrix< T > &D, const T &mu, const Matrix< T > &R)
Residual ||D + R (C - mu I) + mu R^2||_inf of the MAP/M/1 rate equation.
Matrix< T > map_compute_R_quadratic(const Matrix< T > &C, const Matrix< T > &D, const T &mu, unsigned max_iter, const T &tol)
The same R by the other splitting, R <- (D + mu R^2) (mu I - C)^-1, warm started at -D (C - mu I)^-1 ...
MapM1psResult< T > map_m1ps_cdfrespt(const Matrix< T > &C, const Matrix< T > &D, const T &mu, const std::vector< T > &x, const T &epsilon, const T &epsilon_prime)
Complementary sojourn time distribution of a MAP/M/1-PS queue by the spectral-radius truncation (map_...
std::vector< T > ctmc_solve(const Matrix< T > &Qin)
Steady-state distribution of a continuous-time Markov chain.
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.
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.
std::vector< T > ones(std::size_t n)
Column vector of ones, the ubiquitous e in MAP algebra.
Number-type abstraction for the templated API port.
Quasi-birth-death processes: the rate matrix R, the fundamental matrix G, the caudal characteristic,...
What the two MAP/M/1-PS sojourn entry points return.
std::vector< T > w_bar
Pr[W > x] at each requested point.
std::vector< std::vector< T > > w_bar_n_unweighted
The reference's second output, sum_i (sum_k)_i / M, per level n and per point.
std::size_t k_max
largest uniformization index used
std::size_t n_levels
levels actually summed