5#ifndef LINE_API_SN_SN_NONMARKOV_TOPH_H
6#define LINE_API_SN_SN_NONMARKOV_TOPH_H
123 std::string m = method;
124 for (
char& c : m) c =
static_cast<char>(std::tolower(
static_cast<unsigned char>(c)));
125 if (m ==
"none")
return false;
126 if (m ==
"bernstein")
return true;
128 "options.config.nonmkv expects one of 'bernstein', 'none', got '" + method +
"'.");
137 case ProcessType::EXP:
138 case ProcessType::ERLANG:
139 case ProcessType::HYPEREXP:
140 case ProcessType::PH:
141 case ProcessType::APH:
142 case ProcessType::MAP:
143 case ProcessType::DMAP:
144 case ProcessType::MMAP:
147 case ProcessType::MPH:
148 case ProcessType::MMAPT:
149 case ProcessType::MPHT:
150 case ProcessType::BMMAPT:
151 case ProcessType::BMAP:
152 case ProcessType::ME:
153 case ProcessType::RAP:
154 case ProcessType::COXIAN:
155 case ProcessType::COX2:
156 case ProcessType::MMPP2:
157 case ProcessType::IMMEDIATE:
158 case ProcessType::DISABLED:
173 return p == ProcessType::NHPP || p == ProcessType::MAPT || p == ProcessType::PHT;
181std::function<double(
double)> nonmkv_density(
const lang::Distrib<T>& d) {
183 std::vector<double> p;
184 for (std::size_t i = 0; i < d.params.size(); ++i)
185 p.push_back(num_traits<T>::to_double(d.params[i]));
187 case ProcessType::GAMMA: {
188 if (p.size() < 2)
break;
189 const double k = p[0], th = p[1];
190 return [k, th](
double x) {
191 if (!(x > 0.0))
return 0.0;
192 return std::exp((k - 1.0) * std::log(x) - x / th - k * std::log(th) -
196 case ProcessType::WEIBULL: {
197 if (p.size() < 2)
break;
198 const double a = p[0], r = p[1];
199 return [a, r](
double x) {
200 if (!(x > 0.0))
return 0.0;
201 return (r / a) * std::pow(x / a, r - 1.0) * std::exp(-std::pow(x / a, r));
204 case ProcessType::LOGNORMAL: {
205 if (p.size() < 2)
break;
206 const double mu = p[0], sg = p[1];
207 return [mu, sg](
double x) {
208 if (!(x > 0.0))
return 0.0;
209 const double z = (std::log(x) - mu) / sg;
210 return std::exp(-0.5 * z * z) / (x * sg * std::sqrt(2.0 * 3.14159265358979323846));
213 case ProcessType::PARETO: {
214 if (p.size() < 2)
break;
215 const double al = p[0], k = p[1];
216 return [al, k](
double x) {
217 if (!(x >= k) || !(x > 0.0))
return 0.0;
218 return al * std::pow(k, al) / std::pow(x, al + 1.0);
221 case ProcessType::UNIFORM: {
222 if (p.size() < 2)
break;
223 const double a = p[0], b = p[1];
224 return [a, b](
double x) {
return (x >= a && x <= b) ? 1.0 / (b - a) : 0.0; };
229 return std::function<double(double)>();
242std::function<double(
double)> nonmkv_ccdf(
const lang::Distrib<T>& d) {
244 if (d.type != ProcessType::GAMMA && d.type != ProcessType::WEIBULL &&
245 d.type != ProcessType::LOGNORMAL && d.type != ProcessType::PARETO)
246 return std::function<double(double)>();
247 const lang::Distrib<T> law = d;
248 return [law](
double x) {
249 if (!(x > 0.0))
return 1.0;
250 return 1.0 - num_traits<T>::to_double(
lang::dist_cdf(law, num_traits<T>::from_double(x)));
264mam::Map<T> nonmkv_longtail(
const std::function<
double(
double)>& ccdf,
double mean,
bool& ok) {
269 mam::HyperexpLongtailResult<double> fit;
272 }
catch (
const std::exception&) {
275 const std::size_t n = fit.p.size();
276 if (n == 0)
return out;
277 Matrix<T> D0(n, n, num_traits<T>::from_int(0));
278 Matrix<T> D1(n, n, num_traits<T>::from_int(0));
279 for (std::size_t i = 0; i < n; ++i) {
280 if (!std::isfinite(fit.lambda[i]) || fit.lambda[i] <= 0.0)
return out;
281 D0(i, i) = num_traits<T>::from_double(-fit.lambda[i]);
282 for (std::size_t j = 0; j < n; ++j)
283 D1(i, j) = num_traits<T>::from_double(fit.lambda[i] * fit.p[j]);
298mam::Map<T> nonmkv_concentrated(
double mean,
double scv, std::size_t order,
PhFit phfit) {
302 const double s = scv > 1e-12 ? scv : 1e-12;
305 return mam::map_erlang(num_traits<T>::from_double(mean),
static_cast<unsigned>(order));
310void nonmkv_install(lang::Distrib<T>& d,
const mam::Map<T>& m) {
311 std::vector<Matrix<T>> blocks;
312 blocks.push_back(m.D0);
313 blocks.push_back(m.D1);
318 std::shared_ptr<lang::Distrib<T>> declared(
new lang::Distrib<T>(d));
319 declared->declared.reset();
326 d.declared = declared;
344bool nonmkv_firing_ready(
const qn::TransitionParam<T>& np, std::size_t m) {
345 if (m >= np.firingphases.size() || np.firingphases[m] <= 0)
return false;
346 if (m >= np.firingproc.size())
return false;
347 return np.firingproc[m].D0.rows() ==
static_cast<std::size_t
>(np.firingphases[m]);
362 for (std::size_t ist = 0; ist <
sn.nstations; ++ist)
363 for (std::size_t r = 0; r <
sn.nclasses; ++r) {
364 if (ist <
sn.disabled.size() && r <
sn.disabled[ist].size() &&
sn.disabled[ist][r])
368 if (detail::nonmkv_is_markovian(d.
type) || detail::nonmkv_is_scheduled(d.
type))
370 if (d.
type == ProcessType::DET && preserve_det)
continue;
374 sn.transparam.begin();
375 it !=
sn.transparam.end(); ++it) {
377 for (std::size_t m = 0; m < np.
firingproc.size(); ++m) {
378 if (detail::nonmkv_firing_ready(np, m))
continue;
381 if (detail::nonmkv_is_markovian(d.
type) || detail::nonmkv_is_scheduled(d.
type))
383 if (d.
type == ProcessType::DET && preserve_det)
continue;
400 "sn_nonmarkov_toph evaluates densities and fits moments");
402 if (!opts.enabled)
return;
403 if (opts.order == 0)
throw InputError(
"sn_nonmarkov_toph: the phase budget must be positive");
405 for (std::size_t ist = 0; ist <
sn.nstations; ++ist) {
406 for (std::size_t r = 0; r <
sn.nclasses; ++r) {
407 if (ist <
sn.disabled.size() && r <
sn.disabled[ist].size() &&
sn.disabled[ist][r])
411 const ProcessType p = d.
type;
412 if (detail::nonmkv_is_markovian(p) || detail::nonmkv_is_scheduled(p))
continue;
413 if (p == ProcessType::DET && opts.preserve_det)
continue;
417 if (!std::isfinite(mean) || mean <= 0.0)
continue;
419 if (p == ProcessType::DET) {
420 detail::nonmkv_install(
422 static_cast<unsigned>(opts.order)));
431 const std::function<double(
double)> ccdf = detail::nonmkv_ccdf(d);
434 const mam::Map<T> lt = detail::nonmkv_longtail<T>(ccdf, mean, ok);
436 detail::nonmkv_install(d, lt);
442 const std::function<double(
double)> pdf = detail::nonmkv_density(d);
444 detail::nonmkv_install(d,
445 detail::nonmkv_concentrated<T>(mean, scv, opts.order,
449 if (opts.phfit ==
PhFit::Cme && scv >= 0.0 && scv < 1.0) {
450 detail::nonmkv_install(d,
451 detail::nonmkv_concentrated<T>(mean, scv, opts.order,
458 detail::nonmkv_install(d, fit);
463 for (std::size_t ind = 0; ind <
sn.nodes.size(); ++ind) {
464 if (
sn.nodes[ind].nodetype != qn::NodeType::Transition)
continue;
465 typename std::map<std::size_t, qn::TransitionParam<T>>::iterator it =
466 sn.transparam.find(ind + 1);
467 if (it ==
sn.transparam.end())
continue;
469 for (std::size_t m = 0; m < np->
firingproc.size(); ++m) {
473 if (detail::nonmkv_firing_ready(*np, m))
continue;
476 const ProcessType p = d.
type;
477 if (detail::nonmkv_is_markovian(p) || detail::nonmkv_is_scheduled(p))
continue;
478 if (p == ProcessType::DET && opts.preserve_det)
continue;
482 if (p == ProcessType::DET) {
484 const double mu = (std::isfinite(mean) && mean > 0.0) ? mean : 1.0;
486 static_cast<unsigned>(opts.order));
493 const std::function<double(
double)> pdf = detail::nonmkv_density(d);
495 if (!std::isfinite(mean) || mean <= 0.0) {
500 static_cast<unsigned>(opts.order));
506 }
catch (
const std::exception&) {
508 static_cast<unsigned>(opts.order));
512 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...
bool nonmkv_enabled(const std::string &method)
Resolve options.config.nonmkv to NonmarkovOptions::enabled.
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.
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), const T &c1=num_traits< T >::from_int(0))
The fit with the component count chosen automatically: one per decade between the 0....
Map< T > map_bernstein(const std::function< double(double)> &f, unsigned order=20)
Acyclic phase-type approximation of an arbitrary density by Bernstein exponentials.
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.
Conservation laws of a layered queueing network, enumerated from its structure.
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.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
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 Transition node: MATLAB sn.nodeparam{ind} for an SPN transition,...
std::vector< lang::Distrib< T > > firingproc
firing distribution per mode
std::vector< std::size_t > firingphases
phase count per mode, 0 when non-Markovian