5#ifndef LINE_API_QSYS_QSYS_MMAPGK1_H
6#define LINE_API_QSYS_QSYS_MMAPGK1_H
104namespace mmapgk1detail {
107inline double binom(std::size_t n, std::size_t k);
111std::vector<T> stat_left_null(
const Matrix<T>& G) {
113 const std::size_t n = G.
rows();
115 for (std::size_t i = 0; i < n; ++i)
116 for (std::size_t j = 0; j < n; ++j) A(j, i) = G(i, j);
117 for (std::size_t j = 0; j < n; ++j) A(n - 1, j) = one;
118 std::vector<T> b(n, zero);
125void stieltjes_nodes(
const lang::Distrib<T>& law, std::vector<T>& x, std::vector<T>& w) {
126 const std::size_t n_grid = 2400;
129 double hi = mean * 60.0;
131 if (std::isfinite(var) && var > 0.0) hi = std::max(hi, mean + 12.0 * std::sqrt(var));
132 const double step = hi /
static_cast<double>(n_grid);
133 x.assign(n_grid, zero);
134 w.assign(n_grid, zero);
137 for (std::size_t i = 0; i < n_grid; ++i) {
146 for (std::size_t i = 0; i < n_grid; ++i) w[i] = w[i] / mass;
159T raw_moment(
const lang::Distrib<T>& law, std::size_t j) {
162 }
catch (
const NumericError&) {
163 return num_traits<T>::from_double(std::numeric_limits<double>::infinity());
169Matrix<T> gk_kron(
const Matrix<T>& A,
const Matrix<T>& B) {
170 Matrix<T> C(A.rows() * B.rows(), A.cols() * B.cols(), num_traits<T>::from_int(0));
171 for (std::size_t i = 0; i < A.rows(); ++i)
172 for (std::size_t j = 0; j < A.cols(); ++j)
173 for (std::size_t p = 0; p < B.rows(); ++p)
174 for (std::size_t q = 0; q < B.cols(); ++q)
175 C(i * B.rows() + p, j * B.cols() + q) = A(i, j) * B(p, q);
181Matrix<T> matrix_lst(
const lang::Distrib<T>& law,
const Matrix<T>& U) {
182 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
183 const std::size_t n = U.rows();
188 const Matrix<T>& S = rep.D0;
189 const std::size_t ms = S.rows();
191 std::vector<T> s0(ms, zero);
192 for (std::size_t i = 0; i < ms; ++i) {
194 for (std::size_t j = 0; j < ms; ++j) r += S(i, j);
197 Matrix<T> Ims =
eye<T>(ms);
198 Matrix<T> KS = gk_kron(U, Ims);
199 const Matrix<T> In =
eye<T>(n);
200 const Matrix<T> IkS = gk_kron(In, S);
201 for (std::size_t i = 0; i < KS.rows(); ++i)
202 for (std::size_t j = 0; j < KS.cols(); ++j) KS(i, j) += IkS(i, j);
203 Matrix<T> betaM(1, ms, zero);
204 for (std::size_t i = 0; i < ms; ++i) betaM(0, i) = beta[i];
205 Matrix<T> s0M(ms, 1, zero);
206 for (std::size_t i = 0; i < ms; ++i) s0M(i, 0) = s0[i];
207 const Matrix<T> L = gk_kron(In, betaM);
208 Matrix<T> R = gk_kron(In, s0M);
210 for (std::size_t i = 0; i < X.rows(); ++i)
211 for (std::size_t j = 0; j < X.cols(); ++j) X(i, j) = -X(i, j);
217 for (std::size_t i = 0; i < n; ++i)
218 for (std::size_t j = 0; j < n; ++j) Ud(i, j) = Ud(i, j) * d;
222 stieltjes_nodes(law, x, w);
223 Matrix<T> F(n, n, zero);
224 for (std::size_t i = 0; i < x.size(); ++i) {
226 for (std::size_t p = 0; p < n; ++p)
227 for (std::size_t q = 0; q < n; ++q) Ut(p, q) = Ut(p, q) * x[i];
229 for (std::size_t p = 0; p < n; ++p)
230 for (std::size_t q = 0; q < n; ++q) F(p, q) += w[i] * E(p, q);
236typedef std::complex<double> cdbl;
239inline std::vector<cdbl> complex_solve(std::vector<std::vector<cdbl> > A, std::vector<cdbl> b) {
240 const std::size_t n = b.size();
241 for (std::size_t col = 0; col < n; ++col) {
242 std::size_t piv = col;
243 double best = std::abs(A[col][col]);
244 for (std::size_t r = col + 1; r < n; ++r) {
245 if (std::abs(A[r][col]) > best) { best = std::abs(A[r][col]); piv = r; }
247 if (piv != col) { std::swap(A[piv], A[col]); std::swap(b[piv], b[col]); }
248 for (std::size_t r = col + 1; r < n; ++r) {
249 const cdbl f = A[r][col] / A[col][col];
250 for (std::size_t c = col; c < n; ++c) A[r][c] -= f * A[col][c];
254 std::vector<cdbl> x(n);
255 for (std::size_t row = n; row-- > 0;) {
257 for (std::size_t c = row + 1; c < n; ++c) s -= A[row][c] * x[c];
258 x[row] = s / A[row][row];
272cdbl scalar_lst(
const lang::Distrib<T>& law, cdbl s) {
279cdbl wait_lst(
const Matrix<T>& D0,
const std::vector<Matrix<T> >& Dk,
280 const std::vector<lang::Distrib<T> >& svc,
const std::vector<T>& v0,
281 const std::vector<T>& lambdas, std::size_t k, cdbl s) {
282 const std::size_t ma = D0.rows();
283 std::vector<std::vector<cdbl> > M(ma, std::vector<cdbl>(ma, cdbl(0.0, 0.0)));
284 for (std::size_t i = 0; i < ma; ++i)
285 for (std::size_t j = 0; j < ma; ++j) M[i][j] = cdbl(num_traits<T>::to_double(D0(i, j)), 0.0);
286 for (std::size_t i = 0; i < ma; ++i) M[i][i] += s;
287 for (std::size_t q = 0; q < Dk.size(); ++q) {
288 const cdbl g = scalar_lst(svc[q], s);
289 for (std::size_t i = 0; i < ma; ++i)
290 for (std::size_t j = 0; j < ma; ++j)
291 M[i][j] += num_traits<T>::to_double(Dk[q](i, j)) * g;
294 std::vector<std::vector<cdbl> > A(ma, std::vector<cdbl>(ma, cdbl(0.0, 0.0)));
295 std::vector<cdbl> b(ma, cdbl(0.0, 0.0));
296 for (std::size_t i = 0; i < ma; ++i) {
297 for (std::size_t j = 0; j < ma; ++j) A[i][j] = M[j][i];
298 b[i] = s * num_traits<T>::to_double(v0[i]);
300 const std::vector<cdbl> f = complex_solve(A, b);
302 for (std::size_t i = 0; i < ma; ++i) {
304 for (std::size_t j = 0; j < ma; ++j) rowsum += num_traits<T>::to_double(Dk[k](i, j));
305 out += f[i] * rowsum;
307 return out / num_traits<T>::to_double(lambdas[k]);
312double euler_invert(
const Matrix<T>& D0,
const std::vector<Matrix<T> >& Dk,
313 const std::vector<lang::Distrib<T> >& svc,
const std::vector<T>& v0,
314 const std::vector<T>& lambdas, std::size_t k,
double t) {
315 if (t <= 0.0)
return wait_lst(D0, Dk, svc, v0, lambdas, k, cdbl(1e12, 0.0)).real();
316 const double A = 18.4;
317 const std::size_t nE = 15, mE = 11;
318 const double u = std::exp(A / 2) / t;
319 const double x = A / (2 * t);
320 std::vector<double> terms(nE + mE + 1, 0.0);
321 terms[0] = wait_lst(D0, Dk, svc, v0, lambdas, k, cdbl(x, 0.0)).real() / x / 2.0;
322 for (std::size_t j = 1; j <= nE + mE; ++j) {
323 const cdbl s(x, M_PI *
static_cast<double>(j) / t);
324 terms[j] = ((j % 2 == 0) ? 1.0 : -1.0) * (wait_lst(D0, Dk, svc, v0, lambdas, k, s) / s).real();
326 std::vector<double> partial(terms.size(), 0.0);
328 for (std::size_t j = 0; j < terms.size(); ++j) { run += terms[j]; partial[j] = run; }
330 for (std::size_t j = 0; j <= mE; ++j) F += binom(mE, j) / std::pow(2.0,
static_cast<double>(mE)) * partial[nE + j];
332 return std::min(std::max(F, 0.0), 1.0);
336inline double binom(std::size_t n, std::size_t k) {
338 for (std::size_t i = 1; i <= k; ++i) r = r * static_cast<double>(n - k + i) /
static_cast<double>(i);
339 return std::floor(r + 0.5);
357 const std::vector<T>& w_points, std::size_t num_w_moms,
358 double tol, std::size_t iter_max) {
360 "qsys_mmapgk1 requires transcendental arithmetic");
361 using namespace mmapgk1detail;
363 if (MMAP.size() < 3)
throw InputError(
"qsys_mmapgk1: the MMAP must carry a marked block");
364 const std::size_t K = MMAP.size() - 2;
365 if (svc.size() != K)
throw InputError(
"qsys_mmapgk1: one service law per marked type");
367 const std::size_t ma = D0.
rows();
368 std::vector<Matrix<T> > Dk;
370 for (std::size_t k = 0; k < K; ++k) {
371 Dk.push_back(MMAP[k + 2]);
372 for (std::size_t i = 0; i < ma; ++i)
373 for (std::size_t j = 0; j < ma; ++j) Dsum(i, j) += Dk[k](i, j);
376 const std::vector<T> theta = stat_left_null(Dsum);
377 std::vector<T> lambdas(K, zero), mean_s(K, zero);
379 for (std::size_t k = 0; k < K; ++k) {
381 for (std::size_t i = 0; i < ma; ++i)
382 for (std::size_t j = 0; j < ma; ++j) lam += theta[i] * Dk[k](i, j);
385 rho += lam * mean_s[k];
387 if (rho >= one)
throw InputError(
"qsys_mmapgk1: load rho must be strictly less than 1");
390 for (std::size_t it = 0; it < iter_max; ++it) {
392 for (std::size_t k = 0; k < K; ++k) {
393 const Matrix<T> Fk = matrix_lst(svc[k], U);
395 for (std::size_t i = 0; i < ma; ++i)
396 for (std::size_t j = 0; j < ma; ++j) Unew(i, j) += P(i, j);
399 for (std::size_t i = 0; i < ma; ++i)
400 for (std::size_t j = 0; j < ma; ++j)
403 if (diff <= tol)
break;
407 for (std::size_t i = 0; i < ma; ++i) {
409 for (std::size_t j = 0; j < ma; ++j) rowsum += U(i, j);
412 std::vector<T> v0 = stat_left_null(U);
413 for (std::size_t i = 0; i < ma; ++i) v0[i] = v0[i] * (one - rho);
416 std::vector<Matrix<T> > Mder;
417 for (std::size_t j = 0; j <= num_w_moms + 1; ++j) {
421 for (std::size_t k = 0; k < K; ++k)
422 for (std::size_t i = 0; i < ma; ++i)
423 for (std::size_t q = 0; q < ma; ++q) Mj(i, q) += Dk[k](i, q);
425 for (std::size_t k = 0; k < K; ++k) {
426 const T mom = raw_moment(svc[k], j);
427 const double sign = (j % 2 == 0) ? 1.0 : -1.0;
428 for (std::size_t i = 0; i < ma; ++i)
429 for (std::size_t q = 0; q < ma; ++q)
433 for (std::size_t i = 0; i < ma; ++i) Mj(i, i) += one;
437 const std::vector<T> e(ma, one);
440 const std::vector<T> v =
mulvec(Mder[1], e);
441 for (std::size_t i = 0; i < ma; ++i) denom += theta[i] * v[i];
443 std::vector<std::vector<T> > fder;
444 fder.push_back(theta);
448 for (std::size_t i = 0; i < ma; ++i)
449 for (std::size_t j = 0; j < ma; ++j) Abase(j, i) = Mder[0](i, j);
450 for (std::size_t i = 0; i < ma; ++i) Abase(ma, i) = one;
452 for (std::size_t i = 0; i < ma; ++i)
453 for (std::size_t j = 0; j < ma; ++j) {
455 for (std::size_t q = 0; q < ma + 1; ++q) s += Abase(q, i) * Abase(q, j);
459 for (std::size_t j = 1; j <= num_w_moms; ++j) {
460 std::vector<T> rhs(ma, zero);
461 if (j == 1)
for (std::size_t i = 0; i < ma; ++i) rhs[i] = v0[i];
462 for (std::size_t i = 0; i < j; ++i) {
463 const double c = binom(j, i);
464 for (std::size_t q = 0; q < ma; ++q) {
466 for (std::size_t p = 0; p < ma; ++p) acc += fder[i][p] * Mder[j - i](p, q);
470 std::vector<T> rhsAug(ma + 1, zero);
471 for (std::size_t q = 0; q < ma; ++q) rhsAug[q] = rhs[q];
472 std::vector<T> Atb(ma, zero);
473 for (std::size_t i = 0; i < ma; ++i) {
475 for (std::size_t q = 0; q < ma + 1; ++q) s += Abase(q, i) * rhsAug[q];
478 const std::vector<T> fp =
mulvec(AtAinv, Atb);
480 for (std::size_t i = 0; i < j; ++i) {
481 const std::vector<T> v =
mulvec(Mder[j + 1 - i], e);
483 for (std::size_t p = 0; p < ma; ++p) inner += fder[i][p] * v[p];
488 const std::vector<T> v =
mulvec(Mder[1], e);
489 for (std::size_t p = 0; p < ma; ++p) fpM1e += fp[p] * v[p];
492 std::vector<T>
fj(ma, zero);
493 for (std::size_t p = 0; p < ma; ++p)
fj[p] = fp[p] + cfree * theta[p];
500 for (std::size_t k = 0; k < K; ++k) r.
arrivalRate += lambdas[k];
503 r.
waitMoments.assign(K, std::vector<T>(num_w_moms, zero));
507 for (std::size_t k = 0; k < K; ++k) {
508 for (std::size_t j = 1; j <= num_w_moms; ++j) {
509 const std::vector<T> v =
mulvec(Dk[k], e);
511 for (std::size_t p = 0; p < ma; ++p) inner += fder[j][p] * v[p];
512 const double sign = (j % 2 == 0) ? 1.0 : -1.0;
521 if (!w_points.empty()) {
522 r.
waitCDF.assign(K, std::vector<T>(w_points.size(), zero));
523 for (std::size_t k = 0; k < K; ++k) {
524 for (std::size_t it = 0; it < w_points.size(); ++it) {
527 euler_invert(D0, Dk, svc, v0, lambdas, k, t));
538 return qsys_mmapgk1(MMAP, svc, std::vector<T>(),
static_cast<std::size_t
>(3), 1e-12,
539 static_cast<std::size_t
>(10000));
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LU factorization with partial pivoting, templated on the number type.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
mam::Map< T > dist_to_map(const Distrib< T > &d)
T dist_lst(const Distrib< T > &d, const T &s)
sn.lst: the Laplace-Stieltjes transform E[exp(-sX)].
T dist_cdf(const Distrib< T > &d, const T &x)
F(x) = P{X <= x}, MATLAB's Distribution.evalCDF.
bool process_is_markovian(ProcessType p)
ProcessType.isMarkovian: true when sn.proc carries an exact matrix representation of the law,...
T dist_moment(const Distrib< T > &d, unsigned k)
The k-th raw moment.
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
MmapGk1Result< T > qsys_mmapgk1(const std::vector< Matrix< T > > &MMAP, const std::vector< lang::Distrib< T > > &svc, const std::vector< T > &w_points, std::size_t num_w_moms, double tol, std::size_t iter_max)
MMAP[K]/G[K]/1 FCFS, per type.
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 > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Matrix< T > eye(std::size_t n)
Identity of order n.
Matrix< T > expm(const Matrix< T > &A)
Matrix exponential exp(A).
Number-type abstraction for the templated API port.
Return value of qsys_mmapgk1, mirroring the MATLAB struct.
T utilization
rho = sum_k lambda_k E[S_k]
std::vector< T > lambdas
per-type arrival rates
std::vector< T > waitPoints
the requested points
std::vector< T > meanWaitingTime
per-type E[Wq]
std::vector< T > meanSojournTime
per-type E[Wq] + E[S]
std::vector< std::vector< T > > waitCDF
[type][point], P(Wk <= t)
T arrivalRate
sum of the per-type rates
std::vector< T > idleVector
v0, summing to 1 - rho
T meanQueueLength
E[N] by Little over all types.
std::vector< std::vector< T > > waitMoments
[type][order], E[Wk^j]