5#ifndef LINE_API_QSYS_QSYS_MAPG1_H
6#define LINE_API_QSYS_QSYS_MAPG1_H
138 throw InputError(
"qsys_mapg1: the mean service time must be positive");
147mam::Map<T> mapg1_erlang(
const T& mean, std::size_t k) {
148 if (k == 0)
throw InputError(
"qsys_mapg1: an Erlang fit needs at least one phase");
154 for (std::size_t i = 0; i < k; ++i) {
156 if (i + 1 < k) m.
D0(i, i + 1) = mu;
167mam::Map<T> mapg1_hyperexp2(
const T& mean,
const T& cv2) {
169 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
170 const T two = num_traits<T>::from_int(2);
171 const T p = T(T(one + sqrt(T(T(cv2 - one) / T(cv2 + one)))) / two);
172 const T l1 = T(two * p / mean), l2 = T(two * T(one - p) / mean);
179 m.D1(0, 1) = l1 * T(one - p);
181 m.D1(1, 1) = l2 * T(one - p);
186inline double mapg1_aph_m2_lower(
double m1,
double n) {
return m1 * m1 * (n + 1.0) / n; }
189inline double mapg1_aph_m3_lower(
double m1,
double m2,
double n) {
190 const double n2 = m2 / m1 / m1;
191 if (n2 < (n + 1.0) / n)
return std::numeric_limits<double>::infinity();
192 if (n2 < (n + 4.0) / (n + 1.0)) {
193 const double p = ((n + 1.0) * (n2 - 2.0)) / (3.0 * n2 * (n - 1.0)) *
194 ((-2.0 * std::sqrt(n + 1.0)) / std::sqrt(-3.0 * n * n2 + 4.0 * n + 4.0) - 1.0);
195 const double a = (n2 - 2.0) / (p * (1.0 - n2) + std::sqrt(p * p + p * n * (n2 - 2.0) / (n - 1.0)));
196 const double l = ((3.0 + a) * (n - 1.0) + 2.0 * a) / ((n - 1.0) * (1.0 + a * p)) -
197 (2.0 * a * (n + 1.0)) / (2.0 * (n - 1.0) + a * p * (n * a + 2.0 * n - 2.0));
200 return (n + 1.0) / n * n2 * m1 * m2;
204inline double mapg1_aph_m3_upper(
double m1,
double m2,
double n) {
205 const double n2 = m2 / m1 / m1;
206 if (n2 < (n + 1.0) / n)
return -std::numeric_limits<double>::infinity();
207 if (n2 <= n / (n - 1.0))
209 (2.0 * (n - 2.0) * (n * n2 - n - 1.0) * std::sqrt(1.0 + (n * (n2 - 2.0)) / (n - 1.0)) +
210 (n + 2.0) * (3.0 * n * n2 - 2.0 * n - 2.0)) /
212 return std::numeric_limits<double>::infinity();
221mam::Map<T> mapg1_ph2_from_3moments(
const T& m1T,
const T& m2T,
const T& m3T) {
223 const double prec = 1e-14;
224 const double m1 = num_traits<T>::to_double(m1T), m2 = num_traits<T>::to_double(m2T),
225 m3 = num_traits<T>::to_double(m3T);
226 if (m2 < mapg1_aph_m2_lower(m1, 2.0))
227 throw NumericError(ErrorKind{
"Infeasible"},
"PH2From3Moments: The given second moment is not feasible!");
228 if (m3 < mapg1_aph_m3_lower(m1, m2, 2.0))
229 throw NumericError(ErrorKind{
"Infeasible"},
"PH2From3Moments: The given third moment is not feasible (too small)!");
230 if (m3 > mapg1_aph_m3_upper(m1, m2, 2.0))
231 throw NumericError(ErrorKind{
"Infeasible"},
"PH2From3Moments: The given third moment is not feasible (too large)!");
232 if (std::fabs(m2 / m1 / m1 - 2.0) < prec)
return mapg1_exponential(m1T);
234 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
235 const T two = num_traits<T>::from_int(2), three = num_traits<T>::from_int(3), six = num_traits<T>::from_int(6);
236 const T b = T(three * m1T * m2T - m3T);
237 const T c = T(three * m2T * m2T - two * m1T * m3T);
238 const T e = T(m2T - two * m1T * m1T);
239 T a = T(b * b + six * c * e);
240 if (a < zero) a = zero;
242 T lambda1, lambda2, p;
244 lambda1 = T(T(b - a) / c);
245 lambda2 = T(T(b + a) / c);
246 p = T(T(-b - six * m1T * e + a) / T(b + a));
247 }
else if (c < zero) {
248 lambda1 = T(T(b + a) / c);
249 lambda2 = T(T(b - a) / c);
250 p = T(T(b + six * m1T * e + a) / T(-b + a));
253 lambda2 = T(one / m1T);
260 m.D0(0, 0) = -lambda1;
261 m.D0(0, 1) = lambda1;
262 m.D0(1, 1) = -lambda2;
263 m.D1(1, 0) = lambda2 * p;
264 m.D1(1, 1) = lambda2 * T(one - p);
282 "qsys_mapg1_service_fit requires transcendental arithmetic");
283 if (moments.empty())
throw InputError(
"qsys_mapg1: no service moments given");
285 const T m1 = moments[0];
287 if (moments.size() >= 3) {
292 return mam::aph_fit(moments[0], moments[1], moments[2]).aph;
293 }
catch (
const Error&) {
297 return detail::mapg1_ph2_from_3moments(moments[0], moments[1], moments[2]);
298 }
catch (
const Error&) {
301 return detail::mapg1_exponential(m1);
303 if (moments.size() == 2) {
304 const T cv2 = T(T(moments[1] / T(m1 * m1)) - one);
309 const T den = cv2 > floored ? cv2 : floored;
311 const std::size_t k = kd < 1.0 ? 1u :
static_cast<std::size_t
>(kd);
313 return detail::mapg1_erlang(m1, k);
317 const std::size_t k = kd < 2.0 ? 2u :
static_cast<std::size_t
>(kd);
319 return detail::mapg1_erlang(m1, k);
323 return detail::mapg1_exponential(m1);
326 return detail::mapg1_hyperexp2(m1, cv2);
329 return detail::mapg1_exponential(m1);
344 std::size_t dist_size, std::size_t numQLMoms, std::size_t numSTMoms) {
346 "qsys_mapg1 requires transcendental arithmetic");
372 std::size_t dist_size) {
373 return qsys_mapg1(arrival, moments, dist_size,
static_cast<std::size_t
>(3),
374 static_cast<std::size_t
>(3));
380 return qsys_mapg1(arrival, moments,
static_cast<std::size_t
>(100));
Minimal-order acyclic phase-type fit of the first three moments (matlab/lib/kpctoolbox/aph/aph_fit....
Base error for the multiprecision C++ port.
NumericError(const std::string &what)
The exception types the port throws.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
AphFitResult< T > aph_fit(const T &e1, const T &e2, const T &e3, unsigned nmax, const T &tol)
Fit an APH(n) with n <= nmax to the raw moments e1, e2, e3.
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
mam::Map< T > qsys_mapg1_service_fit(const std::vector< T > &moments, MapG1ServiceFit &kind)
Fit a general service time to a PH, following qsys_mapg1.m's fitServiceToPH.
MapG1ServiceFit
Which branch of fitServiceToPH was taken, for the caller and for tests.
@ Acyclic
three or more moments
@ Hyperexponential
cv2 > 1
@ Exponential
one moment, or cv2 exactly 1
@ Erlang
0 < cv2 < 1, or the cv2 <= 0 branch
MapG1Result< T > qsys_mapg1(const mam::Map< T > &arrival, const std::vector< T > &moments, std::size_t dist_size, std::size_t numQLMoms, std::size_t numSTMoms)
The MAP/G/1 FCFS queue.
MapMap1Result< T > qsys_mapmap1(const mam::Map< T > &arrival, const mam::Map< T > &service, std::size_t dist_size, std::size_t numQLMoms, std::size_t numSTMoms)
MAP/MAP/1 as the reference solves it: the service MAP collapsed to its PH marginal (mapToPh),...
Conservation laws of a layered queueing network, enumerated from its structure.
Number-type abstraction for the templated API port.
The MAP/MAP/1 FCFS queue: mean number in system, waiting time, sojourn time, utilization and the queu...
A MAP as the pair of matrices (D0, D1).
std::string analyzer
The reference's analyzer label ('BUTools:MMAPPH1FCFS'), kept for parity; the port computes the same q...
MapG1ServiceFit fitKind
which branch produced it
T meanWaitingTime
max(0, E[W] - m1), as in the reference
T meanQueueLength
E[N], number in system.
std::vector< T > sojournTimeMoments
raw moments E[W^k], k = 1..numSTMoms
std::size_t servicePhases
order of the fitted PH
std::vector< T > queueLengthMoments
raw moments E[N^k], k = 1..numQLMoms
mam::Map< T > serviceFit
the fitted PH as its renewal MAP
std::vector< T > queueLengthDist
P(N = n), n = 0, 1, ...
Return value of the MAP/MAP/1 family (qsys_mapmap1, qsys_mapph1, qsys_phph1), carrying the same quant...
std::vector< T > sojournTimeMoments
raw moments E[W^k], k = 1..numSTMoms
std::vector< T > queueLengthMoments
raw moments E[N^k], k = 1..numQLMoms
T meanQueueLength
E[N], number in system.
T meanSojournTime
E[W] = E[Wq] + E[S].
std::vector< T > queueLengthDist
P(N = n), n = 0, 1, ...