5#ifndef LINE_API_MAM_ME_SAMPLE_H
6#define LINE_API_MAM_ME_SAMPLE_H
58namespace mesampledetail {
61const std::size_t grid_points = 1000;
63const int max_doublings = 40;
65const double tail_mass_tol = 1e-12;
67const int newton_steps = 3;
69const double taylor_theta = 0.5;
71const int taylor_max_steps = 64;
73const int taylor_max_terms = 30;
77std::vector<T> vec_times_mat(
const std::vector<T>& w,
const Matrix<T>& M) {
78 const std::size_t n = w.size();
79 std::vector<T> out(n, num_traits<T>::from_int(0));
80 for (std::size_t j = 0; j < n; ++j)
81 for (std::size_t i = 0; i < n; ++i) out[j] += w[i] * M(i, j);
93std::vector<T> expm_propagate(
const std::vector<T>& w,
const Matrix<T>& A,
double norm_a,
double d) {
94 const std::size_t n = w.size();
95 if (!(d > 0.0))
return w;
97 const double theta = norm_a * d;
98 if (theta > taylor_theta) {
99 steps =
static_cast<int>(std::ceil(theta / taylor_theta));
100 if (steps > taylor_max_steps)
101 return vec_times_mat(w,
expm(A, num_traits<T>::from_double(d)));
103 const double ds = d / steps;
104 std::vector<T> acc = w;
105 for (
int s = 0; s < steps; ++s) {
106 std::vector<T> term = acc, next = acc;
107 for (
int k = 1; k <= taylor_max_terms; ++k) {
108 term = vec_times_mat(term, A);
109 const T c = num_traits<T>::from_double(ds / k);
110 double max_term = 0.0, max_acc = 0.0;
111 for (std::size_t j = 0; j < n; ++j) {
112 term[j] = T(term[j] * c);
114 const double at = std::fabs(num_traits<T>::to_double(term[j]));
115 if (at > max_term) max_term = at;
116 const double an = std::fabs(num_traits<T>::to_double(next[j]));
117 if (an > max_acc) max_acc = an;
119 if (max_term <= 1e-18 * (max_acc > 1e-300 ? max_acc : 1e-300))
break;
134double dominant_rate(
const Matrix<T>& A,
double mean) {
135 double eta = -std::numeric_limits<double>::infinity();
136 const std::size_t n = A.rows();
137 Matrix<double> Ad(n, n);
138 for (std::size_t i = 0; i < n; ++i)
139 for (std::size_t j = 0; j < n; ++j) Ad(i, j) = num_traits<T>::to_double(A(i, j));
141 const std::vector<std::complex<double> > ev =
eig_values(Ad);
142 for (std::size_t i = 0; i < ev.size(); ++i)
143 if (ev[i].real() > eta) eta = ev[i].real();
144 }
catch (
const std::exception&) {
145 eta = -std::numeric_limits<double>::infinity();
147 if (!(eta < 0.0) || !std::isfinite(eta)) eta = (mean > 0.0) ? -1.0 / mean : -1.0;
163 MeSampler() : n_(0), exponential_(false), exp_rate_(0.0), h_(0.0), t_end_(0.0), s_end_(0.0),
164 eta_(-1.0), norm_a_(0.0) {}
169 "MeSampler inverts a matrix-exponential distribution function");
171 if (n_ == 0)
throw InputError(
"me_sample: the representation is empty");
173 std::vector<T> alpha = a0.empty() ?
map_pie(m) : a0;
174 if (alpha.size() != n_)
throw InputError(
"me_sample: the entry law has the wrong length");
178 for (std::size_t i = 0; i < n_; ++i) {
179 double abs_row = 0.0;
180 for (std::size_t j = 0; j < n_; ++j) {
181 row_sum_[i] += A_(i, j);
184 if (abs_row > norm_a_) norm_a_ = abs_row;
193 h_ = t_end_ = s_end_ = 0.0;
200 const double sigma = (var > 0.0) ? std::sqrt(var) : 0.0;
201 double horizon = mean + 10.0 * sigma;
202 if (!(horizon > 0.0) || !std::isfinite(horizon)) horizon = 1.0;
203 for (
int k = 0; k < mesampledetail::max_doublings; ++k) {
204 if (survival_by_expm(alpha, horizon) < mesampledetail::tail_mass_tol)
break;
208 const std::size_t g = mesampledetail::grid_points;
209 h_ = horizon /
static_cast<double>(g - 1);
219 F_[0] = clamp_unit(1.0 - sum_of(alpha));
220 for (std::size_t i = 1; i < g; ++i) {
221 w_[i] = mesampledetail::vec_times_mat(w_[i - 1], Eh);
222 t_[i] =
static_cast<double>(i) * h_;
225 double c = clamp_unit(1.0 - sum_of(w_[i]));
226 if (c < F_[i - 1]) c = F_[i - 1];
230 const double surv = 1.0 - F_[g - 1];
231 s_end_ = (surv > 0.0) ? surv : 0.0;
232 eta_ = mesampledetail::dominant_rate(A_, mean);
243 if (exponential_)
return -std::log(1.0 - u) / exp_rate_;
244 const std::size_t g = F_.size();
245 if (u <= F_[0])
return 0.0;
246 if (u >= F_[g - 1]) {
249 const double tail = 1.0 - u;
250 if (s_end_ <= 0.0 || tail <= 0.0 || !(eta_ < 0.0))
return t_end_;
251 const double x = t_end_ + std::log(s_end_ / tail) / (-eta_);
252 return (x > t_end_) ? x : t_end_;
254 std::size_t lo = 0, hi = g - 1;
255 while (hi - lo > 1) {
256 const std::size_t mid = (lo + hi) / 2;
262 const double den = F_[lo + 1] - F_[lo];
263 double x = (den > 0.0) ? t_[lo] + (u - F_[lo]) / den * h_ : t_[lo];
264 const double left = t_[lo], right = t_[lo] + h_;
266 for (
int k = 0; k < mesampledetail::newton_steps; ++k) {
267 const std::vector<T> w =
268 mesampledetail::expm_propagate(w_[lo], A_, norm_a_, x - left);
269 const double surv = sum_of(w);
271 for (std::size_t j = 0; j < n_; ++j)
273 if (!(f > 0.0))
break;
274 const double err = (1.0 - surv) - u;
275 if (std::fabs(err) < 1e-14)
break;
276 const double xn = x - err / f;
277 if (!(xn > left) || !(xn < right))
break;
278 const bool converged =
279 std::fabs(xn - x) <= 1e-15 * (std::fabs(x) > 1.0 ? std::fabs(x) : 1.0);
281 if (converged)
break;
287 double survival_by_expm(
const std::vector<T>& alpha,
double t)
const {
288 const std::vector<T> w =
290 const double s = sum_of(w);
291 return (s > 0.0) ? s : 0.0;
294 static double sum_of(
const std::vector<T>& v) {
300 static double clamp_unit(
double x) {
return x < 0.0 ? 0.0 : (x > 1.0 ? 1.0 : x); }
304 std::vector<T> row_sum_;
307 std::vector<double> t_;
308 std::vector<double> F_;
309 std::vector<std::vector<T> > w_;
331 const std::vector<T>& a0 = std::vector<T>()) {
335 for (std::size_t i = 0; i < n; ++i)
Stateful ME sampler holding the inversion table.
MeSampler(const Map< T > &m, const std::vector< T > &a0=std::vector< T >())
Build the table of m, whose D0 is the ME matrix and whose entry law is map_pie.
double next(pfqn::McRng &rng) const
One variate, consuming exactly one uniform.
double quantile(double u) const
The inverse CDF at u, exposed so a caller can supply its own uniform.
Eigenvalues and singular values, backed by LAPACK.
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
T map_var(const Map< T > &m)
Variance of the inter-arrival time.
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
std::vector< T > me_sample(const Map< T > &m, std::size_t n, pfqn::McRng &rng, const std::vector< T > &a0=std::vector< T >())
me_sample: n INDEPENDENT variates of the matrix exponential (D0, D1).
std::mt19937_64 McRng
The generator type every Monte Carlo entry point in this tree accepts.
double mc_uniform01(McRng &g)
Uniform deviate on [0,1) with 53 significant bits, as a double.
std::vector< std::complex< double > > eig_values(const Matrix< double > &A)
Eigenvalues of a general real square matrix, in LAPACK's order.
Matrix< T > expm(const Matrix< T > &A)
Matrix exponential exp(A).
Number-type abstraction for the templated API port.
Randomness scaffolding shared by the Monte Carlo normalizing-constant estimators (pfqn_mci,...
A MAP as the pair of matrices (D0, D1).
std::size_t order() const