5#ifndef LINE_LANG_DISTRIBUTION_H
6#define LINE_LANG_DISTRIBUTION_H
64 const double n = std::ceil(1.0 / scv);
65 const double capped = n < 1.0 ? 1.0 : (n > 100.0 ? 100.0 : n);
66 return static_cast<unsigned>(capped);
81 ": the distribution is a Prior, which is a weighted set of alternative "
82 "MODELS rather than one law; solve the model with SolverUQ, which "
83 "replaces each Prior by one alternative per design point");
100 "dist_to_map: a Normal puts mass below zero, so it is not the law of any duration and "
101 "has no Markovian representation. The default arm of this function would hand back the "
102 "Erlang fit of its mean, which is a positive law with the same mean and nothing else "
103 "in common; refusing instead. A Normal reaches this port only as the parameter density "
104 "of a continuous Prior");
121 for (
const T& x : d.
trace) {
126 return mam::aph_fit(T(m1 / n), T(m2 / n), T(m3 / n)).aph;
129 "dist_to_map: fitting a Replayer trace to an acyclic phase-type needs "
130 "transcendental arithmetic, which exact rational arithmetic does not provide; "
131 "solve in double or Real<n>, or replace the trace by a fitted distribution");
177 if (s == zero)
return one;
191 "dist_lst: the transform of a Replayer trace is a mean of exp, which exact "
192 "arithmetic has no representation for; use the double or real backend");
203 "dist_lst: the transform of a Det is exp(-s m), which exact arithmetic has "
204 "no representation for; use the double or real backend");
213 "dist_lst: the transform of a Uniform evaluates exp, which exact arithmetic "
214 "has no representation for; use the double or real backend");
226 "dist_lst: the transform of a Normal is a value of exp, which exact arithmetic "
227 "has no representation for; use the double or real backend");
248 "dist_lst: the transform of a Pareto is an integral of exp, which exact "
249 "arithmetic has no representation for; use the double or real backend");
255 auto g = [&alpha, &k, &s, &tiny, &one](
const T& u) -> T {
257 const T uu = u > tiny ? u : tiny;
263 const T val = qsys::detail::num_integral<T>(g, zero, one,
266 return T(alpha * val);
272 "dist_lst: the transform of a Weibull is a numerical integral, which exact "
273 "arithmetic has no representation for; use the double or real backend");
279 const double upper = a * std::pow(-std::log(1e-10), 1.0 / r);
281 const double dx = upper / n;
283 for (
int i = 1; i <= n; ++i) {
284 const double x = i * dx;
286 (r / a) * std::pow(x / a, r - 1.0) * std::exp(-std::pow(x / a, r));
287 acc += std::exp(-sv * x) * pdf;
295 "dist_lst: the transform of a Lognormal is a numerical integral, which exact "
296 "arithmetic has no representation for; use the double or real backend");
301 const double upper = std::exp(mu + 5.0 * sg);
303 const double dx = upper / n;
305 for (
int i = 1; i <= n; ++i) {
306 const double x = i * dx;
307 const double lx = std::log(x);
308 const double pdf = std::exp(-(lx - mu) * (lx - mu) / (2.0 * sg * sg)) /
309 (x * sg * std::sqrt(2.0 * M_PI));
310 acc += std::exp(-sv * x) * pdf;
318 "dist_lst: the transform of a Gamma is (1 + s theta)^-k, which exact "
319 "arithmetic has no representation for; use the double or real backend");
335 const std::size_t n = m.
D0.rows();
337 for (std::size_t i = 0; i < n; ++i)
338 for (std::size_t j = 0; j < n; ++j) A(i, j) = T((i == j ? s : zero) - m.
D0(i, j));
340 std::vector<T> rhs(n, zero);
341 for (std::size_t i = 0; i < n; ++i) {
343 for (std::size_t j = 0; j < n; ++j) acc += m.
D0(i, j);
347 std::vector<T> x = rhs;
348 for (std::size_t col = 0; col < n; ++col) {
349 std::size_t best = col;
351 for (std::size_t r = col + 1; r < n; ++r) {
359 for (std::size_t j = 0; j < n; ++j) std::swap(A(col, j), A(best, j));
360 std::swap(x[col], x[best]);
362 if (A(col, col) == zero)
throw NumericError(
ErrorKind{
"Singular"},
"dist_lst: singular transform matrix");
363 for (std::size_t r = 0; r < n; ++r) {
364 if (r == col)
continue;
365 const T f = T(A(r, col) / A(col, col));
366 if (f == zero)
continue;
367 for (std::size_t j = 0; j < n; ++j) A(r, j) = T(A(r, j) - f * A(col, j));
368 x[r] = T(x[r] - f * x[col]);
372 for (std::size_t i = 0; i < n; ++i) out += pie[i] * T(x[i] / A(i, i));
380T
dist_cdf(
const Distrib<T>& d,
const T& x);
406 "dist_lst at a complex argument requires transcendental arithmetic");
409 if (std::abs(s) == 0.0)
return std::complex<double>(1.0, 0.0);
413 return std::complex<double>(1.0, 0.0);
415 std::complex<double> acc(0.0, 0.0);
417 return acc /
static_cast<double>(d.
trace.size());
424 return (std::exp(-s * a) - std::exp(-s * b)) / (s * (b - a));
429 return std::exp(-mu * s + sg * sg * s * s / 2.0);
434 return std::pow(std::complex<double>(1.0, 0.0) + s * scale, -shape);
445 const std::size_t n = m.
D0.rows();
446 std::vector<std::vector<std::complex<double> > > A(
447 n, std::vector<std::complex<double> >(n, std::complex<double>(0.0, 0.0)));
448 std::vector<std::complex<double> > x(n, std::complex<double>(0.0, 0.0));
449 for (std::size_t i = 0; i < n; ++i) {
451 for (std::size_t j = 0; j < n; ++j) {
453 A[i][j] = (i == j ? s : std::complex<double>(0.0, 0.0)) - d0;
456 x[i] = std::complex<double>(-exit, 0.0);
458 for (std::size_t col = 0; col < n; ++col) {
459 std::size_t piv = col;
460 double bv = std::abs(A[col][col]);
461 for (std::size_t r = col + 1; r < n; ++r) {
462 if (std::abs(A[r][col]) > bv) { bv = std::abs(A[r][col]); piv = r; }
464 if (piv != col) { std::swap(A[piv], A[col]); std::swap(x[piv], x[col]); }
465 if (std::abs(A[col][col]) == 0.0)
467 for (std::size_t r = col + 1; r < n; ++r) {
468 const std::complex<double> f = A[r][col] / A[col][col];
469 for (std::size_t c = col; c < n; ++c) A[r][c] -= f * A[col][c];
473 for (std::size_t row = n; row-- > 0;) {
474 std::complex<double> acc = x[row];
475 for (std::size_t c = row + 1; c < n; ++c) acc -= A[row][c] * x[c];
476 x[row] = acc / A[row][row];
478 std::complex<double> out(0.0, 0.0);
479 for (std::size_t i = 0; i < n; ++i) out += num_traits<T>::to_double(pie[i]) * x[i];
485 const std::size_t n_grid = 2400;
487 double hi = mean * 60.0;
489 const double var = m2 - mean * mean;
490 if (std::isfinite(var) && var > 0.0) hi = std::max(hi, mean + 12.0 * std::sqrt(var));
491 if (!std::isfinite(hi) || hi <= 0.0)
return std::complex<double>(1.0, 0.0);
492 const double step = hi /
static_cast<double>(n_grid);
493 std::complex<double> acc(0.0, 0.0);
496 for (std::size_t i = 0; i < n_grid; ++i) {
497 const double right =
static_cast<double>(i + 1) * step;
499 const double w = cur - prev;
501 if (w == 0.0)
continue;
503 acc += w * std::exp(-s * ((
static_cast<double>(i) + 0.5) * step));
505 return mass > 0.0 ? acc / mass : std::complex<double>(1.0, 0.0);
518 if (k == 0)
return one;
526 for (
unsigned i = 0; i < k; ++i) v = T(v * d.
mean);
532 T pa = one, pb = one;
533 for (
unsigned i = 0; i <= k; ++i) {
544 throw NumericError(
ErrorKind{
"Overflow"},
"dist_moment: the Pareto moment of this order is infinite");
546 for (
unsigned i = 0; i < k; ++i) ps = T(ps * scale);
547 return T(alpha * ps / (alpha - m));
552 for (
const T& x : d.
trace) {
554 for (
unsigned i = 0; i < k; ++i) v = T(v * x);
562 "dist_moment: the moments of a Gamma are values of the gamma function, which "
563 "exact arithmetic has no representation for");
568 std::pow(scale,
static_cast<double>(k)));
574 "dist_moment: the moments of a Weibull are values of the gamma function, "
575 "which exact arithmetic has no representation for");
580 std::tgamma(1.0 + k / r));
586 "dist_moment: the moments of a Lognormal are values of exp, which exact "
587 "arithmetic has no representation for");
599 T prev2 = one, prev1 = mu;
600 if (k == 1)
return prev1;
601 for (
unsigned i = 2; i <= k; ++i) {
602 const T next = T(mu * prev1 +
644 "dist_cdf: a Prior's law is the mixture over its alternatives, which lives with the "
645 "Prior; call prior_cdf (lang/prior.h)");
652 return x < d.
params[0] ? zero : one;
655 if (x <= a)
return zero;
656 if (x >= b)
return one;
657 return T((x - a) / (b - a));
662 "dist_cdf: the Gaussian CDF is a value of erf, which exact arithmetic has no "
663 "representation for");
670 (sg * std::sqrt(2.0)))));
675 if (d.
trace.empty())
throw InputError(
"dist_cdf: the Replayer carries no samples");
676 std::size_t below = 0;
677 for (
const T& v : d.
trace)
678 if (!(v > x)) ++below;
687 "dist_cdf: the law of this family is an exponential, which exact arithmetic has no "
688 "representation for; use the double or real backend");
690 if (!(x > zero))
return zero;
701 const double z = alpha * xv;
702 double term = std::exp(-z), acc = term;
703 for (
long j = 1; j < r; ++j) {
704 term *= z /
static_cast<double>(j);
714 (1.0 - p) * (1.0 - std::exp(-m2 * xv)));
719 if (xv <= scale)
return zero;
736 0.5 * std::erfc(-(std::log(xv) - mu) / (sg * std::sqrt(2.0))));
745 for (std::size_t i = 0; i < A.
rows(); ++i)
746 for (std::size_t j = 0; j < A.
cols(); ++j) A(i, j) = T(m.
D0(i, j) * x);
749 for (std::size_t i = 0; i < E.
rows(); ++i)
750 for (std::size_t j = 0; j < E.
cols(); ++j) acc += T(pie[i] * E(i, j));
769 if (!(p > zero) || !(p < one))
770 throw InputError(
"dist_quantile: p must lie strictly between 0 and 1");
775 const int max_expand = 200;
784 lo = T(d.
mean - step);
785 hi = T(d.
mean + step);
787 for (; j < max_expand; ++j) {
789 if (low_ok && high_ok)
break;
790 step = T(step * two);
791 if (!low_ok) lo = T(d.
mean - step);
792 if (!high_ok) hi = T(d.
mean + step);
794 if (j == max_expand)
throw NumericError(
ErrorKind{
"NonConvergence"},
"dist_quantile: failed to bracket the quantile");
797 for (; i < max_expand; ++i) {
801 if (i == max_expand)
throw NumericError(
ErrorKind{
"NonConvergence"},
"dist_quantile: failed to bracket the quantile");
804 for (
int k = 0; k < 200; ++k) {
805 const T mid = T((lo + hi) / two);
810 const T scale = hi > one ? hi : one;
811 if (T(hi - lo) <= T(tol * scale))
break;
813 return T((lo + hi) / two);
835 if (d.
trace.size() < 2)
836 throw InputError(
"dist_is_nhpp: the trace needs at least two inter-arrival times to test");
837 std::vector<T> epochs;
838 epochs.reserve(d.
trace.size());
840 for (std::size_t i = 0; i < d.
trace.size(); ++i) {
841 acc = T(acc + d.
trace[i]);
842 epochs.push_back(acc);
Minimal-order acyclic phase-type fit of the first three moments (matlab/lib/kpctoolbox/aph/aph_fit....
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
Kolmogorov-Smirnov tests for a non-homogeneous Poisson arrival process.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Transient distribution of a level-independent-in-the-tail QBD by an adaptive Taylor series (libQBD QB...
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
NhppKsResult< Tv > infer_nhpp_ks(const std::vector< Tv > ×, const Tv &T, const std::function< Tv(const Tv &)> &cumRate=std::function< Tv(const Tv &)>(), NhppKsMethod method=NhppKsMethod::Lewis, const Tv &T0=num_traits< Tv >::from_int(0))
Kolmogorov-Smirnov tests for a non-homogeneous Poisson arrival process.
mam::Map< T > dist_to_map(const Distrib< T > &d)
The (D0,D1) pair that reaches sn.proc.
T dist_quantile(const Distrib< T > &d, const T &p)
The p-quantile, by bisection on dist_cdf.
infer::NhppKsResult< T > dist_is_nhpp(const Distrib< T > &d)
Port of Replayer.isNHPP: test whether a trace is a sample path of a NON-HOMOGENEOUS POISSON process,...
T dist_lst(const Distrib< T > &d, const T &s)
sn.lst: the Laplace-Stieltjes transform E[exp(-sX)].
unsigned convert_to_map_phases(double scv)
The number of Erlang phases convertToMAP picks for a non-Markovian distribution: 20 when the SCV is b...
T dist_cdf(const Distrib< T > &d, const T &x)
F(x) = P{X <= x}, MATLAB's Distribution.evalCDF.
std::vector< T > dist_pie(const Distrib< T > &d)
sn.pie: the phase distribution seen by an arriving job.
@ NORMAL
A Gaussian, and the ONE family whose value is not MATLAB's, because MATLAB has none to copy: ProcessT...
void dmap_refresh_moments(Distrib< T > &d)
The first two moments of a DISCRETE-time MAP, from its own law.
bool process_is_markovian(ProcessType p)
ProcessType.isMarkovian: true when sn.proc carries an exact matrix representation of the law,...
void dist_refresh_moments(Distrib< T > &d)
Fill in the first two moments of a distribution given by its matrices.
void reject_prior(const char *who)
The refusal every lowering of a Prior shares.
T dist_moment(const Distrib< T > &d, unsigned k)
The k-th raw moment.
double gammainc_lower(double a, double x)
Regularized lower incomplete gamma P(a, x), MATLAB's gammainc(x, a, 'lower').
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
AphFitResult< T > aph_fit(const T &e1, const T &e2, const T &e3, unsigned nmax, const T &tol)
Fit an APH(n) with n <= nmax to the raw moments e1, e2, e3.
Map< T > map_erlang(const T &mean, unsigned k)
Erlang-k renewal MAP with the given mean (map_erlang.m).
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
T map_scv(const Map< T > &m)
Squared coefficient of variation.
T map_moment(const Map< T > &m, unsigned k)
Raw moment of order k of the inter-arrival time: k!
Conservation laws of a layered queueing network, enumerated from its structure.
Matrix< T > expm(const Matrix< T > &A)
Matrix exponential exp(A).
Number-type abstraction for the templated API port.
Adaptive quadrature for the qsys functions whose MATLAB originals call integral(),...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
bool has_map() const
True when the type carries a (D0,D1) pair of its own.
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
std::vector< T > params
Constructor arguments, in MATLAB getParam order.
std::vector< T > trace
Replayer / Trace samples; empty for every other type.
static constexpr double FineTol
static constexpr double CoarseTol
A MAP as the pair of matrices (D0, D1).