LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
mmpp2_fitc_approx.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_APPROX_H
6#define LINE_API_MAM_MMPP2_FITC_APPROX_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * MMPP(2) fitted to counting-process characteristics by optimization
12 * (matlab/lib/kpctoolbox/mmpp/mmpp2_fitc_approx.m).
13 *
14 * Where mmpp2_fitc.h inverts the Heffes-Lucantoni relations in closed form
15 * and therefore fails outright when the requested characteristics are not
16 * exactly representable, this routine minimizes the mismatch. The decision
17 * variables are the two phase arrival rates l1, l2 and the two switching
18 * rates r1, r2 of
19 *
20 * D0 = [ -(l1 + r1) r1 ; r2 -(l2 + r2) ], D1 = diag(l1, l2)
21 *
22 * and the objective is the sum of squared RELATIVE errors of the five
23 * characteristics (rate a, IDC at t1 and t2, IDC at infinity, third central
24 * moment of the counts at t2), each computed from the closed forms of the
25 * reference rather than from a numerical counting-process evaluation. As in
26 * the reference, the characteristics are evaluated on the time scale
27 * stretched by factor = a/xa, so the objective is invariant to the rate, and
28 * the returned MAP is finally rescaled with map_scale to have rate exactly a.
29 *
30 * ACCEPTANCE CONTRACT (see line/util/levmar.h). MATLAB drives this with the
31 * problem-based `solve`, i.e. fmincon interior-point over an
32 * automatically differentiated fcn2optimexpr expression; the JAR uses Apache
33 * Commons Math BOBYQA. This port uses the augmented Lagrangian of
34 * line/util/auglag.h with a Levenberg-Marquardt inner solver, since the
35 * objective is literally a sum of squares. None of the three reproduce each
36 * other's iterates. What is guaranteed and tested is the specification:
37 * 1. the returned (D0, D1) is a valid MAP -- row sums zero, non-negative
38 * off-diagonals of D0, non-negative D1 -- verified with map_isfeasible;
39 * 2. its rate is exactly a, by construction (map_scale);
40 * 3. the achieved objective, the same sum of squared relative errors the
41 * reference minimizes, is returned in Mmpp2FitcApproxResult::objective so
42 * it can be compared against any other optimizer's on the same input;
43 * 4. when the target characteristics are those of an actual MMPP(2), the
44 * objective reaches zero to the stated tolerance and the achieved
45 * characteristics, recomputed independently with map_count_var and
46 * map_count_moment, reproduce the targets.
47 *
48 * The two bound constraints of the reference (l1, l2 >= 1e-6 and r1, r2 >= 0)
49 * are passed as inequality rows. Note that the characteristics are undefined
50 * at r1 = r2 = 0 and at l1 r2 + l2 r1 = 0, where the expressions divide by
51 * zero; no guard is needed, because a step producing a non-finite residual
52 * compares false against the incumbent sum of squares and is therefore
53 * rejected by the Levenberg-Marquardt loop, which then increases its damping.
54 *
55 * REFERENCE DEFECT (matlab/lib/kpctoolbox/mmpp/mmpp2_fitc_approx.m): when
56 * t1 == t2 the local xbt2 is never assigned, yet it is used unconditionally
57 * two lines later in the expression for xm3t2. Calling the reference with
58 * t1 == t2 therefore raises "Unrecognized function or variable 'xbt2'". This
59 * port sets xbt2 = xbt1 in that case, which is what the value means, and drops
60 * the duplicated IDC residual, matching the JAR. MATLAB was NOT edited.
61 *
62 * Gated on transcendental arithmetic: exponentials in the IDC and third-moment
63 * expressions, and tolerance-driven optimization.
64 */
65
66#include <cstddef>
67#include <vector>
68
72#include "line/num/number.h"
73#include "line/util/auglag.h"
74#include "line/util/error.h"
75#include "line/util/matrix.h"
76
77namespace line {
78namespace mam {
79
80/** Result of mmpp2_fitc_approx. */
81template <class T>
83 Map<T> map; ///< the fitted MMPP(2), rescaled to rate a
84 T objective; ///< sum of squared relative errors at the optimum
85 T violation; ///< worst bound violation at the optimum
86 bool converged; ///< the constrained solve reached feasibility within its caps
87};
88
89namespace fitdetail {
90
91/**
92 * Closed-form counting characteristics of an MMPP(2) with parameters
93 * (l1, l2, r1, r2), transcribed from the reference's compute_obj. The product
94 * exp(-r1 T - r2 T) is written as a single exponential of -(r1 + r2) T.
95 */
96template <class T>
97struct Mmpp2Chars {
98 T xa; ///< arrival rate
99 T xbt1; ///< IDC at the (stretched) scale t1
100 T xbt2; ///< IDC at the (stretched) scale t2
101 T xbinf; ///< limiting IDC
102 T xm3t2; ///< third central moment of the counts at the stretched t2
103};
104
105template <class T>
106T mmpp2_idc_at(const T& l1, const T& l2, const T& r1, const T& r2, const T& tt) {
107 const T one = num_traits<T>::from_int(1);
108 const T two = num_traits<T>::from_int(2);
109 const T four = num_traits<T>::from_int(4);
110 const T E = num_exp(T(-(r1 + r2) * tt));
111 const T inner = r1 * (two * l1 * l1 * r2 * r2 * tt - two * l2 * l2 * r2 - two * l1 * l1 * r2 +
112 two * l2 * l2 * r2 * r2 * tt + four * l1 * l2 * r2 +
113 two * l1 * l1 * r2 * E + two * l2 * l2 * r2 * E -
114 four * l1 * l2 * r2 * r2 * tt - four * l1 * l2 * r2 * E) +
115 r1 * r1 * (two * r2 * tt * l1 * l1 - four * r2 * tt * l1 * l2 +
116 two * r2 * tt * l2 * l2);
117 return inner / (tt * pw(T(r1 + r2), 3) * (l1 * r2 + l2 * r1)) + one;
118}
119
120template <class T>
121Mmpp2Chars<T> mmpp2_chars(const T& l1, const T& l2, const T& r1, const T& r2, const T& a,
122 const T& t1, const T& t2, bool same_scale) {
123 const T one = num_traits<T>::from_int(1);
124 const T two = num_traits<T>::from_int(2);
125 const T three = num_traits<T>::from_int(3);
126 const T four = num_traits<T>::from_int(4);
127 const T six = num_traits<T>::from_int(6);
128
129 Mmpp2Chars<T> c;
130 c.xa = (l1 * r2 + l2 * r1) / (r1 + r2);
131 const T factor = a / c.xa;
132
133 c.xbt1 = mmpp2_idc_at(l1, l2, r1, r2, T(t1 * factor));
134 c.xbt2 = same_scale ? c.xbt1 : mmpp2_idc_at(l1, l2, r1, r2, T(t2 * factor));
135
136 c.xbinf = ((two * r2 * l1 * l1 - four * r2 * l1 * l2 + two * r2 * l2 * l2) * r1 * r1 +
137 (two * l1 * l1 * r2 * r2 - four * l1 * l2 * r2 * r2 + two * l2 * l2 * r2 * r2) * r1) /
138 (pw(T(r1 + r2), 3) * (l1 * r2 + l2 * r1)) +
139 one;
140
141 const T t = t2 * factor;
142 const T d = r1 + r2;
143 const T p = (l1 - l2) * (r1 - r2);
144 const T Etd = num_exp(T(-t * d));
145 const T bm1 = c.xbinf - one;
146 const T xg3t = pw(c.xa, 3) * pw(t, 3) + three * c.xa * c.xa * bm1 * t * t +
147 three * c.xa * bm1 / d * (p / d - c.xa) * t +
148 three * c.xa / (d * d) * bm1 * (p + c.xa * d) * t * Etd -
149 six * c.xa / pw(d, 3) * bm1 * p * (one - Etd);
150 c.xm3t2 = xg3t - three * c.xa * t * (c.xa * t - one) * c.xbt2 -
151 c.xa * t * (c.xa * t - one) * (c.xa * t - two);
152 return c;
153}
154
155} // namespace fitdetail
156
157/**
158 * Fit an MMPP(2) to counting characteristics.
159 *
160 * @param a arrival rate
161 * @param bt1 IDC at time scale t1
162 * @param bt2 IDC at time scale t2
163 * @param binf limiting IDC
164 * @param m3t2 third central moment of the counts at t2
165 * @param t1,t2 the two time scales
166 * @param opt tuning of the constrained solve
167 */
168template <class T>
169Mmpp2FitcApproxResult<T> mmpp2_fitc_approx(const T& a, const T& bt1, const T& bt2, const T& binf,
170 const T& m3t2, const T& t1, const T& t2,
171 const AugLagOptions<T>& opt) {
173 "mmpp2_fitc_approx requires transcendental arithmetic");
174 const T zero = num_traits<T>::from_int(0);
175 const T one = num_traits<T>::from_int(1);
176 if (a <= zero) throw InputError("mmpp2_fitc_approx: non-positive arrival rate");
177 if (t1 <= zero || t2 <= zero) throw InputError("mmpp2_fitc_approx: non-positive time scale");
178 if (bt1 == zero || bt2 == zero || binf == zero || m3t2 == zero)
179 throw InputError("mmpp2_fitc_approx: a target characteristic is zero, so the relative "
180 "objective of the reference is undefined");
181
182 const bool same_scale = t1 == t2;
183 const std::size_t nres = same_scale ? 4 : 5;
184
185 auto resid = [a, bt1, bt2, binf, m3t2, t1, t2, same_scale, one](const std::vector<T>& x) {
186 const fitdetail::Mmpp2Chars<T> c =
187 fitdetail::mmpp2_chars(x[0], x[1], x[2], x[3], a, t1, t2, same_scale);
188 std::vector<T> r;
189 r.push_back(T(c.xa / a - one));
190 r.push_back(T(c.xbt1 / bt1 - one));
191 if (!same_scale) r.push_back(T(c.xbt2 / bt2 - one));
192 r.push_back(T(c.xbinf / binf - one));
193 r.push_back(T(c.xm3t2 / m3t2 - one));
194 return r;
195 };
196
197 // the reference's prob.Constraints: l1, l2 >= 1e-6 and r1, r2 >= 0
198 const T lmin = num_traits<T>::from_double(1e-6);
199 auto g = [lmin, zero](const std::vector<T>& x) {
200 std::vector<T> gv(4);
201 gv[0] = lmin - x[0];
202 gv[1] = lmin - x[1];
203 gv[2] = zero - x[2];
204 gv[3] = zero - x[3];
205 return gv;
206 };
207
208 std::vector<T> x0(4);
209 x0[0] = a * num_traits<T>::from_rational(3, 4); // the reference's guess
210 x0[1] = a * num_traits<T>::from_rational(3, 2);
211 x0[2] = num_traits<T>::from_rational(1, 3);
212 x0[3] = num_traits<T>::from_rational(2, 3);
213
214 const AugLagResult<T> sol = auglag_ls(resid, nres, NoConstraints<T>(), g, x0, opt);
215
216 const T l1 = sol.x[0];
217 const T l2 = sol.x[1];
218 const T r1 = sol.x[2];
219 const T r2 = sol.x[3];
220
221 Map<T> m;
222 m.D0 = Matrix<T>(2, 2, zero);
223 m.D1 = Matrix<T>(2, 2, zero);
224 m.D0(0, 0) = -(l1 + r1);
225 m.D0(0, 1) = r1;
226 m.D0(1, 0) = r2;
227 m.D0(1, 1) = -(l2 + r2);
228 m.D1(0, 0) = l1;
229 m.D1(1, 1) = l2;
230
232 res.map = map_scale(m, T(one / a)); // force the rate to a, as the reference does
233 res.objective = sol.fval;
234 res.violation = sol.violation;
235 res.converged = sol.violation <= opt.ctol;
236 return res;
237}
238
239/** mmpp2_fitc_approx with the default tuning. */
240template <class T>
241Mmpp2FitcApproxResult<T> mmpp2_fitc_approx(const T& a, const T& bt1, const T& bt2, const T& binf,
242 const T& m3t2, const T& t1, const T& t2) {
244 opt.ctol = num_traits<T>::from_double(1e-12);
245 return mmpp2_fitc_approx(a, bt1, bt2, binf, m3t2, t1, t2, opt);
246}
247
248} // namespace mam
249} // namespace line
250
251#endif // LINE_API_MAM_MMPP2_FITC_APPROX_H
Augmented Lagrangian method for equality- and inequality-constrained minimization,...
InputError(const std::string &what)
Definition error.h:39
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...
MAP constructors and structural transformations.
Dense matrix and non-owning view.
Mmpp2FitcApproxResult< T > mmpp2_fitc_approx(const T &a, const T &bt1, const T &bt2, const T &binf, const T &m3t2, const T &t1, const T &t2, const AugLagOptions< T > &opt)
Fit an MMPP(2) to counting characteristics.
Map< T > map_scale(const Map< T > &in, const T &new_mean)
Rescale time so that the mean inter-arrival time becomes new_mean.
AugLagOptions< T > auglag_defaults()
Defaults: rho0 = 10, growth 10, feasibility 1e-10, 50 outer iterations.
Definition auglag.h:81
AugLagResult< T > auglag_ls(R r, std::size_t m, H h, G g, const std::vector< T > &x0, const AugLagOptions< T > &opt)
Augmented Lagrangian with a least-squares objective and levmar as the inner solver.
Definition auglag.h:246
Number-type abstraction for the templated API port.
Tuning of the outer multiplier iteration.
Definition auglag.h:68
Outcome of a constrained solve.
Definition auglag.h:96
std::vector< T > x
best point found
Definition auglag.h:97
T violation
max(|h_i|, max(0, g_j)) at x
Definition auglag.h:99
T fval
the ORIGINAL objective f(x), not the augmented one
Definition auglag.h:98
A constraint map that returns no constraints; the default for h or g.
Definition auglag.h:108
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_approx.
Map< T > map
the fitted MMPP(2), rescaled to rate a
T objective
sum of squared relative errors at the optimum
T violation
worst bound violation at the optimum
bool converged
the constrained solve reached feasibility within its caps