5#ifndef LINE_API_MAM_MAMAP2M_FIT_H
6#define LINE_API_MAM_MAMAP2M_FIT_H
93namespace mamapdetail {
100template <
class T,
class QFun>
101std::vector<T> solve_split(
const std::vector<T>& target,
const std::vector<T>& w,
102 std::size_t nflows, std::size_t k, QFun qof) {
104 const std::size_t nv = target.size();
106 auto fobj = [&](
const std::vector<T>& x) {
108 for (std::size_t v = 0; v < nv; ++v) {
109 const T r = T(x[v] / target[v] - one);
114 auto heq = [&](
const std::vector<T>& x) {
115 std::vector<T> v(nflows, zero);
116 for (std::size_t j = 0; j < nflows; ++j) {
118 for (std::size_t c = 0; c < k; ++c) s += qof(x, j, c);
123 auto gineq = [&](
const std::vector<T>& x) {
125 v.reserve(2 * nflows * k);
126 for (std::size_t c = 0; c < k; ++c)
127 for (std::size_t j = 0; j < nflows; ++j) {
128 const T qq = qof(x, j, c);
129 v.push_back(T(qq - one));
135 std::vector<T> x0 = target;
136 std::vector<Bound<T>> bounds(nv);
137 for (std::size_t v = 0; v < nv; ++v) {
140 if (x0[v] < bounds[v].lo) x0[v] = bounds[v].lo;
141 if (x0[v] > bounds[v].hi) x0[v] = bounds[v].hi;
143 return auglag(fobj, heq, gineq, x0, bounds).x;
148void fix_split(std::vector<std::vector<T>>& q, std::size_t k) {
150 for (std::size_t j = 0; j < q.size(); ++j) {
152 for (std::size_t c = 0; c < k; ++c) {
153 if (q[j][c] < zero) q[j][c] = zero;
156 if (!(num_traits<T>::to_double(s) > 0.0))
157 throw NumericError(
"mamap2m_fit: a flow split lost all its mass");
158 for (std::size_t c = 0; c < k; ++c) q[j][c] = T(q[j][c] / s);
177 const std::vector<T>& F,
const std::vector<T>& B,
178 const std::vector<T>& classWeights = std::vector<T>(),
179 const std::vector<T>& fbWeights = std::vector<T>()) {
181 "mamap2m_fit_fb_multiclass solves a quadratic program");
184 if (map.
D0.rows() != 2)
185 throw InputError(
"mamap2m_fit_fb_multiclass: the underlying MAP must be second order");
187 throw InputError(
"mamap2m_fit_fb_multiclass: the underlying MAP must be acyclic");
190 if (map.
D1(0, 1) == zero)
192 else if (map.
D1(0, 0) == zero)
196 "mamap2m_fit_fb_multiclass: the underlying MAP must be in canonical acyclic form "
197 "(D1(1,2) = 0 for a positive decay, D1(1,1) = 0 for a negative one)");
199 const std::size_t k = p.size();
200 if (k == 0)
throw InputError(
"mamap2m_fit_fb_multiclass: no classes given");
201 if (F.size() != k || B.size() != k)
203 "mamap2m_fit_fb_multiclass: one forward and one backward moment per class is required");
204 std::vector<T> cw = classWeights;
205 if (cw.empty()) cw.assign(k, one);
206 std::vector<T> fbw = fbWeights;
207 if (fbw.empty()) fbw.assign(2, one);
209 const T h1 = T(-one / map.
D0(0, 0));
210 const T h2 = T(-one / map.
D0(1, 1));
211 const T r1 = T(map.
D0(0, 1) * h1);
212 const T r2 = T(map.
D1(1, 1) * h2);
213 const double dt = 1e-8;
222 auto finish = [&](std::vector<std::vector<T>>& q) {
223 mamapdetail::fix_split(q, k);
224 for (std::size_t c = 0; c < k; ++c) {
226 out.
mmap.Dc[c](0, 0) = T(out.
mmap.D1(0, 0) * q[0][c]);
227 out.
mmap.Dc[c](1, 0) = T(out.
mmap.D1(1, 0) * q[1][c]);
228 out.
mmap.Dc[c](1, 1) = T(out.
mmap.D1(1, 1) * q[2][c]);
230 out.
mmap.Dc[c](0, 1) = T(out.
mmap.D1(0, 1) * q[0][c]);
231 out.
mmap.Dc[c](1, 0) = T(out.
mmap.D1(1, 0) * q[1][c]);
232 out.
mmap.Dc[c](1, 1) = T(out.
mmap.D1(1, 1) * q[2][c]);
235 const std::vector<unsigned> one_order(1, 1u);
238 out.
fF.assign(k, zero);
239 out.
fB.assign(k, zero);
240 for (std::size_t c = 0; c < k; ++c) {
241 out.
fF[c] = fm(c, 0);
242 out.
fB[c] = bm[c][0];
248 const bool poisson1 =
249 form == 1 && (dr1 < dt || dr2 > 1.0 - dt || std::fabs(dh2 - dh1 * dr2) < dt ||
250 std::fabs(dh1 - dh2 + dh2 * dr1) < dt);
251 const bool poisson2 =
252 form == 2 && (dr2 > 1.0 - dt || std::fabs(dh1 - dh2 + dh2 * dr1) < dt ||
253 std::fabs(dh1 - dh2 - dh1 * dr1 + dh1 * dr1 * dr2) < dt);
254 if (poisson1 || poisson2) {
255 out.
mmap = mamapdetail::marked_poisson(
map_mean(map), p);
256 const std::vector<unsigned> one_order(1, 1u);
259 out.
fF.assign(k, zero);
260 out.
fB.assign(k, zero);
261 for (std::size_t c = 0; c < k; ++c) {
262 out.
fF[c] = fm(c, 0);
263 out.
fB[c] = bm[c][0];
269 if (form == 2 && dr2 < dt && std::fabs(1.0 - dr1) < dt) {
270 std::vector<std::vector<T>> q(3, std::vector<T>(k, zero));
271 for (std::size_t c = 0; c < k; ++c) q[0][c] = q[1][c] = q[2][c] = p[c];
277 if (form == 1 && dr2 < dt) {
283 const std::vector<unsigned> one_order(1, 1u);
286 out.
fF.assign(k, zero);
287 out.
fB.assign(k, zero);
288 for (std::size_t c = 0; c < k; ++c) {
289 out.
fF[c] = fm(c, 0);
290 out.
fB[c] = bm[c][0];
296 if (std::fabs(1.0 - dr1) < dt) {
297 std::vector<std::vector<T>> qf(2, std::vector<T>(k, zero)),
298 q0(2, std::vector<T>(k, zero));
299 for (std::size_t c = 0; c < k; ++c) {
300 qf[0][c] = T(p[c] * (-one / ((h1 + h2 * (r1 - one)) * (r2 - one) *
301 (r1 + r2 - r1 * r2))));
302 q0[0][c] = T(p[c] * (h2 / ((r2 - one) * (r1 + r2 - r1 * r2) *
303 (h1 - h2 + h2 * r1))));
304 qf[1][c] = T(p[c] * (-one / (r2 * (h1 + h2 * (r1 - one)) * (r1 + r2 - r1 * r2))));
305 q0[1][c] = T(p[c] * ((h1 + h2 * r1) /
306 (r2 * (r1 + r2 - r1 * r2) * (h1 - h2 + h2 * r1))));
308 std::vector<T> w(k, zero);
309 for (std::size_t c = 0; c < k; ++c) w[c] = T(cw[c] * fbw[0]);
310 auto qof = [&](
const std::vector<T>& x, std::size_t j, std::size_t c) {
311 return T(x[c] * qf[j][c] + q0[j][c]);
313 const std::vector<T> x = mamapdetail::solve_split(F, w, 2, k, qof);
314 std::vector<std::vector<T>> q(3, std::vector<T>(k, zero));
316 for (std::size_t c = 0; c < k; ++c) {
318 q[1][c] = T(x[c] * qf[0][c] + q0[0][c]);
319 q[2][c] = T(x[c] * qf[1][c] + q0[1][c]);
326 if (form == 2 && dr2 < dt) {
327 const bool doForward = !(fbw[0] < fbw[1]);
328 std::vector<std::vector<T>> qc(2, std::vector<T>(k, zero)),
329 q0(2, std::vector<T>(k, zero));
330 for (std::size_t c = 0; c < k; ++c) {
332 qc[0][c] = T(p[c] * (-(r1 - two) / ((h1 + h2 * (r1 - one)) * (r1 - one))));
333 q0[0][c] = T(p[c] * (one - (h1 + h2) / ((r1 - one) * (h1 - h2 + h2 * r1))));
334 qc[1][c] = T(p[c] * (-(r1 - two) / (h1 + h2 * (r1 - one))));
335 q0[1][c] = T(p[c] * ((h2 * (r1 - two)) / (h1 - h2 + h2 * r1)));
337 qc[0][c] = T(p[c] * (-(r1 - two) / ((h2 + h1 * (r1 - one)) * (r1 - one))));
338 q0[0][c] = T(p[c] * (one - (h1 + h2) / ((r1 - one) * (h2 - h1 + h1 * r1))));
339 qc[1][c] = T(p[c] * (-(r1 - two) / (h2 + h1 * (r1 - one))));
340 q0[1][c] = T(p[c] * ((h1 * (r1 - two)) / (h2 - h1 + h1 * r1)));
343 std::vector<T> w(k, zero);
344 for (std::size_t c = 0; c < k; ++c) w[c] = T(cw[c] * (doForward ? fbw[0] : fbw[1]));
345 auto qof = [&](
const std::vector<T>& x, std::size_t j, std::size_t c) {
346 return T(x[c] * qc[j][c] + q0[j][c]);
348 const std::vector<T> x = mamapdetail::solve_split(doForward ? F : B, w, 2, k, qof);
349 std::vector<std::vector<T>> q(3, std::vector<T>(k, zero));
351 for (std::size_t c = 0; c < k; ++c) {
352 q[0][c] = T(x[c] * qc[0][c] + q0[0][c]);
353 q[1][c] = T(x[c] * qc[1][c] + q0[1][c]);
361 std::vector<std::vector<T>> qf(3, std::vector<T>(k, zero)), qb(3, std::vector<T>(k, zero)),
362 q0(3, std::vector<T>(k, zero));
363 for (std::size_t c = 0; c < k; ++c) {
365 const T z = T(r1 * r2 - r2 + one);
367 qb[0][c] = T(-(p[c] * z) / ((h2 - h1 * r2) * (r1 - one) * (r2 - one)));
368 q0[0][c] = T((p[c] * (h1 + h2 - h1 * r2) * z) /
369 ((h2 - h1 * r2) * (r1 - one) * (r2 - one)));
370 qf[1][c] = T(-(p[c] * z) / (r1 * (h1 + h2 * (r1 - one)) * (r2 - one)));
371 qb[1][c] = T(-(p[c] * z) / (r1 * (h2 - h1 * r2) * (r2 - one)));
372 q0[1][c] = T((p[c] * z) / ((r1 - one) * (r2 - one)) +
373 (h1 * p[c] * z) / (r1 * (h2 - h1 * r2) * (r2 - one)) -
374 (h1 * p[c] * z) / (r1 * (h1 + h2 * (r1 - one)) * (r1 - one) * (r2 - one)));
375 qf[2][c] = T(-(p[c] * z) / (r1 * r2 * (h1 - h2 + h2 * r1)));
377 q0[2][c] = T((p[c] * (h1 + h2 * r1) * z) / (r1 * r2 * (h1 - h2 + h2 * r1)));
379 const T z = T(r1 + r2 - r1 * r2 - two);
380 const T d2 = T(h1 - h2 - h1 * r1 + h1 * r1 * r2);
382 qb[0][c] = T(-(p[c] * z) / ((r1 - one) * (r2 - one) * d2));
383 q0[0][c] = T((p[c] * (h2 + h1 * r1 - h1 * r1 * r2) * z) /
384 ((r1 - one) * (r2 - one) * d2));
385 qf[1][c] = T((p[c] * z) / ((r2 - one) * (h1 - h2 + h2 * r1)));
387 q0[1][c] = T(-(h2 * p[c] * z) / ((r2 - one) * (h1 - h2 + h2 * r1)));
388 qf[2][c] = T((p[c] * z) / (r2 * (h1 + h2 * (r1 - one))));
389 qb[2][c] = T((p[c] * z) / (r2 * d2));
390 q0[2][c] = T((h1 * p[c] * z) / (r2 * (h1 + h2 * (r1 - one)) * (r1 - one)) -
391 (h1 * p[c] * z) / (r2 * d2) - (p[c] * z) / (r2 * (r1 - one)));
396 std::vector<T> target(2 * k, zero), w(2 * k, zero);
397 for (std::size_t c = 0; c < k; ++c) {
398 target[2 * c] = F[c];
399 target[2 * c + 1] = B[c];
400 w[2 * c] = T(cw[c] * fbw[0]);
401 w[2 * c + 1] = T(cw[c] * fbw[1]);
403 auto qof = [&](
const std::vector<T>& x, std::size_t j, std::size_t c) {
404 return T(x[2 * c] * qf[j][c] + x[2 * c + 1] * qb[j][c] + q0[j][c]);
406 const std::vector<T> x = mamapdetail::solve_split(target, w, 3, k, qof);
407 std::vector<std::vector<T>> q(3, std::vector<T>(k, zero));
408 for (std::size_t c = 0; c < k; ++c)
409 for (std::size_t j = 0; j < 3; ++j) q[j][c] = qof(x, j, c);
420 const std::vector<T>& p,
const std::vector<T>& F,
421 const std::vector<T>& B) {
423 if (a.
amaps.empty() || (a.
amaps.size() == 1 && a.
amaps[0].order() == 1))
424 return mamapdetail::marked_poisson(M1, p);
427 double bestErr = 0.0;
429 for (std::size_t j = 0; j < a.
amaps.size(); ++j) {
433 }
catch (
const Error&) {
437 for (std::size_t c = 0; c < p.size(); ++c) {
440 err += df * df + db * db;
442 if (!have || err < bestErr) {
450 "mamap2m_fit_gamma_fb: no AMAP(2) form admits a valid class split for the requested "
451 "class probabilities and forward/backward moments");
478 const std::vector<T>& p,
const std::vector<T>& F,
const std::vector<T>& B,
479 const Matrix<T>& S,
const std::vector<T>& fbsWeights = std::vector<T>()) {
481 std::vector<T> w = fbsWeights;
482 if (w.empty()) w.assign(3, one);
483 if (w.size() != 3)
throw InputError(
"mamap2m_fit: three (F, B, S) weights are required");
486 const double gammatol = 1e-4, degentol = 1e-8;
491 if (a.
amaps.empty() || (a.
amaps.size() == 1 && a.
amaps[0].order() == 1))
492 return mamapdetail::marked_poisson(M1, p);
494 const bool preferFB = !(w[0] < w[2]) && !(w[1] < w[2]);
495 const bool preferFS = !preferFB && !(w[0] < w[1]);
498 double bestErr = 0.0;
500 for (std::size_t j = 0; j < a.
amaps.size(); ++j) {
502 if (mp.
order() != 2)
continue;
503 const T h1 = T(-one / mp.
D0(0, 0)), h2 = T(-one / mp.
D0(1, 1));
504 const T r1 = T(h1 * mp.
D0(0, 1)), r2 = T(h2 * mp.
D1(1, 1));
511 bool needsFs =
false, needsBs =
false;
513 if (std::fabs(dh2 - dh1 * dr2) < degentol) needsFs =
true;
514 else if (std::fabs(dh1 - dh2 + dh2 * dr1) < degentol) needsBs =
true;
515 else if (1.0 - dr1 < degentol) needsFs =
true;
517 if (std::fabs(dh1 - dh2 - dh1 * dr1 + dh1 * dr1 * dr2) < degentol) needsFs =
true;
518 else if (std::fabs(dh1 - dh2 + dh2 * dr1) < degentol) needsBs =
true;
521 if (!needsFs && !needsBs && !preferFB) {
522 if (preferFS) needsFs =
true;
527 std::vector<T> cF, cB;
532 }
else if (needsBs) {
539 }
catch (
const Error&) {
543 const std::vector<unsigned> ord1(1, 1u);
548 for (std::size_t c = 0; c < p.size(); ++c) {
554 if (S_or_empty.
rows() > 0 && fsc.
rows() > 0) {
558 if (!have || err < bestErr) {
566 "mamap2m_fit: no AMAP(2) form admits a valid class split for the requested "
584 if (Tv.empty() || Tv.size() != A.size())
586 "mamap2m_fit_gamma_fb_trace: the trace and its labels must agree in length");
588 T m1 = zero, m2 = zero, m3 = zero;
589 for (std::size_t i = 0; i < Tv.size(); ++i) {
596 const std::vector<unsigned> one_order(1, 1u);
600 std::vector<T> F(p.size(), zero), B(p.size(), zero);
601 for (std::size_t c = 0; c < p.size(); ++c) {
627 const std::vector<T>& fbsWeights = std::vector<T>()) {
628 if (Tv.empty() || Tv.size() != A.size())
629 throw InputError(
"mamap2m_fit_trace: the trace and its labels must agree in length");
631 T m1 = zero, m2 = zero, m3 = zero;
632 for (std::size_t i = 0; i < Tv.size(); ++i) {
639 const std::vector<unsigned> one_order(1, 1u);
643 std::vector<T> F(p.size(), zero), B(p.size(), zero);
644 for (std::size_t c = 0; c < p.size(); ++c) {
650 F, B, S, fbsWeights);
AMAP(2) fit of three moments and the autocorrelation decay rate (matlab/lib/m3a/m3a/amap2/amap2_fit_g...
Augmented Lagrangian method for equality- and inequality-constrained minimization,...
Base error for the multiprecision C++ port.
NumericError(const std::string &what)
The exception types the port throws.
Fit a MAMAP(2,2) matching the FORWARD moment and the class TRANSITION probability sigma.
The marked Poisson process every MAMAP fitter falls back to.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Fit a MAPH(2,m): a second-order acyclic phase-type marked with m classes.
Dense matrix and non-owning view.
Compression of a marked MAP into a smaller representation, and the two M3A primitives it is built fro...
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
Marked MAP statistics: embedded chains, class-transition probabilities, forward and cross moments,...
Backward moments of a marked trace: the moments of the inter-arrival time that PRECEDES an event of e...
Forward moments of a marked trace: the moments of the inter-arrival time that FOLLOWS an event of eac...
Class probabilities of a marked trace, p_c = count_c / N.
One-step class transition frequencies of a marked trace,.
Mmap< T > maph2m_fit(const T &M1, const T &M2, const T &M3, const std::vector< T > &p, const std::vector< T > &B)
Fit a MAPH(2,m) to three moments, the class probabilities and the per-class backward moments,...
Amap2FitGammaResult< T > amap2_fit_gamma(const T &M1, const T &M2, const T &M3, const T &GAMMA, const T &cvtol)
Fit an AMAP(2) to (M1, M2, M3, GAMMA).
Mmap< T > mamap2m_fit_trace(const std::vector< T > &Tv, const std::vector< int > &A, const std::vector< T > &fbsWeights=std::vector< T >())
Fit a MAPH(2,m) or MAMAP(2,m) matching the characteristics of a marked trace.
Matrix< T > mmap_forward_moment(const Mmap< T > &mm, const std::vector< unsigned > &orders, bool normalize)
Forward moments: MOMENTS(a,h) is the order-orders[h] moment of the interval ENDING with a class-a arr...
Mmap< T > mamap2m_fit(const T &M1, const T &M2, const T &M3, const T &GAMMA, const std::vector< T > &p, const std::vector< T > &F, const std::vector< T > &B, const Matrix< T > &S, const std::vector< T > &fbsWeights=std::vector< T >())
The full mamap2m_fit dispatcher.
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
std::vector< std::vector< T > > mmap_backward_moment(const Mmap< T > &m, const std::vector< unsigned > &orders, bool normalized)
Class-conditional backward moments of an MMAP (mmap_backward_moment.m).
Mamap22FsFitResult< T > mamap22_fit_fs_multiclass(const Map< T > &map, const std::vector< T > &p, const std::vector< T > &F, const Matrix< T > &S, const std::vector< T > &classWeights=std::vector< T >(), const std::vector< T > &fsWeights=std::vector< T >(), bool adjust=true)
Mamap22FitResult< T > mamap22_fit_bs_multiclass(const Map< T > &map, const std::vector< T > &p, const std::vector< T > &B, const Matrix< T > &S, const std::vector< T > &classWeights=std::vector< T >(), const std::vector< T > &bsWeights=std::vector< T >(), bool adjust=true)
Matrix< T > mmap_sigma(const Mmap< T > &mm)
sigma(i,j) = pie E_i E_j 1, the probability that two consecutive marks are (i,j).
Mmap< T > mamap2m_fit_gamma_fb(const T &M1, const T &M2, const T &M3, const T &GAMMA, const std::vector< T > &p, const std::vector< T > &F, const std::vector< T > &B)
Fit a MAMAP(2,m) to three moments, the decay rate, the class probabilities and the forward and backwa...
Mamap2mFitResult< T > mamap2m_fit_fb_multiclass(const Map< T > &map, const std::vector< T > &p, const std::vector< T > &F, const std::vector< T > &B, const std::vector< T > &classWeights=std::vector< T >(), const std::vector< T > &fbWeights=std::vector< T >())
Mark a canonical acyclic AMAP(2) with m classes, matching the forward and backward moments.
Map< T > map_normalize(const Map< T > &in)
Clamp negative off-diagonal entries of D0 and negative entries of D1 to zero, then rebuild the diagon...
Mmap< T > mamap2m_fit_gamma_fb_trace(const std::vector< T > &Tv, const std::vector< int > &A)
Fit a MAMAP(2,m) from a marked trace through the (F, B) pair alone.
Maph2mFitResult< T > maph2m_fit_multiclass(const Map< T > &aph, const std::vector< T > &p, const std::vector< T > &B, const std::vector< T > &classWeights=std::vector< T >())
Mark a canonical acyclic APH(2) with m classes.
Matrix< T > mtrace_forward_moment(const std::vector< T > &Tv, const std::vector< int > &A, const std::vector< unsigned > &orders, bool norm=true)
Forward moments of a marked trace: the moments of the inter-arrival time that FOLLOWS an event of eac...
Matrix< T > mtrace_backward_moment(const std::vector< T > &Tv, const std::vector< int > &A, const std::vector< unsigned > &orders, bool norm=true)
Backward moments of a marked trace: the moments of the inter-arrival time that PRECEDES an event of e...
TraceGammaResult< T > trace_gamma(const std::vector< T > &S, long limit=1000, const std::vector< T > &grid=std::vector< T >())
Autocorrelation decay rate of a trace: the gamma of the geometric model rho(k) = rho0 * gamma^k,...
std::vector< T > mtrace_pc(const std::vector< int > &A)
Class probabilities of a marked trace, p_c = count_c / N.
Matrix< T > mtrace_sigma(const std::vector< int > &L)
One-step class transition frequencies of a marked trace, sigma(i,j) = #{t : A_t = i,...
AugLagResult< T > auglag(F f, H h, G g, const std::vector< T > &x0, const std::vector< Bound< T > > &bounds, const AugLagOptions< T > &opt)
Augmented Lagrangian with a scalar objective and a simplex inner solver.
Number-type abstraction for the templated API port.
Result of amap2_fit_gamma.
std::vector< Map< T > > amaps
every exact solution found
The fitted MAMAP(2,2), what it achieved, and whether the fit was exact.
The forward-plus-sigma result; warning carries the reference's diagnostic.
The fitted MAMAP and the moments it achieved.
std::vector< T > fF
achieved per-class forward moments
std::vector< T > fB
achieved per-class backward moments
A MAP as the pair of matrices (D0, D1).
std::size_t order() const
The fitted MAPH and the backward moments it actually achieved.
An MMAP: the underlying MAP plus the per-class arrival matrices.
Autocorrelation decay rate of a trace: the gamma of the geometric model rho(k) = rho0 * gamma^k,...