LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
mmpp2_fitc.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_MAM_MMPP2_FITC_H
6#define LINE_API_MAM_MMPP2_FITC_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * MMPP(2) matching counting-process characteristics
12 * (matlab/lib/kpctoolbox/mmpp/mmpp2_fitc.m).
13 *
14 * Implements Heffes and Lucantoni (1986): the arrival rate mu, the index of
15 * dispersion for counts at two finite time scales and in the limit
16 * (bt1, bt2, binf) and the third central moment of the counts at t2 determine
17 * the four MMPP(2) parameters (r1, r2, l1, l2) in closed form. The only
18 * implicit step is the total switching rate x = r1 + r2, which solves
19 * (binf - 1)/(binf - bt1) = c, x t1 = W(-c e^-c) + c
20 * with W the principal branch of the Lambert function. MATLAB solves
21 * w e^w = -c e^-c with fsolve started at w = 1, i.e. it targets the principal
22 * branch; this port evaluates W0 directly by Halley iteration, so the result
23 * is deterministic and does not depend on a solver's tolerances.
24 *
25 * Gated on transcendental arithmetic: exp, log and square roots appear
26 * throughout, and the Lambert step is inherently tolerance-driven.
27 *
28 * Degenerate inputs return a Poisson process of rate mu, as in the reference:
29 * a constant unit IDC (binf = bt1 = 1) has no MMPP(2) representation, and
30 * neither does an IDC profile violating binf > bt1 > 1.
31 */
32
35#include "line/num/number.h"
36#include "line/util/error.h"
37#include "line/util/matrix.h"
38
39namespace line {
40namespace mam {
41
42namespace fitdetail {
43
44/**
45 * Principal branch of the Lambert W function, W0(z) for z >= -1/e.
46 *
47 * Halley iteration on w e^w = z. The initialization is the branch-point
48 * expansion near z = -1/e, where the derivative of w e^w vanishes and a naive
49 * start converges slowly, and log(1 + z) elsewhere.
50 */
51template <class T>
52T lambertw0(const T& z, unsigned max_iter) {
53 const T zero = num_traits<T>::from_int(0);
54 const T one = num_traits<T>::from_int(1);
55 const T two = num_traits<T>::from_int(2);
56 const T e = num_exp(one);
57 const T ez1 = e * z + one;
58 if (ez1 < zero) throw NumericError("lambertw0: argument below the branch point -1/e");
59 if (z == zero) return zero;
60 T w;
61 if (ez1 < num_traits<T>::from_double(0.3)) {
62 const T p = num_sqrt(T(two * ez1));
63 w = -one + p - p * p / num_traits<T>::from_int(3) +
64 num_traits<T>::from_int(11) * p * p * p / num_traits<T>::from_int(72);
65 } else if (z > zero) {
66 w = num_log(T(one + z));
67 } else {
68 w = z;
69 }
70 for (unsigned it = 0; it < max_iter; ++it) {
71 const T ew = num_exp(w);
72 const T f = w * ew - z;
73 if (f == zero) break;
74 const T d1 = ew * (w + one);
75 // w=-1 branch point rationale: see _kb/03-api-layer.md (cpp port notes: mam)
76 const T dd = two * w + two;
77 T den = d1;
78 if (dd != zero) den = d1 - (w + two) * f / dd;
79 if (den == zero || !(den == den)) break;
80 const T step = f / den;
81 w -= step;
82 if (num_abs(step) <= num_traits<T>::from_double(1e-40) * (one + num_abs(w))) break;
83 }
84 return w;
85}
86
87} // namespace fitdetail
88
89/** Result of mmpp2_fitc. */
90template <class T>
93 bool degenerate; ///< true when a Poisson process was returned instead
94 bool third_moment_ok; ///< false when the third moment had to be dropped for feasibility
95};
96
97/**
98 * MMPP(2) from the arrival rate, the IDC at t1, t2 and infinity, and the
99 * third central moment of the counts at t2.
100 */
101template <class T>
102Mmpp2FitcResult<T> mmpp2_fitc(const T& mu, const T& bt1, const T& bt2, const T& binf, const T& m3t2,
103 const T& t1, const T& t2) {
105 "mmpp2_fitc requires transcendental arithmetic");
106 using fitdetail::lambertw0;
107 using fitdetail::num_exp;
108 using fitdetail::num_sqrt;
109 using fitdetail::pw;
110
111 const T zero = num_traits<T>::from_int(0);
112 const T one = num_traits<T>::from_int(1);
113 const T two = num_traits<T>::from_int(2);
114 const T three = num_traits<T>::from_int(3);
115 const T six = num_traits<T>::from_int(6);
116 const T half = num_traits<T>::from_rational(1, 2);
117 const T tiny = num_traits<T>::from_double(1e-8);
118
119 if (mu <= zero) throw InputError("mmpp2_fitc: non-positive arrival rate");
120 if (t1 <= zero || t2 <= zero) throw InputError("mmpp2_fitc: non-positive time scale");
121
123 res.degenerate = false;
124 res.third_moment_ok = true;
125
126 if ((num_abs(T(binf - one)) < tiny && num_abs(T(binf - bt1)) < tiny) ||
127 !(binf > bt1 && bt1 > one)) {
128 res.map = map_exponential(mu);
129 res.degenerate = true;
130 return res;
131 }
132
133 const T c = (binf - one) / (binf - bt1);
134 const T zarg = -c * num_exp(T(-c));
135 const T w0 = lambertw0(zarg, 200u);
136 const T x = (w0 + c) / t1;
137 if (x <= zero) throw NumericError("mmpp2_fitc: non-positive switching rate");
138
139 const T k1 = pw(mu, 3) * pw(t2, 3);
140 const T k2 = three * mu * mu * (binf - one) * t2 * t2;
141 const T k3 = three * mu * (binf - one) / x * t2;
142 const T k4 = three * mu / (x * x) * (binf - one) * t2 * num_exp(T(-x * t2));
143 const T k5 = six * mu / pw(x, 3) * (binf - one) * (one - num_exp(T(-x * t2)));
144 const T g1t2 = m3t2 + three * mu * t2 * (mu * t2 - one) * bt2 +
145 mu * t2 * (mu * t2 - one) * (mu * t2 - two);
146 const T hden = (k3 / x) + k4 - k5;
147 if (hden == zero) throw NumericError("mmpp2_fitc: degenerate third-moment equation");
148 const T h = (g1t2 - k1 - k2 - k3 * (-mu) - k4 * mu * x) / hden;
149
150 T r1, r2, l1, l2;
151 if (num_abs(h) < num_traits<T>::from_double(1e-4)) {
152 r1 = x / two;
153 r2 = x / two;
154 const T d = half * num_sqrt(T(two * (binf - one) * mu * x));
155 l2 = mu - d;
156 l1 = mu + d;
157 } else {
158 const T y = (binf - one) * mu * pw(x, 3) / (two * h * h);
159 r1 = x / two * (one + one / num_sqrt(T(num_traits<T>::from_int(4) * y + one)));
160 r2 = x - r1;
161 if (r1 < r2) {
162 const T tmp = r1;
163 r1 = r2;
164 r2 = tmp;
165 }
166 if (r1 == r2) throw NumericError("mmpp2_fitc: degenerate switching rates");
167 const T wv = h / (r1 - r2);
168 const T w_min = -mu / r1 * (r1 + r2);
169 const T w_max = mu / r2 * (r1 + r2);
170 if (wv < w_min || wv > w_max) {
171 // Third moment dropped to keep the rates non-negative.
172 res.third_moment_ok = false;
173 const T zz = (binf - one) * pw(x, 3) * mu;
174 const T u = x * zz / (two * mu * mu * x * x + zz);
175 r1 = u + (x - u) / two;
176 r2 = x - r1;
177 const T delta = num_sqrt(T(zz / (two * r1 * r2)));
178 l2 = mu - r2 / x * delta;
179 l1 = l2 + delta;
180 } else {
181 l2 = mu - h / (r1 - r2) * (r2 / (r1 + r2));
182 l1 = h / (r1 - r2) + l2;
183 }
184 }
185
186 Map<T> m;
187 m.D0 = Matrix<T>(2, 2, zero);
188 m.D1 = Matrix<T>(2, 2, zero);
189 m.D0(0, 0) = -(r1 + l1);
190 m.D0(0, 1) = r1;
191 m.D0(1, 0) = r2;
192 m.D0(1, 1) = -(r2 + l2);
193 m.D1(0, 0) = l1;
194 m.D1(1, 1) = l2;
195 res.map = m;
196 return res;
197}
198
199} // namespace mam
200} // namespace line
201
202#endif // LINE_API_MAM_MMPP2_FITC_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Scalar helpers shared by the MAP/PH moment-matching headers.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
Mmpp2FitcResult< T > mmpp2_fitc(const T &mu, const T &bt1, const T &bt2, const T &binf, const T &m3t2, const T &t1, const T &t2)
MMPP(2) from the arrival rate, the IDC at t1, t2 and infinity, and the third central moment of the co...
Definition mmpp2_fitc.h:102
Map< T > map_exponential(const T &lambda)
Two-phase MAP constructor for a Poisson process of rate lambda.
Definition map_moment.h:213
T num_abs(const T &v)
Definition number.h:172
Number-type abstraction for the templated API port.
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
Matrix< T > D1
Definition map_moment.h:55
Matrix< T > D0
Definition map_moment.h:54
Result of mmpp2_fitc.
Definition mmpp2_fitc.h:91
bool degenerate
true when a Poisson process was returned instead
Definition mmpp2_fitc.h:93
bool third_moment_ok
false when the third moment had to be dropped for feasibility
Definition mmpp2_fitc.h:94