5#ifndef LINE_API_SN_SN_NONMARKOV_TOPH_H
6#define LINE_API_SN_SN_NONMARKOV_TOPH_H
117 case ProcessType::EXP:
118 case ProcessType::ERLANG:
119 case ProcessType::HYPEREXP:
120 case ProcessType::PH:
121 case ProcessType::APH:
122 case ProcessType::MAP:
123 case ProcessType::DMAP:
124 case ProcessType::MMAP:
125 case ProcessType::BMAP:
126 case ProcessType::ME:
127 case ProcessType::RAP:
128 case ProcessType::COXIAN:
129 case ProcessType::COX2:
130 case ProcessType::MMPP2:
131 case ProcessType::IMMEDIATE:
132 case ProcessType::DISABLED:
147 return p == ProcessType::NHPP || p == ProcessType::MAPT || p == ProcessType::PHT;
155std::function<double(
double)> nonmkv_density(
const lang::Distrib<T>& d) {
157 std::vector<double> p;
158 for (std::size_t i = 0; i < d.params.size(); ++i)
159 p.push_back(num_traits<T>::to_double(d.params[i]));
161 case ProcessType::GAMMA: {
162 if (p.size() < 2)
break;
163 const double k = p[0], th = p[1];
164 return [k, th](
double x) {
165 if (!(x > 0.0))
return 0.0;
166 return std::exp((k - 1.0) * std::log(x) - x / th - k * std::log(th) -
170 case ProcessType::WEIBULL: {
171 if (p.size() < 2)
break;
172 const double a = p[0], r = p[1];
173 return [a, r](
double x) {
174 if (!(x > 0.0))
return 0.0;
175 return (r / a) * std::pow(x / a, r - 1.0) * std::exp(-std::pow(x / a, r));
178 case ProcessType::LOGNORMAL: {
179 if (p.size() < 2)
break;
180 const double mu = p[0], sg = p[1];
181 return [mu, sg](
double x) {
182 if (!(x > 0.0))
return 0.0;
183 const double z = (std::log(x) - mu) / sg;
184 return std::exp(-0.5 * z * z) / (x * sg * std::sqrt(2.0 * 3.14159265358979323846));
187 case ProcessType::PARETO: {
188 if (p.size() < 2)
break;
189 const double al = p[0], k = p[1];
190 return [al, k](
double x) {
191 if (!(x >= k) || !(x > 0.0))
return 0.0;
192 return al * std::pow(k, al) / std::pow(x, al + 1.0);
195 case ProcessType::UNIFORM: {
196 if (p.size() < 2)
break;
197 const double a = p[0], b = p[1];
198 return [a, b](
double x) {
return (x >= a && x <= b) ? 1.0 / (b - a) : 0.0; };
203 return std::function<double(double)>();
216std::function<double(
double)> nonmkv_ccdf(
const lang::Distrib<T>& d) {
218 if (d.type != ProcessType::GAMMA && d.type != ProcessType::WEIBULL &&
219 d.type != ProcessType::LOGNORMAL && d.type != ProcessType::PARETO)
220 return std::function<double(double)>();
221 const lang::Distrib<T> law = d;
222 return [law](
double x) {
223 if (!(x > 0.0))
return 1.0;
224 return 1.0 - num_traits<T>::to_double(
lang::dist_cdf(law, num_traits<T>::from_double(x)));
238mam::Map<T> nonmkv_longtail(
const std::function<
double(
double)>& ccdf,
double mean,
bool& ok) {
243 mam::HyperexpLongtailResult<double> fit;
246 }
catch (
const std::exception&) {
249 const std::size_t n = fit.p.size();
250 if (n == 0)
return out;
251 Matrix<T> D0(n, n, num_traits<T>::from_int(0));
252 Matrix<T> D1(n, n, num_traits<T>::from_int(0));
253 for (std::size_t i = 0; i < n; ++i) {
254 if (!std::isfinite(fit.lambda[i]) || fit.lambda[i] <= 0.0)
return out;
255 D0(i, i) = num_traits<T>::from_double(-fit.lambda[i]);
256 for (std::size_t j = 0; j < n; ++j)
257 D1(i, j) = num_traits<T>::from_double(fit.lambda[i] * fit.p[j]);
272mam::Map<T> nonmkv_concentrated(
double mean,
double scv, std::size_t order,
PhFit phfit) {
276 const double s = scv > 1e-12 ? scv : 1e-12;
279 return mam::map_erlang(num_traits<T>::from_double(mean),
static_cast<unsigned>(order));
284void nonmkv_install(lang::Distrib<T>& d,
const mam::Map<T>& m) {
285 std::vector<Matrix<T>> blocks;
286 blocks.push_back(m.D0);
287 blocks.push_back(m.D1);
292 std::shared_ptr<lang::Distrib<T>> declared(
new lang::Distrib<T>(d));
293 declared->declared.reset();
300 d.declared = declared;
318bool nonmkv_firing_ready(
const qn::TransitionParam<T>& np, std::size_t m) {
319 if (m >= np.firingphases.size() || np.firingphases[m] <= 0)
return false;
320 if (m >= np.firingproc.size())
return false;
321 return np.firingproc[m].D0.rows() ==
static_cast<std::size_t
>(np.firingphases[m]);
336 for (std::size_t ist = 0; ist <
sn.nstations; ++ist)
337 for (std::size_t r = 0; r <
sn.nclasses; ++r) {
338 if (ist <
sn.disabled.size() && r <
sn.disabled[ist].size() &&
sn.disabled[ist][r])
342 if (detail::nonmkv_is_markovian(d.
type) || detail::nonmkv_is_scheduled(d.
type))
344 if (d.
type == ProcessType::DET && preserve_det)
continue;
348 sn.transparam.begin();
349 it !=
sn.transparam.end(); ++it) {
351 for (std::size_t m = 0; m < np.
firingproc.size(); ++m) {
352 if (detail::nonmkv_firing_ready(np, m))
continue;
355 if (detail::nonmkv_is_markovian(d.
type) || detail::nonmkv_is_scheduled(d.
type))
357 if (d.
type == ProcessType::DET && preserve_det)
continue;
374 "sn_nonmarkov_toph evaluates densities and fits moments");
376 if (!opts.enabled)
return;
377 if (opts.order == 0)
throw InputError(
"sn_nonmarkov_toph: the phase budget must be positive");
379 for (std::size_t ist = 0; ist <
sn.nstations; ++ist) {
380 for (std::size_t r = 0; r <
sn.nclasses; ++r) {
381 if (ist <
sn.disabled.size() && r <
sn.disabled[ist].size() &&
sn.disabled[ist][r])
385 const ProcessType p = d.
type;
386 if (detail::nonmkv_is_markovian(p) || detail::nonmkv_is_scheduled(p))
continue;
387 if (p == ProcessType::DET && opts.preserve_det)
continue;
391 if (!std::isfinite(mean) || mean <= 0.0)
continue;
393 if (p == ProcessType::DET) {
394 detail::nonmkv_install(
396 static_cast<unsigned>(opts.order)));
405 const std::function<double(
double)> ccdf = detail::nonmkv_ccdf(d);
408 const mam::Map<T> lt = detail::nonmkv_longtail<T>(ccdf, mean, ok);
410 detail::nonmkv_install(d, lt);
416 const std::function<double(
double)> pdf = detail::nonmkv_density(d);
418 detail::nonmkv_install(d,
419 detail::nonmkv_concentrated<T>(mean, scv, opts.order,
423 if (opts.phfit ==
PhFit::Cme && scv >= 0.0 && scv < 1.0) {
424 detail::nonmkv_install(d,
425 detail::nonmkv_concentrated<T>(mean, scv, opts.order,
432 detail::nonmkv_install(d, fit);
437 for (std::size_t ind = 0; ind <
sn.nodes.size(); ++ind) {
438 if (
sn.nodes[ind].nodetype != qn::NodeType::Transition)
continue;
439 typename std::map<std::size_t, qn::TransitionParam<T>>::iterator it =
440 sn.transparam.find(ind + 1);
441 if (it ==
sn.transparam.end())
continue;
443 for (std::size_t m = 0; m < np->
firingproc.size(); ++m) {
447 if (detail::nonmkv_firing_ready(*np, m))
continue;
450 const ProcessType p = d.
type;
451 if (detail::nonmkv_is_markovian(p) || detail::nonmkv_is_scheduled(p))
continue;
452 if (p == ProcessType::DET && opts.preserve_det)
continue;
457 if (p == ProcessType::DET || !std::isfinite(mean) || mean <= 0.0) {
460 const double mu = (std::isfinite(mean) && mean > 0.0) ? mean : 1.0;
462 static_cast<unsigned>(opts.order));
464 const std::function<double(
double)> pdf = detail::nonmkv_density(d);
465 if (!pdf || (opts.phfit ==
PhFit::Cme && scv >= 0.0 && scv < 1.0))
466 fit = detail::nonmkv_concentrated<T>(mean, scv, opts.order, opts.phfit);
472 detail::nonmkv_install(d, fit);
A network plus its refreshed NetworkStruct.
Concentrated matrix exponentials, and the two-moment fit built on them.
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Fitting a hyperexponential to a long-tail distribution.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Acyclic phase-type approximation of an arbitrary density by Bernstein exponentials.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
bool sn_has_nonmarkov(const qn::NetworkStruct< T > &sn, bool preserve_det=false)
Whether any law in the struct would be replaced, so a caller can skip copying the struct when there i...
PhFit
Which Markovian surrogate to fit; options.config.phfit.
@ Ph
Bernstein density fit: a genuine phase-type, shape-carrying.
@ Hyperexp
Mixture of exponentials fitted to the ccdf ITSELF across decades of time scale (hyperexp_fit_longtail...
@ Cme
concentrated ME plus exponential tail: two moments, exactly
bool sn_is_phasetype(const std::vector< Matrix< T > > &maps, const std::vector< T > &pie)
Whether a (D0, D1, ...) list is a valid phase-type / MAP representation.
void sn_nonmarkov_toph(qn::NetworkStruct< T > &sn, const NonmarkovOptions &opts=NonmarkovOptions())
Replace every non-Markovian service and firing law by a Markovian surrogate.
T dist_cdf(const Distrib< T > &d, const T &x)
F(x) = P{X <= x}, MATLAB's Distribution.evalCDF.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
void dist_refresh_moments(Distrib< T > &d)
Fill in the first two moments of a distribution given by its matrices.
Map< T > map_bernstein(const std::function< double(double)> &f, unsigned order=20)
Acyclic phase-type approximation of an arbitrary density by Bernstein exponentials.
HyperexpLongtailResult< T > hyperexp_fit_longtail(Ccdf &&ccdf, const T &b=num_traits< T >::from_rational(3, 2), const T &decade=num_traits< T >::from_int(4))
The fit with the component count chosen automatically: one per decade between the 0....
Map< T > dist_fit_me(double mean, double scv, std::size_t maxPhases=0)
Two-moment matrix-exponential fit for 0 < scv < 1, a port of dist_fit_me.m.
Map< T > map_erlang(const T &mean, unsigned k)
Erlang-k renewal MAP with the given mean (map_erlang.m).
Map< T > map_scale(const Map< T > &in, const T &new_mean)
Rescale time so that the mean inter-arrival time becomes new_mean.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Whether a (D0, D1, ...) list is a valid phase-type / MAP representation.
options.config.nonmkv and friends.
std::size_t order
nonmkvorder, the phase budget
PhFit phfit
which surrogate family
bool preserve_det
Leave Det alone for the exact MAP/D/c branch, options.config.preserveDet.
bool enabled
false is the reference's nonmkv = 'none'
static Distrib map_dist(const Matrix< T > &D0, const Matrix< T > &D1, ProcessType tag)
A MAP given by its two matrices; the moments are those of its stationary phase.
A MAP as the pair of matrices (D0, D1).
std::size_t order() const
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
std::vector< lang::Distrib< T > > firingproc
firing distribution per mode
std::vector< std::size_t > firingphases
phase count per mode, 0 when non-Markovian