5#ifndef LINE_LANG_NNPP_H
6#define LINE_LANG_NNPP_H
71 const std::vector<double>&
omega,
const std::vector<double>&
phi,
74 if (
alpha.empty())
throw InputError(
"NNPP.eptmp: alpha must hold at least one coefficient");
76 throw InputError(
"NNPP.eptmp: gamma, omega and phi must have the same length");
78 throw InputError(
"NNPP.eptmp: the horizon must be finite and positive");
79 for (
double v :
alpha)
80 if (!std::isfinite(v))
throw InputError(
"NNPP.eptmp: alpha must be finite");
81 for (std::size_t k = 0; k <
gamma.size(); ++k)
82 if (!std::isfinite(
gamma[k]) || !std::isfinite(
omega[k]) || !std::isfinite(
phi[k]))
83 throw InputError(
"NNPP.eptmp: gamma, omega and phi must be finite");
97 double dispersion = 1.0,
bool cyclic =
true,
int Q = 200) {
99 throw InputError(
"NNPP.piecewiseLinear: times and rates must have the same length, at "
101 if (
times[0] != 0.0)
throw InputError(
"NNPP.piecewiseLinear: times[0] must be 0");
102 for (std::size_t j = 0; j + 1 <
times.size(); ++j)
104 throw InputError(
"NNPP.piecewiseLinear: times must be strictly increasing");
105 if (!std::isfinite(
times.back()))
106 throw InputError(
"NNPP.piecewiseLinear: times must be finite");
107 for (
double r :
rates)
108 if (!(r >= 0.0) || !std::isfinite(r))
109 throw InputError(
"NNPP.piecewiseLinear: rates must be finite and non-negative");
114 n.horizon_ =
times.back();
126 if (
params.size() < 2)
throw InputError(
"NNPP: malformed EPTMP params row");
127 const std::size_t m1 =
static_cast<std::size_t
>(
params[0]);
128 const std::size_t p =
static_cast<std::size_t
>(
params[1]);
129 if (
params.size() != 2 + m1 + 3 * p)
throw InputError(
"NNPP: malformed EPTMP params row");
130 std::vector<double> a(
params.begin() + 2,
params.begin() + 2 + m1);
131 std::vector<double> g(
params.begin() + 2 + m1,
params.begin() + 2 + m1 + p);
132 std::vector<double> w(
params.begin() + 2 + m1 + p,
params.begin() + 2 + m1 + 2 * p);
133 std::vector<double> f(
params.begin() + 2 + m1 + 2 * p,
params.end());
137 if (
params.empty())
throw InputError(
"NNPP: malformed PWLINEAR params row");
138 const std::size_t n =
static_cast<std::size_t
>(
params[0]);
139 if (
params.size() != 1 + 2 * n)
throw InputError(
"NNPP: malformed PWLINEAR params row");
140 std::vector<double> t(
params.begin() + 1,
params.begin() + 1 + n);
141 std::vector<double> r(
params.begin() + 1 + n,
params.end());
144 throw InputError(
"NNPP: unknown rate-function kind " + std::to_string(
kind));
153 if (t < 0.0)
return 0.0;
154 if (cyclic_)
return lam(reduce(t));
155 if (t > horizon_)
return 0.0;
161 if (!(t > 0.0))
return 0.0;
163 const double k = std::floor(t / horizon_);
164 const double r = t - k * horizon_;
165 return k * mu_s_ + mu_within(r);
167 if (t >= horizon_)
return mu_s_;
173 return mu_s_ > 0.0 ? horizon_ / mu_s_ : std::numeric_limits<double>::infinity();
175 double rate()
const {
return mu_s_ / horizon_; }
176 double scv()
const {
return std::numeric_limits<double>::quiet_NaN(); }
177 double skewness()
const {
return std::numeric_limits<double>::quiet_NaN(); }
187 if (!(t > 0.0))
return 0.0;
190 k = std::floor(t / horizon_);
191 t = t - k * horizon_;
192 }
else if (t >= horizon_) {
195 std::size_t i =
static_cast<std::size_t
>(std::floor(t / h_));
196 if (i >= lt_.size()) i = lt_.size() - 1;
197 return k * M_.back() + M_[i] + lt_[i] * (t - z_[i]);
205 int q()
const {
return static_cast<int>(lt_.size()); }
206 int kind()
const {
return kind_; }
208 std::string
method()
const {
return disp_ == 1.0 ?
"thinning" :
"sciata"; }
209 std::string
name()
const {
return "NNPP"; }
211 const std::vector<double>&
alpha()
const {
return alpha_; }
212 const std::vector<double>&
gamma()
const {
return gamma_; }
213 const std::vector<double>&
omega()
const {
return omega_; }
214 const std::vector<double>&
phi()
const {
return phi_; }
215 const std::vector<double>&
times()
const {
return times_; }
216 const std::vector<double>&
rates()
const {
return rates_; }
220 std::vector<double> p;
221 if (kind_ ==
EPTMP) {
222 p.push_back(
static_cast<double>(alpha_.size()));
223 p.push_back(
static_cast<double>(gamma_.size()));
224 p.insert(p.end(), alpha_.begin(), alpha_.end());
225 p.insert(p.end(), gamma_.begin(), gamma_.end());
226 p.insert(p.end(), omega_.begin(), omega_.end());
227 p.insert(p.end(), phi_.begin(), phi_.end());
229 p.push_back(
static_cast<double>(times_.size()));
230 p.insert(p.end(), times_.begin(), times_.end());
231 p.insert(p.end(), rates_.begin(), rates_.end());
241 if (!(C > 0.0) || !std::isfinite(C))
242 throw InputError(
"NNPP: the dispersion ratio must be finite and positive");
248 auto g = [C](
double x) {
249 const double a = std::exp(x);
250 return std::exp(std::lgamma(1.0 + 2.0 / a) - 2.0 * std::lgamma(1.0 + 1.0 / a)) - 1.0 - C;
252 double lo = std::log(0.02), hi = std::log(200.0);
253 const double glo = g(lo), ghi = g(hi);
254 if (!(glo > 0.0 && ghi < 0.0))
255 throw InputError(
"NNPP: no Weibull shape in [0.02, 200] gives dispersion " +
257 for (
int it = 0; it < 200; ++it) {
258 const double mid = 0.5 * (lo + hi);
264 shape = std::exp(0.5 * (lo + hi));
265 scale = std::exp(-std::lgamma(1.0 + 1.0 / shape));
274 std::size_t i =
static_cast<std::size_t
>(std::floor(tc / h_));
275 if (i >= lt_.size()) i = lt_.size() - 1;
281 const double MQ = M_.back();
282 if (!(MQ > 0.0))
return std::numeric_limits<double>::infinity();
285 k = std::floor(u / MQ);
288 return std::numeric_limits<double>::infinity();
290 const std::size_t Q = lt_.size();
292 std::size_t i =
static_cast<std::size_t
>(std::lower_bound(M_.begin() + 1, M_.end(), u) -
295 while (i < Q && !(lt_[i - 1] > 0.0)) ++i;
296 while (i > 1 && !(lt_[i - 1] > 0.0)) --i;
297 const double t = z_[i - 1] + (u - M_[i - 1]) / lt_[i - 1];
298 return k * horizon_ + t;
302 template <
class Unif>
304 const double u = unif();
305 if (wshape_ == 1.0)
return -std::log1p(-u);
307 return wscale_ * std::pow(y, 1.0 / wshape_);
311 template <
class Unif>
313 const double u = unif();
314 const double e = -std::log1p(-u);
315 if (wshape_ == 1.0)
return e;
316 return wscale_ * std::pow(e, 1.0 / wshape_);
323 template <
class Unif>
325 if (!(M_.back() > 0.0))
return std::numeric_limits<double>::infinity();
330 if (!cyclic_ && s.
u > M_.back())
return std::numeric_limits<double>::infinity();
332 const double tc = cyclic_ ? reduce(t) : std::min(t, horizon_);
334 double p = (maj > 0.0) ? lam(tc) / maj : 0.0;
335 if (p < 0.0) p = 0.0;
336 if (p > 1.0) p = 1.0;
348 template <
class Unif>
349 std::vector<double>
sample(std::size_t n, Unif&& unif)
const {
350 std::vector<double> out;
354 for (std::size_t j = 0; j < n; ++j) {
356 if (!std::isfinite(t))
break;
357 out.push_back(t - prev);
364 std::vector<double>
sample(std::size_t n, std::mt19937_64&
rng)
const {
365 std::uniform_real_distribution<double> U(0.0, 1.0);
368 while (!(u > 0.0)) u = U(
rng);
375 void init(
double dispersion,
bool cyclic,
int Q) {
376 if (Q < 1)
throw InputError(
"NNPP: the majorizer needs Q >= 1 subintervals");
379 std::cerr <<
"[LINE] Warning: NNPP: dispersion " <<
dispersion
380 <<
" exceeds 2; SCIATA is designed for C in (0, 2]." << std::endl;
383 const double S = horizon_;
385 z_.assign(
static_cast<std::size_t
>(Q) + 1, 0.0);
386 for (
int i = 0; i <= Q; ++i) z_[static_cast<std::size_t>(i)] = i * S / Q;
387 lt_.assign(
static_cast<std::size_t
>(Q), 0.0);
388 for (
int i = 1; i <= Q; ++i) {
389 const double a = z_[
static_cast<std::size_t
>(i - 1)], b = z_[
static_cast<std::size_t
>(i)];
390 lt_[
static_cast<std::size_t
>(i - 1)] = (kind_ ==
EPTMP) ? golden_max(a, b) : pwl_max(a, b);
392 M_.assign(
static_cast<std::size_t
>(Q) + 1, 0.0);
393 for (
int i = 1; i <= Q; ++i) {
394 const std::size_t ii =
static_cast<std::size_t
>(i);
395 M_[ii] = M_[ii - 1] + lt_[ii - 1] * (z_[ii] - z_[ii - 1]);
397 mu_s_ = (kind_ ==
EPTMP) ? simpson(S, 64 *
static_cast<long>(Q)) : trapezoid(S);
401 double reduce(
double t)
const {
402 const double k = std::floor(t / horizon_);
403 double r = t - k * horizon_;
404 if (r < 0.0) r = 0.0;
405 if (r >= horizon_) r = 0.0;
410 double lam(
double t)
const {
411 if (kind_ ==
EPTMP) {
412 const std::size_t m = alpha_.size() - 1;
413 double s = alpha_[m];
414 for (std::size_t i = m; i-- > 0;) s = s * t + alpha_[i];
415 for (std::size_t k = 0; k < gamma_.size(); ++k)
416 s += gamma_[k] * std::sin(omega_[k] * t + phi_[k]);
419 const std::size_t n = times_.size();
421 std::size_t j =
static_cast<std::size_t
>(
422 std::upper_bound(times_.begin(), times_.begin() +
static_cast<long>(n - 1), t) -
424 j = (j == 0) ? 0 : j - 1;
425 return rates_[j] + (rates_[j + 1] - rates_[j]) * (t - times_[j]) / (times_[j + 1] - times_[j]);
428 double golden_max(
double a,
double b)
const {
429 const double phi = (1.0 + std::sqrt(5.0)) / 2.0;
430 const double delta = 1e-4;
431 double best = std::max(lam(a), lam(b));
432 double x1 = a + (2.0 -
phi) * (b - a);
433 double x2 = a + (
phi - 1.0) * (b - a);
434 double f1 = lam(x1), f2 = lam(x2);
435 best = std::max(best, std::max(f1, f2));
436 while ((b - a) >= delta) {
441 x2 = a + (
phi - 1.0) * (b - a);
443 best = std::max(best, f2);
448 x1 = a + (2.0 -
phi) * (b - a);
450 best = std::max(best, f1);
456 double pwl_max(
double a,
double b)
const {
457 double best = std::max(lam(a), lam(b));
458 for (std::size_t j = 0; j < times_.size(); ++j)
459 if (a < times_[j] && times_[j] < b) best = std::max(best, rates_[j]);
464 double simpson(
double t,
long N)
const {
465 const double h = t /
static_cast<double>(N);
466 double s = lam(0.0) + lam(t);
467 for (
long i = 1; i < N; ++i) s += ((i % 2 == 1) ? 4.0 : 2.0) * lam(
static_cast<double>(i) * h);
472 double trapezoid(
double t)
const {
474 for (std::size_t j = 0; j + 1 < times_.size(); ++j) {
475 const double a = times_[j];
477 const double b = std::min(t, times_[j + 1]);
478 const double fb = (b == times_[j + 1]) ? rates_[j + 1] : lam(b);
479 s += 0.5 * (rates_[j] + fb) * (b - a);
485 double mu_within(
double t)
const {
486 if (!(t > 0.0))
return 0.0;
487 if (kind_ ==
PWLINEAR)
return trapezoid(t);
488 if (t == horizon_)
return mu_s_;
489 const long Q =
static_cast<long>(lt_.size());
490 long N = 2 *
static_cast<long>(std::ceil(32.0 * Q * t / horizon_));
492 return simpson(t, N);
496 std::vector<double> alpha_, gamma_, omega_, phi_;
497 std::vector<double> times_, rates_;
498 double horizon_ = 1.0;
501 double wshape_ = 1.0, wscale_ = 1.0;
503 std::vector<double> z_, lt_, M_;
double renewal_gap(Unif &unif) const
A noninitial renewal gap: scale * (-log(1-U))^(1/shape).
const std::vector< double > & alpha() const
const std::vector< double > & rates() const
Kind
The rate-function family, with the codes sn.proc carries.
double majorizer_inverse(double u) const
muTilde^{-1}(u), absolute time, cyclic aware; +Inf when the majorizer has no mass.
double majorizer_at(double tc) const
Majorizer rate in force at an in-cycle time tc.
const std::vector< double > & majorizer_breakpoints() const
The Q+1 majorizer breakpoints z_i = i*S/Q.
std::vector< double > sample(std::size_t n, std::mt19937_64 &rng) const
sample(n) off a std::mt19937_64, U drawn in (0,1).
double time_average_rate() const
std::string kind_name() const
const std::vector< double > & majorizer_rates() const
The Q piecewise-constant majorizer rates.
const std::vector< double > & omega() const
std::string method() const
static void weibull_for_dispersion(double C, double &shape, double &scale)
Weibull (shape, scale) whose mean is 1 and SCV is C: 200 bisection steps on x = log(shape) in [log 0....
double weibull_shape() const
static Nnpp eptmp(const std::vector< double > &alpha, const std::vector< double > &gamma, const std::vector< double > &omega, const std::vector< double > &phi, double horizon, double dispersion=1.0, bool cyclic=true, int Q=200)
EPTMP: lambda(t) = exp(sum_i alpha_i t^i + sum_k gamma_k sin(omega_k t + phi_k)) on [0,...
const std::vector< double > & gamma() const
std::vector< double > params() const
The params row of sn.proc, see from_params.
static Nnpp from_params(int kind, const std::vector< double > ¶ms, double horizon, double dispersion, bool cyclic, int Q)
Rebuild from the params row of sn.proc ([m+1, p, alpha, gamma, omega, phi] for EPTMP,...
double mean_value_majorizer() const
muTilde(S), the majorizer mass over one cycle.
static Nnpp piecewise_linear(const std::vector< double > ×, const std::vector< double > &rates, double dispersion=1.0, bool cyclic=true, int Q=200)
PWLINEAR: lambda linearly interpolated between (times[j], rates[j]); horizon = times....
double mean_value(double t) const
mu(t) = int_0^t lambda, cyclic aware; constant past the horizon when not cyclic.
double acceptance_rate() const
const std::vector< double > & phi() const
const std::vector< double > & times() const
double weibull_scale() const
double rate_at(double t) const
lambda(t), cyclic aware; zero past the horizon when not cyclic.
double next_arrival(Stream &s, Unif &unif) const
The next accepted arrival epoch (absolute time) of the path s, or +Inf when a non-cyclic process has ...
double equilibrium_gap(Unif &unif) const
The equilibrium (first) renewal gap: scale * Y^(1/shape), Y ~ Gamma(1/shape, 1).
double mean_value_majorizer(double t) const
muTilde(t), cyclic aware.
std::vector< double > sample(std::size_t n, Unif &&unif) const
n successive interarrival times along one path from time 0.
double dispersion() const
static VerboseLevel get_verbose()
The session verbosity.
The exception types the port throws.
The variate layer of the Java LDES engine, reproduced: SSJ's randvar generators as inverse-CDF functi...
Running progress log of a LINE solver run (the "solver console").
double gamma_inverse(double alpha, double lambda, double u)
GammaDist.inverseF(alpha, lambda, u): the quantile of a Gamma of shape alpha and RATE lambda,...
Conservation laws of a layered queueing network, enumerated from its structure.
One arrival path: renewal epoch in majorizer space, first-gap flag, last arrival.