5#ifndef LINE_API_PFQN_STDF_H
6#define LINE_API_PFQN_STDF_H
98 std::vector<std::vector<Matrix<T>>>
RD;
115 for (std::size_t i = 0; i < A.
rows(); ++i) {
116 if (i == k)
continue;
117 for (std::size_t j = 0; j < A.
cols(); ++j) B(o, j) = A(i, j);
125Matrix<T> stdf_mu(
const std::vector<int>& S,
int Nt) {
126 Matrix<T> mu(S.size(),
static_cast<std::size_t
>(Nt));
127 for (std::size_t k = 0; k < S.size(); ++k)
128 for (
int n = 1; n <= Nt; ++n)
129 mu(k,
static_cast<std::size_t
>(n - 1)) =
136std::vector<T> stdf_guard_tset(
const std::vector<T>& tset) {
137 const T zero = num_traits<T>::from_int(0);
138 const T fine = num_traits<T>::from_double(
kStdfFineTol);
139 std::vector<T> out = tset;
140 for (std::size_t t = 0; t < out.size(); ++t) {
141 if (out[t] < zero)
throw InputError(
"pfqn_stdf: negative evaluation time");
142 if (out[t] == zero) out[t] = fine;
154double stdf_lg(
const Matrix<T>& L,
const std::vector<int>& N,
const Matrix<T>& Z,
155 const Matrix<T>& mu,
bool singleStation,
bool* stable) {
157 const MvaLdResult<T> r =
pfqn_mvald(L, N, Z, mu);
158 if (!r.isNumStable && stable !=
nullptr) *stable =
false;
200T stdf_hypoexp_cdf(
const std::vector<T>& r,
const T& t) {
201 const T zero = num_traits<T>::from_int(0);
202 const T one = num_traits<T>::from_int(1);
203 const std::size_t p = r.size();
205 for (std::size_t i = 1; i < p; ++i)
206 if (lambda < r[i]) lambda = r[i];
207 const T x = T(lambda * t);
209 std::vector<T> step(p);
210 for (std::size_t i = 0; i < p; ++i) step[i] = T(r[i] / lambda);
212 std::vector<T> v(p, zero);
216 T pois = exp(T(zero - x));
218 const T tol = num_traits<T>::from_double(1e-20);
221 for (std::size_t n = 0; n <= 100000; ++n) {
223 const T out = T(v[p - 1] * step[p - 1]);
224 for (std::size_t i = p - 1; i >= 1; --i)
225 v[i] = T(v[i] * T(one - step[i])) + T(v[i - 1] * step[i - 1]);
226 v[0] = T(v[0] * T(one - step[0]));
228 pois = T(pois * x) / num_traits<T>::from_int(
static_cast<long>(n));
232 const T nx = num_traits<T>::from_int(
static_cast<long>(n) + 2);
235 const T next = T(pois * x) / num_traits<T>::from_int(
static_cast<long>(n) + 1);
236 const T tail = T(next / T(one - T(x / nx)));
237 if (tail <= T(F * tol) || tail == zero)
return F;
241 throw NumericError(
"pfqn_stdf: the level CDF series did not converge");
259T stdf_level_cdf(
const T& rate,
int S,
int n,
const T& t) {
263 const T sr = T(num_traits<T>::from_int(S) * rate);
264 for (
int j = 0; j < n - S + 1; ++j) r.push_back(sr);
267 for (std::size_t i = 1; i < r.size(); ++i)
268 if (lambda < r[i]) lambda = r[i];
269 if (T(lambda * t) <= num_traits<T>::from_int(
static_cast<long>(r.size())))
270 return stdf_hypoexp_cdf(r, t);
271 const T one = num_traits<T>::from_int(1);
276 std::vector<mam::Map<T>> parts;
279 T(num_traits<T>::from_int(n - S + 1) / (num_traits<T>::from_int(S) * rate)),
280 static_cast<unsigned>(n - S + 1)));
283 std::vector<T> one_t;
290void stdf_gamma(
const Matrix<T>& mu,
const Matrix<T>& hkc, std::size_t t, std::size_t k,
int sumNr,
291 bool truncate, Matrix<T>& gammat, Matrix<T>& gammak) {
293 for (
int m = 1; m <= sumNr; ++m)
294 gammat(k,
static_cast<std::size_t
>(m - 1)) =
295 mu(k,
static_cast<std::size_t
>(m - 1)) *
296 hkc(t,
static_cast<std::size_t
>(m - 1)) / hkc(t,
static_cast<std::size_t
>(m));
298 if (!truncate)
return;
300 const std::size_t keep = sumNr >= 1 ?
static_cast<std::size_t
>(sumNr - 1) : 0;
301 Matrix<T> g(gammak.rows(), keep);
302 for (std::size_t i = 0; i < gammak.rows(); ++i)
303 for (std::size_t j = 0; j < keep; ++j) g(i, j) = gammak(i, j);
323 const std::vector<int>& S,
const std::vector<std::size_t>& fcfsNodes,
324 const Matrix<T>& rates,
const std::vector<T>& tset) {
326 "pfqn_stdf requires transcendental arithmetic: the level CDFs are matrix "
327 "exponentials and the normalizing constants are combined in the log domain");
329 const std::size_t M = L.
rows();
330 const std::size_t R = L.
cols();
331 if (R != N.size())
throw InputError(
"pfqn_stdf: L and N disagree on the class count");
332 if (S.size() != M)
throw InputError(
"pfqn_stdf: S has the wrong length");
333 if (rates.
rows() != M || rates.
cols() != R)
334 throw InputError(
"pfqn_stdf: rates has the wrong shape");
335 for (std::size_t k = 0; k < M; ++k)
336 if (S[k] < 1)
throw InputError(
"pfqn_stdf: the server count must be at least one");
340 if (n < 0)
throw InputError(
"pfqn_stdf: negative population");
343 if (Nt < 1)
throw InputError(
"pfqn_stdf: the population must be at least one");
348 const std::size_t nt = tset.size();
350 const Matrix<T> mu = detail::stdf_mu<T>(S, Nt);
351 const std::vector<T> tv = detail::stdf_guard_tset(tset);
357 const bool singleStation = (M == 1);
359 for (std::size_t ki = 0; ki < fcfsNodes.size(); ++ki) {
360 const std::size_t k = fcfsNodes[ki];
361 if (k >= M)
throw InputError(
"pfqn_stdf: FCFS station index out of range");
363 T lo = rates(k, 0), hi = rates(k, 0);
364 for (std::size_t r = 1; r < R; ++r) {
365 if (rates(k, r) < lo) lo = rates(k, r);
366 if (rates(k, r) > hi) hi = rates(k, r);
368 if (T(hi - lo) > fine)
370 "pfqn_stdf: the FCFS station has distinct per-class service rates, the model is "
372 if (!(rates(k, 0) > zero))
373 throw InputError(
"pfqn_stdf: the FCFS service rate must be strictly positive");
378 Matrix<T> hkc(nt,
static_cast<std::size_t
>(Nt) + 1);
379 for (
int n = 0; n <= Nt; ++n)
380 for (std::size_t t = 0; t < nt; ++t)
381 hkc(t,
static_cast<std::size_t
>(n)) =
382 detail::stdf_level_cdf(rates(k, 0), S[k], n, tv[t]);
384 const Matrix<T> Lk = detail::stdf_drop_row(L, k);
385 const Matrix<T> muk = detail::stdf_drop_row(mu, k);
387 for (std::size_t r = 0; r < R; ++r) {
388 if (!(L(k, r) > fine))
continue;
389 std::vector<int> Nr = N;
392 for (
int n : Nr) sumNr += n;
394 const double lGr = detail::stdf_lg(L, Nr, Z, mu, singleStation, &res.
isNumStable);
395 const double lGk = detail::stdf_lg(Lk, Nr, Z, muk, singleStation, &res.
isNumStable);
398 for (std::size_t t = 0; t < nt; ++t) RD(t, 1) = tv[t];
401 for (std::size_t t = 0; t < nt; ++t) {
402 detail::stdf_gamma(mu, hkc, t, k, sumNr,
true, gammat, gammak);
404 for (std::size_t s = 0; s < R; ++s) {
405 if (Nr[s] <= 0)
continue;
406 std::vector<int> Nrs = Nr;
409 detail::stdf_lg(L, Nrs, Z, gammak, singleStation, &res.
isNumStable);
410 H += L(k, s) * hkc(t, 0) / gammat(k, 0) *
413 if (!(H == H)) H = fine;
416 RD(t, 0) = v > one ? one : v;
NumericError(const std::string &what)
The exception types the port throws.
Cumulative distribution of the inter-arrival time of a MAP.
Dense matrix and non-owning view.
Map< T > map_erlang(const T &mean, unsigned k)
Erlang-k renewal MAP with the given mean (map_erlang.m).
Map< T > map_exponential_mean(const T &mean)
Poisson process with the given mean inter-arrival time (map_exponential.m).
Map< T > map_sumind(const std::vector< Map< T > > &maps)
Sum of independent, not necessarily identical MAPs: after each component completes,...
std::vector< T > map_cdf(const Map< T > &m, const std::vector< T > &points)
Cumulative distribution of the inter-arrival time at the given points.
StdfResult< T > pfqn_stdf(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &S, const std::vector< std::size_t > &fcfsNodes, const Matrix< T > &rates, const std::vector< T > &tset)
Sojourn-time distribution at the listed FCFS stations.
MvaLdResult< T > pfqn_mvald(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu, bool stabilize=true)
Exact MVA for a closed network of load-dependent stations.
Matrix< T > pfqn_mushift(const Matrix< T > &mu, const std::vector< std::size_t > &iset)
Shift the load-dependent service-rate lattice of selected stations.
static const double kStdfFineTol
GlobalConstants.FineTol, as set by matlab/lineStart.m.
ComomRmResult< T > pfqn_comomrm_ld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const Matrix< T > &mu)
CoMoM for the repairman model with an arbitrary LOAD-DEPENDENT rate lattice at the single queueing st...
Number-type abstraction for the templated API port.
CoMoM for the repairman model with an arbitrary LOAD-DEPENDENT rate lattice at the single queueing st...
Shift the load-dependent service-rate lattice of selected stations.
Exact Mean Value Analysis for mixed open/closed networks with multiserver stations.
Result of pfqn_stdf / pfqn_stdf_heur, mirroring the MATLAB cell array RD.
std::vector< std::vector< Matrix< T > > > RD
bool isNumStable
false once an aggregate solve reported instability
std::vector< T > tset
the time set after the zero guard