LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
m3pp2m_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_M3PP2M_FITC_APPROX_H
6#define LINE_API_MAM_M3PP2M_FITC_APPROX_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * M3PP(2, m) fitted to counting-process characteristics with an optimized
12 * per-class split
13 * (matlab/lib/m3a/m3a/m3pp/m3pp2m_fitc_approx.m,
14 * matlab/lib/m3a/m3a/m3pp/m3pp2m_fitc_approx_ag.m,
15 * matlab/lib/m3a/m3a/m3pp/m3pp2m_fitc_approx_ag_multiclass.m).
16 *
17 * The underlying MMPP(2) comes from mmpp2_fitc_approx. The per-class split is
18 * then a pair of per-phase marking probabilities (q1i, q2i) per class, each an
19 * AFFINE function of the class rate ai (matched exactly) and of one free
20 * per-class quantity:
21 * - the non-'ag' variant frees dvi, the difference between the variance of
22 * class i and that of all other classes combined at resolution t3;
23 * - the 'ag' variant frees gi, the sum of the variance of class i and its
24 * covariance with all other classes combined at t3.
25 * Since m3pp2m_fitc.h can match only m - 1 of those exactly (the last class
26 * absorbs the remainder), the reference instead asks for the least-squares
27 * compromise over ALL m classes, subject to the marking probabilities being
28 * non-negative and summing to one in each phase. That is a quadratic program:
29 *
30 * min sum_i (x_i / target_i - 1)^2
31 * s.t. q1i(x_i) >= 0, q2i(x_i) >= 0 for every class i
32 * sum_i q1i(x_i) = 1, sum_i q2i(x_i) = 1
33 *
34 * (the reference writes the objective as 1/2 x'Hx + f'x with H = diag(2/t_i^2)
35 * and f_i = -2/t_i, which is the expansion of the sum above minus the constant
36 * m; its reported fit_error = fx + m is therefore exactly the sum above, and
37 * that is what M3pp2mFitcApproxResult::class_objective holds).
38 *
39 * Because the two coefficients q1i_dvi and q2i_dvi do not depend on i, both
40 * equality rows constrain only sum_i x_i; they have rank one and, as the
41 * algebra of the reference guarantees, a consistent right-hand side, so the
42 * program is a single equality plus 2m inequalities.
43 *
44 * ACCEPTANCE CONTRACT (see line/util/auglag.h). MATLAB solves the QP with
45 * quadprog (interior-point-convex) or the bundled Goldfarb-Idnani QP; the JAR
46 * uses OSQP. This port uses the augmented Lagrangian with a
47 * Levenberg-Marquardt inner solver on the residual (x_i/target_i - 1). The
48 * iterates do not match any of them. What is guaranteed and tested is the
49 * specification:
50 * 1. the returned MMAP is valid -- sum_i Dc_i = D1 and every Dc_i
51 * non-negative to the toolbox feasibility tolerance 1e-8, and the
52 * underlying (D0, D1) passes map_isfeasible (see mmap_isfeasible_tol);
53 * 2. the per-class rates are matched exactly, by construction;
54 * 3. the achieved objective (the reference's fit_error) is returned so it
55 * can be compared against any other QP solver's on the same input;
56 * 4. when the requested per-class targets are those of an actual M3PP(2, m),
57 * the objective reaches zero and the achieved targets, recomputed
58 * independently with mmap_count_var, reproduce the requested ones.
59 *
60 * The box is UNCONSTRAINED by default (lb = -inf), as in both MATLAB files.
61 * The free variables are variance DIFFERENCES, routinely negative, and the
62 * equality row fixes their sum, which is negative on ordinary inputs: for the
63 * MMPP(2) l1 = 2, l2 = 0.5, r1 = 0.3, r2 = 0.7 at t3 = 1 it forces
64 * sum_i x_i = -1.8976, so a positive lower bound empties the feasible set.
65 * Optional bounds are exposed for a caller that wants them.
66 *
67 * DIVERGENCE FROM THE JAR: M3pp2m_fitc_approx.java and
68 * M3pp2m_fitc_approx_ag_multiclass.java add 2m further inequality rows
69 * (q1i <= 1, q2i <= 1) that neither MATLAB file has, and then project the
70 * solution post hoc with max(0, q) followed by a row renormalization. The
71 * extra rows can make a program infeasible that MATLAB solves, and the
72 * projection silently changes the fitted per-class rates. This port follows
73 * MATLAB: 2m rows, no post-hoc projection, and the infeasibility of the result
74 * reported through M3pp2mFitcApproxResult::feasible rather than repaired.
75 *
76 * Gated on transcendental arithmetic.
77 */
78
79#include <cstddef>
80#include <vector>
81
87#include "line/num/number.h"
88#include "line/util/auglag.h"
89#include "line/util/error.h"
90#include "line/util/matrix.h"
91
92namespace line {
93namespace mam {
94
95/** Result of the optimization-based M3PP(2, m) fits. */
96template <class T>
98 Mmap<T> mmap; ///< the fitted M3PP(2, m)
99 T mmpp_objective; ///< objective of the underlying MMPP(2) fit (zero when it was given)
100 T class_objective; ///< sum_i (x_i/target_i - 1)^2, the reference's fit_error
101 T class_violation; ///< worst constraint violation of the per-class QP
102 bool degenerate; ///< the underlying process was a Poisson process
103 bool feasible; ///< mmap_isfeasible_tol of the result at 1e-8
104};
105
106namespace fitdetail {
107
108/** The six affine coefficients of the per-class marking probabilities. */
109template <class T>
110struct M3ppSplitCoeffs {
111 T q1_a; ///< coefficient of ai in q1i
112 T q1_x; ///< coefficient of the free variable in q1i
113 T q1_c; ///< constant term of q1i
114 T q2_a;
115 T q2_x;
116 T q2_c;
117};
118
119/**
120 * Coefficients of the non-'ag' split (m3pp2m_fitc_approx.m), the free variable
121 * being the per-class variance difference at t3. The reference's repeated
122 * product sinh(u) exp(-u) with u = (r1 + r2) t / 2 is (1 - exp(-(r1+r2) t))/2
123 * exactly; it is evaluated in that form here, as in m3pp2m_fitc.h.
124 */
125template <class T>
126M3ppSplitCoeffs<T> m3pp_split_coeffs_dv(const T& l1, const T& l2, const T& r1, const T& r2,
127 const T& t) {
128 const T one = num_traits<T>::from_int(1);
129 const T two = num_traits<T>::from_int(2);
130 const T three = num_traits<T>::from_int(3);
131 const T four = num_traits<T>::from_int(4);
132 const T SH = (one - num_exp(T(-(r1 + r2) * t))) / two;
133
134 const T BR = two * l1 * SH - two * l2 * SH - l1 * r1 * t - l1 * r2 * t + l2 * r1 * t +
135 l2 * r2 * t;
136 const T DEN1 = l1 * r2 * (r1 + r2) * BR;
137 const T BR2 = l2 * l2 * r1 * r1 * t - two * l2 * l2 * r1 * SH - l1 * l2 * r1 * r1 * t +
138 l2 * l2 * r1 * r2 * t + two * l1 * l2 * r1 * SH - l1 * l2 * r1 * r2 * t;
139 const T DEN2 = (r1 + r2) * BR2;
140
141 const T P4 = pw(r1, 4) + four * pw(r1, 3) * r2 + num_traits<T>::from_int(6) * r1 * r1 * r2 * r2 +
142 four * r1 * pw(r2, 3) + pw(r2, 4);
143 const T CN = l1 * pw(r2, 4) * t + l2 * pw(r1, 4) * t + three * l1 * r1 * pw(r2, 3) * t +
144 l1 * pw(r1, 3) * r2 * t + l2 * r1 * pw(r2, 3) * t + three * l2 * pw(r1, 3) * r2 * t +
145 three * l1 * r1 * r1 * r2 * r2 * t + two * l1 * l1 * r1 * r2 * r2 * t +
146 two * l1 * l1 * r1 * r1 * r2 * t + three * l2 * r1 * r1 * r2 * r2 * t +
147 two * l2 * l2 * r1 * r2 * r2 * t + two * l2 * l2 * r1 * r1 * r2 * t -
148 four * l1 * l1 * r1 * r2 * SH - four * l2 * l2 * r1 * r2 * SH -
149 four * l1 * l2 * r1 * r2 * r2 * t - four * l1 * l2 * r1 * r1 * r2 * t +
150 num_traits<T>::from_int(8) * l1 * l2 * r1 * r2 * SH;
151
152 M3ppSplitCoeffs<T> q;
153 q.q1_a = (pw(r1, 4) * t / two + pw(r2, 4) * t / two - l1 * pw(r2, 3) * t +
154 l2 * pw(r2, 3) * t + two * r1 * pw(r2, 3) * t + two * pw(r1, 3) * r2 * t +
155 three * r1 * r1 * r2 * r2 * t + two * l1 * r2 * r2 * SH - two * l2 * r2 * r2 * SH -
156 two * l1 * r1 * r2 * r2 * t - l1 * r1 * r1 * r2 * t + two * l2 * r1 * r2 * r2 * t +
157 l2 * r1 * r1 * r2 * t + two * l1 * r1 * r2 * SH - two * l2 * r1 * r2 * SH) /
158 DEN1;
159 q.q1_x = -P4 / (four * DEN1);
160 q.q1_c = -CN / (four * DEN1);
161 q.q2_a = -(pw(r1, 4) * t / two + pw(r2, 4) * t / two + l1 * pw(r1, 3) * t -
162 l2 * pw(r1, 3) * t + two * r1 * pw(r2, 3) * t + two * pw(r1, 3) * r2 * t +
163 three * r1 * r1 * r2 * r2 * t - two * l1 * r1 * r1 * SH + two * l2 * r1 * r1 * SH +
164 l1 * r1 * r2 * r2 * t + two * l1 * r1 * r1 * r2 * t - l2 * r1 * r2 * r2 * t -
165 two * l2 * r1 * r1 * r2 * t - two * l1 * r1 * r2 * SH + two * l2 * r1 * r2 * SH) /
166 DEN2;
167 q.q2_x = P4 / (four * DEN2);
168 q.q2_c = CN / (four * DEN2);
169 return q;
170}
171
172/**
173 * Coefficients of the 'ag' split (m3pp2m_fitc_approx_ag_multiclass.m), the
174 * free variable being the per-class variance-plus-covariance at t3. Both
175 * constant terms are zero there.
176 */
177template <class T>
178M3ppSplitCoeffs<T> m3pp_split_coeffs_ag(const T& l1, const T& l2, const T& r1, const T& r2,
179 const T& t) {
180 const T zero = num_traits<T>::from_int(0);
181 const T two = num_traits<T>::from_int(2);
182 const T three = num_traits<T>::from_int(3);
183 const T E = num_exp(T(-(r1 + r2) * t));
184
185 const T f1num = l1 * r2 *
186 (two * l2 * r1 - two * l1 * r1 + pw(r1, 3) * t + pw(r2, 3) * t +
187 two * l1 * r1 * r1 * t - two * l2 * r1 * r1 * t + three * r1 * r2 * r2 * t +
188 three * r1 * r1 * r2 * t + two * l1 * r1 * E - two * l2 * r1 * E +
189 two * l1 * r1 * r2 * t - two * l2 * r1 * r2 * t);
190 const T f1 = f1num / pw(T(r1 + r2), 4);
191 const T f2num = l2 * r1 *
192 (two * l1 * r2 - two * l2 * r2 + pw(r1, 3) * t + pw(r2, 3) * t -
193 two * l1 * r2 * r2 * t + two * l2 * r2 * r2 * t + three * r1 * r2 * r2 * t +
194 three * r1 * r1 * r2 * t - two * l1 * r2 * E + two * l2 * r2 * E -
195 two * l1 * r1 * r2 * t + two * l2 * r1 * r2 * t);
196 const T f2 = f2num / pw(T(r1 + r2), 4);
197 const T tmp = f1 * l2 * r1 - f2 * l1 * r2;
198 if (tmp == zero)
199 throw NumericError(ErrorKind{"Degenerate"}, "m3pp2m_fitc_approx_ag: degenerate per-class split (f1 l2 r1 = f2 l1 r2)");
200
201 M3ppSplitCoeffs<T> q;
202 q.q1_a = -(f2 * (r1 + r2)) / tmp;
203 q.q1_x = (l2 * r1) / tmp;
204 q.q1_c = zero;
205 q.q2_a = (f1 * (r1 + r2)) / tmp;
206 q.q2_x = -(l1 * r2) / tmp;
207 q.q2_c = zero;
208 return q;
209}
210
211/**
212 * Solve the per-class quadratic program described in the header comment.
213 *
214 * @param q the six affine coefficients
215 * @param ai per-class rates
216 * @param target per-class targets (dvt3 or gt3); none may be zero, since the
217 * objective is relative to them
218 * @param a total rate
219 * @param bounds optional box on the free variables; pass an empty vector for
220 * the unconstrained default (see the reference-defect note)
221 * @param opt tuning of the constrained solve
222 * @param objective out: sum_i (x_i/target_i - 1)^2 at the answer
223 * @param violation out: worst constraint violation at the answer
224 */
225template <class T>
226std::vector<T> m3pp_split_solve(const M3ppSplitCoeffs<T>& q, const std::vector<T>& ai,
227 const std::vector<T>& target, const T& a,
228 const std::vector<Bound<T>>& bounds, const AugLagOptions<T>& opt,
229 T& objective, T& violation) {
230 const T zero = num_traits<T>::from_int(0);
231 const T one = num_traits<T>::from_int(1);
232 const std::size_t m = ai.size();
233 if (target.size() != m) throw InputError("m3pp_split_solve: target length disagrees with ai");
234 for (std::size_t i = 0; i < m; ++i)
235 if (target[i] == zero)
236 throw InputError("m3pp_split_solve: a per-class target is zero, so the relative "
237 "objective of the reference is undefined");
238 if (!bounds.empty() && bounds.size() != m)
239 throw InputError("m3pp_split_solve: one bound per class is required");
240
241 const std::vector<T> tgt = target;
242 auto resid = [tgt, m, one](const std::vector<T>& x) {
243 std::vector<T> r(m);
244 for (std::size_t i = 0; i < m; ++i) r[i] = x[i] / tgt[i] - one;
245 return r;
246 };
247
248 const std::vector<T> rates = ai;
249 const M3ppSplitCoeffs<T> qc = q;
250 const T mT = num_traits<T>::from_int(long(m));
251 auto heq = [qc, rates, a, m, mT, one](const std::vector<T>& x) {
252 T s = num_traits<T>::from_int(0);
253 for (std::size_t i = 0; i < m; ++i) s += x[i];
254 std::vector<T> hv(2);
255 hv[0] = qc.q1_a * a + qc.q1_x * s + mT * qc.q1_c - one;
256 hv[1] = qc.q2_a * a + qc.q2_x * s + mT * qc.q2_c - one;
257 return hv;
258 };
259 const std::vector<Bound<T>> bnd = bounds;
260 auto gineq = [qc, rates, m, bnd](const std::vector<T>& x) {
261 std::vector<T> gv;
262 for (std::size_t i = 0; i < m; ++i) {
263 // q1i >= 0 and q2i >= 0, the reference's A x <= b
264 gv.push_back(T(-(qc.q1_a * rates[i] + qc.q1_x * x[i] + qc.q1_c)));
265 gv.push_back(T(-(qc.q2_a * rates[i] + qc.q2_x * x[i] + qc.q2_c)));
266 }
267 for (std::size_t i = 0; i < bnd.size(); ++i) {
268 if (bnd[i].has_lo) gv.push_back(T(bnd[i].lo - x[i]));
269 if (bnd[i].has_hi) gv.push_back(T(x[i] - bnd[i].hi));
270 }
271 return gv;
272 };
273
274 // start from the requested targets themselves, the point of zero objective
275 std::vector<T> x0 = target;
276 const AugLagResult<T> sol = auglag_ls(resid, m, heq, gineq, x0, opt);
277 objective = sol.fval;
278 violation = sol.violation;
279 return sol.x;
280}
281
282} // namespace fitdetail
283
284/**
285 * Assemble the M3PP from an underlying MAP and the per-phase marking
286 * probabilities. The last class is NOT special-cased: every class gets the
287 * probability the QP assigned it, and the equality rows are what make the
288 * columns sum to D1.
289 */
290template <class T>
291Mmap<T> m3pp2m_assemble(const Map<T>& base, const std::vector<T>& q1, const std::vector<T>& q2) {
292 const T zero = num_traits<T>::from_int(0);
293 if (q1.size() != q2.size()) throw InputError("m3pp2m_assemble: q1 and q2 differ in length");
294 Mmap<T> mm;
295 mm.D0 = base.D0;
296 mm.D1 = base.D1;
297 for (std::size_t i = 0; i < q1.size(); ++i) {
298 Matrix<T> Dc(2, 2, zero);
299 Dc(0, 0) = q1[i] * base.D1(0, 0);
300 Dc(1, 1) = q2[i] * base.D1(1, 1);
301 mm.Dc.push_back(Dc);
302 }
303 return mm;
304}
305
306namespace fitdetail {
307
308/** Shared body of the two multiclass splits, once the coefficients are known. */
309template <class T>
310M3pp2mFitcApproxResult<T> m3pp2m_split_and_assemble(const Map<T>& base,
311 const M3ppSplitCoeffs<T>& q,
312 const std::vector<T>& ai,
313 const std::vector<T>& target, const T& a,
314 const std::vector<Bound<T>>& bounds,
315 const AugLagOptions<T>& opt) {
316 const std::size_t m = ai.size();
317 M3pp2mFitcApproxResult<T> res;
318 res.degenerate = false;
319 res.mmpp_objective = num_traits<T>::from_int(0);
320
321 T obj = num_traits<T>::from_int(0);
322 T viol = num_traits<T>::from_int(0);
323 const std::vector<T> x = m3pp_split_solve(q, ai, target, a, bounds, opt, obj, viol);
324
325 std::vector<T> q1(m), q2(m);
326 for (std::size_t i = 0; i < m; ++i) {
327 q1[i] = q.q1_a * ai[i] + q.q1_x * x[i] + q.q1_c;
328 q2[i] = q.q2_a * ai[i] + q.q2_x * x[i] + q.q2_c;
329 }
330 res.mmap = m3pp2m_assemble(base, q1, q2);
331 res.class_objective = obj;
332 res.class_violation = viol;
333 res.feasible = mmap_isfeasible_tol(res.mmap, T(num_traits<T>::from_double(1e-8)));
334 return res;
335}
336
337/** The Poisson and single-class short-circuits shared by all three entry points. */
338template <class T>
339bool m3pp2m_trivial_split(const Map<T>& base, const std::vector<T>& ai, const T& a,
340 M3pp2mFitcApproxResult<T>& res) {
341 const std::size_t m = ai.size();
342 if (base.D0.rows() == 1) {
343 // marked Poisson process: split D1 in proportion to the class rates
344 res.degenerate = true;
345 res.mmap.D0 = base.D0;
346 res.mmap.D1 = base.D1;
347 for (std::size_t i = 0; i < m; ++i) {
348 Matrix<T> Dc(1, 1, T(ai[i] / a * base.D1(0, 0)));
349 res.mmap.Dc.push_back(Dc);
350 }
351 res.class_objective = num_traits<T>::from_int(0);
352 res.class_violation = num_traits<T>::from_int(0);
353 res.feasible = mmap_isfeasible_tol(res.mmap, T(num_traits<T>::from_double(1e-8)));
354 return true;
355 }
356 if (m == 1) {
357 res.mmap.D0 = base.D0;
358 res.mmap.D1 = base.D1;
359 res.mmap.Dc.push_back(base.D1);
360 res.class_objective = num_traits<T>::from_int(0);
361 res.class_violation = num_traits<T>::from_int(0);
362 res.feasible = mmap_isfeasible_tol(res.mmap, T(num_traits<T>::from_double(1e-8)));
363 return true;
364 }
365 return false;
366}
367
368} // namespace fitdetail
369
370/**
371 * m3pp2m_fitc_approx: fit the underlying MMPP(2) by optimization, then split
372 * the classes on their variance DIFFERENCES at t3.
373 *
374 * @param a,bt1,bt2,binf,m3t2,t1,t2 the aggregate counting characteristics
375 * @param ai per-class rates, which must sum to a
376 * @param dvt3 per-class variance differences at t3
377 * @param t3 the third time scale
378 * @param bounds optional box on the free variables; empty for none, which is
379 * the default (see the reference-defect note in the header)
380 * @param opt tuning of the constrained solve
381 */
382template <class T>
383M3pp2mFitcApproxResult<T> m3pp2m_fitc_approx(const T& a, const T& bt1, const T& bt2, const T& binf,
384 const T& m3t2, const T& t1, const T& t2,
385 const std::vector<T>& ai, const std::vector<T>& dvt3,
386 const T& t3, const std::vector<Bound<T>>& bounds,
387 const AugLagOptions<T>& opt) {
389 "m3pp2m_fitc_approx requires transcendental arithmetic");
390 const T zero = num_traits<T>::from_int(0);
391 const std::size_t m = ai.size();
392 if (m == 0) throw InputError("m3pp2m_fitc_approx: no classes");
393 T asum = zero;
394 for (std::size_t i = 0; i < m; ++i) asum += ai[i];
395 if (num_abs(T(a - asum)) > num_traits<T>::from_double(1e-8))
396 throw InputError("m3pp2m_fitc_approx: inconsistent per-class arrival rates");
397
398 const Mmpp2FitcApproxResult<T> base = mmpp2_fitc_approx(a, bt1, bt2, binf, m3t2, t1, t2, opt);
399
401 res.degenerate = false;
402 res.mmpp_objective = base.objective;
403 if (fitdetail::m3pp2m_trivial_split(base.map, ai, a, res)) return res;
404
405 const fitdetail::M3ppSplitCoeffs<T> q = fitdetail::m3pp_split_coeffs_dv(
406 base.map.D1(0, 0), base.map.D1(1, 1), base.map.D0(0, 1), base.map.D0(1, 0), t3);
408 fitdetail::m3pp2m_split_and_assemble(base.map, q, ai, dvt3, a, bounds, opt);
409 out.mmpp_objective = base.objective;
410 return out;
411}
412
413/** m3pp2m_fitc_approx with no box on the free variables and the default tuning. */
414template <class T>
415M3pp2mFitcApproxResult<T> m3pp2m_fitc_approx(const T& a, const T& bt1, const T& bt2, const T& binf,
416 const T& m3t2, const T& t1, const T& t2,
417 const std::vector<T>& ai, const std::vector<T>& dvt3,
418 const T& t3) {
420 opt.ctol = num_traits<T>::from_double(1e-12);
421 return m3pp2m_fitc_approx(a, bt1, bt2, binf, m3t2, t1, t2, ai, dvt3, t3,
422 std::vector<Bound<T>>(), opt);
423}
424
425/**
426 * m3pp2m_fitc_approx_ag_multiclass: split a GIVEN MMPP(2) into m classes on
427 * their variance-plus-covariance at t3.
428 *
429 * @param mmpp the underlying MAP, of order 1 (Poisson) or 2
430 * @param ai per-class rates, which must sum to the rate of mmpp
431 * @param gt3 per-class variance-plus-covariance targets at t3
432 * @param t3 the third time scale
433 * @param bounds optional box on the free variables; empty for none
434 * @param opt tuning of the constrained solve
435 */
436template <class T>
438 const std::vector<T>& ai,
439 const std::vector<T>& gt3, const T& t3,
440 const std::vector<Bound<T>>& bounds,
441 const AugLagOptions<T>& opt) {
443 "m3pp2m_fitc_approx_ag_multiclass requires transcendental arithmetic");
444 const T zero = num_traits<T>::from_int(0);
445 const std::size_t m = ai.size();
446 if (m == 0) throw InputError("m3pp2m_fitc_approx_ag_multiclass: no classes");
447 const T a = map_lambda(mmpp);
448 T asum = zero;
449 for (std::size_t i = 0; i < m; ++i) asum += ai[i];
450 if (num_abs(T(a - asum)) > num_traits<T>::from_double(1e-8))
451 throw InputError("m3pp2m_fitc_approx_ag_multiclass: inconsistent per-class arrival rates");
452
454 res.degenerate = false;
455 res.mmpp_objective = zero;
456 if (fitdetail::m3pp2m_trivial_split(mmpp, ai, a, res)) return res;
457 if (mmpp.D0.rows() != 2)
458 throw InputError("m3pp2m_fitc_approx_ag_multiclass: the underlying MAP must have order 2");
459
460 const fitdetail::M3ppSplitCoeffs<T> q = fitdetail::m3pp_split_coeffs_ag(
461 mmpp.D1(0, 0), mmpp.D1(1, 1), mmpp.D0(0, 1), mmpp.D0(1, 0), t3);
462 return fitdetail::m3pp2m_split_and_assemble(mmpp, q, ai, gt3, a, bounds, opt);
463}
464
465/** m3pp2m_fitc_approx_ag_multiclass with no box and the default tuning. */
466template <class T>
468 const std::vector<T>& ai,
469 const std::vector<T>& gt3,
470 const T& t3) {
472 opt.ctol = num_traits<T>::from_double(1e-12);
473 return m3pp2m_fitc_approx_ag_multiclass(mmpp, ai, gt3, t3, std::vector<Bound<T>>(), opt);
474}
475
476/**
477 * m3pp2m_fitc_approx_ag: fit the underlying MMPP(2) by optimization, then
478 * apply the 'ag' per-class split.
479 *
480 * @param a,bt1,bt2,binf,m3t2,t1,t2 the aggregate counting characteristics
481 * @param ai per-class rates, which must sum to a
482 * @param gt3 per-class variance-plus-covariance targets at t3
483 * @param t3 the third time scale
484 * @param bounds optional box on the free variables; empty for none
485 * @param opt tuning of the constrained solve
486 */
487template <class T>
488M3pp2mFitcApproxResult<T> m3pp2m_fitc_approx_ag(const T& a, const T& bt1, const T& bt2,
489 const T& binf, const T& m3t2, const T& t1,
490 const T& t2, const std::vector<T>& ai,
491 const std::vector<T>& gt3, const T& t3,
492 const std::vector<Bound<T>>& bounds,
493 const AugLagOptions<T>& opt) {
495 "m3pp2m_fitc_approx_ag requires transcendental arithmetic");
496 const T zero = num_traits<T>::from_int(0);
497 T asum = zero;
498 for (std::size_t i = 0; i < ai.size(); ++i) asum += ai[i];
499 if (num_abs(T(a - asum)) > num_traits<T>::from_double(1e-8))
500 throw InputError("m3pp2m_fitc_approx_ag: inconsistent per-class arrival rates");
501
502 const Mmpp2FitcApproxResult<T> base = mmpp2_fitc_approx(a, bt1, bt2, binf, m3t2, t1, t2, opt);
504 m3pp2m_fitc_approx_ag_multiclass(base.map, ai, gt3, t3, bounds, opt);
505 res.mmpp_objective = base.objective;
506 return res;
507}
508
509/** m3pp2m_fitc_approx_ag with no box and the default tuning. */
510template <class T>
511M3pp2mFitcApproxResult<T> m3pp2m_fitc_approx_ag(const T& a, const T& bt1, const T& bt2,
512 const T& binf, const T& m3t2, const T& t1,
513 const T& t2, const std::vector<T>& ai,
514 const std::vector<T>& gt3, const T& t3) {
516 opt.ctol = num_traits<T>::from_double(1e-12);
517 return m3pp2m_fitc_approx_ag(a, bt1, bt2, binf, m3t2, t1, t2, ai, gt3, t3,
518 std::vector<Bound<T>>(), opt);
519}
520
521} // namespace mam
522} // namespace line
523
524#endif // LINE_API_MAM_M3PP2M_FITC_APPROX_H
Augmented Lagrangian method for equality- and inequality-constrained minimization,...
InputError(const std::string &what)
Definition error.h:105
NumericError(const std::string &what)
Definition error.h:112
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.
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
MMPP(2) fitted to counting-process characteristics by optimization (matlab/lib/kpctoolbox/mmpp/mmpp2_...
M3pp2mFitcApproxResult< T > m3pp2m_fitc_approx_ag(const T &a, const T &bt1, const T &bt2, const T &binf, const T &m3t2, const T &t1, const T &t2, const std::vector< T > &ai, const std::vector< T > &gt3, const T &t3, const std::vector< Bound< T > > &bounds, const AugLagOptions< T > &opt)
m3pp2m_fitc_approx_ag: fit the underlying MMPP(2) by optimization, then apply the 'ag' per-class spli...
bool mmap_isfeasible_tol(const Mmap< T > &m, const T &tol)
Feasibility of a marked MAP WITHIN A TOLERANCE, the semantics of matlab/lib/m3a/m3a/mmap/mmap_isfeasi...
M3pp2mFitcApproxResult< T > m3pp2m_fitc_approx_ag_multiclass(const Map< T > &mmpp, const std::vector< T > &ai, const std::vector< T > &gt3, const T &t3, const std::vector< Bound< T > > &bounds, const AugLagOptions< T > &opt)
m3pp2m_fitc_approx_ag_multiclass: split a GIVEN MMPP(2) into m classes on their variance-plus-covaria...
Mmap< T > m3pp2m_assemble(const Map< T > &base, const std::vector< T > &q1, const std::vector< T > &q2)
Assemble the M3PP from an underlying MAP and the per-phase marking probabilities.
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.
M3pp2mFitcApproxResult< T > m3pp2m_fitc_approx(const T &a, const T &bt1, const T &bt2, const T &binf, const T &m3t2, const T &t1, const T &t2, const std::vector< T > &ai, const std::vector< T > &dvt3, const T &t3, const std::vector< Bound< T > > &bounds, const AugLagOptions< T > &opt)
m3pp2m_fitc_approx: fit the underlying MMPP(2) by optimization, then split the classes on their varia...
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
T num_abs(const T &v)
Definition number.h:184
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
Box constraint on one variable.
Definition neldermead.h:110
Result of the optimization-based M3PP(2, m) fits.
Mmap< T > mmap
the fitted M3PP(2, m)
T class_objective
sum_i (x_i/target_i - 1)^2, the reference's fit_error
bool feasible
mmap_isfeasible_tol of the result at 1e-8
T mmpp_objective
objective of the underlying MMPP(2) fit (zero when it was given)
bool degenerate
the underlying process was a Poisson process
T class_violation
worst constraint violation of the per-class QP
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
An MMAP: the underlying MAP plus the per-class arrival matrices.
Definition mmap_lambda.h:45
Matrix< T > D0
Definition mmap_lambda.h:46
Matrix< T > D1
Definition mmap_lambda.h:47
std::vector< Matrix< T > > Dc
per-class matrices, sum_c Dc = D1
Definition mmap_lambda.h:48
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