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 * When t1 == t2, xbt2 = xbt1 and the duplicated IDC residual is dropped,
56 * matching the JAR; the t2 branch alone would leave xbt2 unassigned.
57 *
58 * Gated on transcendental arithmetic: exponentials in the IDC and third-moment
59 * expressions, and tolerance-driven optimization.
60 */
61
62#include <cstddef>
63#include <vector>
64
68#include "line/num/number.h"
69#include "line/util/auglag.h"
70#include "line/util/error.h"
71#include "line/util/matrix.h"
72
73namespace line {
74namespace mam {
75
76/** Result of mmpp2_fitc_approx. */
77template <class T>
79 Map<T> map; ///< the fitted MMPP(2), rescaled to rate a
80 T objective; ///< sum of squared relative errors at the optimum
81 T violation; ///< worst bound violation at the optimum
82 bool converged; ///< the constrained solve reached feasibility within its caps
83};
84
85namespace fitdetail {
86
87/**
88 * Closed-form counting characteristics of an MMPP(2) with parameters
89 * (l1, l2, r1, r2), transcribed from the reference's compute_obj. The product
90 * exp(-r1 T - r2 T) is written as a single exponential of -(r1 + r2) T.
91 */
92template <class T>
93struct Mmpp2Chars {
94 T xa; ///< arrival rate
95 T xbt1; ///< IDC at the (stretched) scale t1
96 T xbt2; ///< IDC at the (stretched) scale t2
97 T xbinf; ///< limiting IDC
98 T xm3t2; ///< third central moment of the counts at the stretched t2
99};
100
101template <class T>
102T mmpp2_idc_at(const T& l1, const T& l2, const T& r1, const T& r2, const T& tt) {
103 const T one = num_traits<T>::from_int(1);
104 const T two = num_traits<T>::from_int(2);
105 const T four = num_traits<T>::from_int(4);
106 const T E = num_exp(T(-(r1 + r2) * tt));
107 const T inner = r1 * (two * l1 * l1 * r2 * r2 * tt - two * l2 * l2 * r2 - two * l1 * l1 * r2 +
108 two * l2 * l2 * r2 * r2 * tt + four * l1 * l2 * r2 +
109 two * l1 * l1 * r2 * E + two * l2 * l2 * r2 * E -
110 four * l1 * l2 * r2 * r2 * tt - four * l1 * l2 * r2 * E) +
111 r1 * r1 * (two * r2 * tt * l1 * l1 - four * r2 * tt * l1 * l2 +
112 two * r2 * tt * l2 * l2);
113 return inner / (tt * pw(T(r1 + r2), 3) * (l1 * r2 + l2 * r1)) + one;
114}
115
116template <class T>
117Mmpp2Chars<T> mmpp2_chars(const T& l1, const T& l2, const T& r1, const T& r2, const T& a,
118 const T& t1, const T& t2, bool same_scale) {
119 const T one = num_traits<T>::from_int(1);
120 const T two = num_traits<T>::from_int(2);
121 const T three = num_traits<T>::from_int(3);
122 const T four = num_traits<T>::from_int(4);
123 const T six = num_traits<T>::from_int(6);
124
125 Mmpp2Chars<T> c;
126 c.xa = (l1 * r2 + l2 * r1) / (r1 + r2);
127 const T factor = a / c.xa;
128
129 c.xbt1 = mmpp2_idc_at(l1, l2, r1, r2, T(t1 * factor));
130 c.xbt2 = same_scale ? c.xbt1 : mmpp2_idc_at(l1, l2, r1, r2, T(t2 * factor));
131
132 c.xbinf = ((two * r2 * l1 * l1 - four * r2 * l1 * l2 + two * r2 * l2 * l2) * r1 * r1 +
133 (two * l1 * l1 * r2 * r2 - four * l1 * l2 * r2 * r2 + two * l2 * l2 * r2 * r2) * r1) /
134 (pw(T(r1 + r2), 3) * (l1 * r2 + l2 * r1)) +
135 one;
136
137 const T t = t2 * factor;
138 const T d = r1 + r2;
139 const T p = (l1 - l2) * (r1 - r2);
140 const T Etd = num_exp(T(-t * d));
141 const T bm1 = c.xbinf - one;
142 const T xg3t = pw(c.xa, 3) * pw(t, 3) + three * c.xa * c.xa * bm1 * t * t +
143 three * c.xa * bm1 / d * (p / d - c.xa) * t +
144 three * c.xa / (d * d) * bm1 * (p + c.xa * d) * t * Etd -
145 six * c.xa / pw(d, 3) * bm1 * p * (one - Etd);
146 c.xm3t2 = xg3t - three * c.xa * t * (c.xa * t - one) * c.xbt2 -
147 c.xa * t * (c.xa * t - one) * (c.xa * t - two);
148 return c;
149}
150
151} // namespace fitdetail
152
153/**
154 * Fit an MMPP(2) to counting characteristics.
155 *
156 * @param a arrival rate
157 * @param bt1 IDC at time scale t1
158 * @param bt2 IDC at time scale t2
159 * @param binf limiting IDC
160 * @param m3t2 third central moment of the counts at t2
161 * @param t1,t2 the two time scales
162 * @param opt tuning of the constrained solve
163 */
164template <class T>
165Mmpp2FitcApproxResult<T> mmpp2_fitc_approx(const T& a, const T& bt1, const T& bt2, const T& binf,
166 const T& m3t2, const T& t1, const T& t2,
167 const AugLagOptions<T>& opt) {
169 "mmpp2_fitc_approx requires transcendental arithmetic");
170 const T zero = num_traits<T>::from_int(0);
171 const T one = num_traits<T>::from_int(1);
172 if (a <= zero) throw InputError("mmpp2_fitc_approx: non-positive arrival rate");
173 if (t1 <= zero || t2 <= zero) throw InputError("mmpp2_fitc_approx: non-positive time scale");
174 if (bt1 == zero || bt2 == zero || binf == zero || m3t2 == zero)
175 throw InputError("mmpp2_fitc_approx: a target characteristic is zero, so the relative "
176 "objective of the reference is undefined");
177
178 const bool same_scale = t1 == t2;
179 const std::size_t nres = same_scale ? 4 : 5;
180
181 auto resid = [a, bt1, bt2, binf, m3t2, t1, t2, same_scale, one](const std::vector<T>& x) {
182 const fitdetail::Mmpp2Chars<T> c =
183 fitdetail::mmpp2_chars(x[0], x[1], x[2], x[3], a, t1, t2, same_scale);
184 std::vector<T> r;
185 r.push_back(T(c.xa / a - one));
186 r.push_back(T(c.xbt1 / bt1 - one));
187 if (!same_scale) r.push_back(T(c.xbt2 / bt2 - one));
188 r.push_back(T(c.xbinf / binf - one));
189 r.push_back(T(c.xm3t2 / m3t2 - one));
190 return r;
191 };
192
193 // the reference's prob.Constraints: l1, l2 >= 1e-6 and r1, r2 >= 0
194 const T lmin = num_traits<T>::from_double(1e-6);
195 auto g = [lmin, zero](const std::vector<T>& x) {
196 std::vector<T> gv(4);
197 gv[0] = lmin - x[0];
198 gv[1] = lmin - x[1];
199 gv[2] = zero - x[2];
200 gv[3] = zero - x[3];
201 return gv;
202 };
203
204 std::vector<T> x0(4);
205 x0[0] = a * num_traits<T>::from_rational(3, 4); // the reference's guess
206 x0[1] = a * num_traits<T>::from_rational(3, 2);
207 x0[2] = num_traits<T>::from_rational(1, 3);
208 x0[3] = num_traits<T>::from_rational(2, 3);
209
210 const AugLagResult<T> sol = auglag_ls(resid, nres, NoConstraints<T>(), g, x0, opt);
211
212 const T l1 = sol.x[0];
213 const T l2 = sol.x[1];
214 const T r1 = sol.x[2];
215 const T r2 = sol.x[3];
216
217 Map<T> m;
218 m.D0 = Matrix<T>(2, 2, zero);
219 m.D1 = Matrix<T>(2, 2, zero);
220 m.D0(0, 0) = -(l1 + r1);
221 m.D0(0, 1) = r1;
222 m.D0(1, 0) = r2;
223 m.D0(1, 1) = -(l2 + r2);
224 m.D1(0, 0) = l1;
225 m.D1(1, 1) = l2;
226
228 res.map = map_scale(m, T(one / a)); // force the rate to a, as the reference does
229 res.objective = sol.fval;
230 res.violation = sol.violation;
231 res.converged = sol.violation <= opt.ctol;
232 return res;
233}
234
235/** mmpp2_fitc_approx with the default tuning. */
236template <class T>
237Mmpp2FitcApproxResult<T> mmpp2_fitc_approx(const T& a, const T& bt1, const T& bt2, const T& binf,
238 const T& m3t2, const T& t1, const T& t2) {
240 opt.ctol = num_traits<T>::from_double(1e-12);
241 return mmpp2_fitc_approx(a, bt1, bt2, binf, m3t2, t1, t2, opt);
242}
243
244} // namespace mam
245} // namespace line
246
247#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:105
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.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
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