LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
m3pp2m_interleave.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_INTERLEAVE_H
6#define LINE_API_MAM_M3PP2M_INTERLEAVE_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * LUMPED interleaving of several M3PP(2, m), and the two fitters built on it
12 * (matlab/lib/m3a/m3a/m3pp/m3pp2m_interleave.m,
13 * matlab/lib/m3a/m3a/m3pp/m3pp2m_fitc_theoretical.m,
14 * matlab/lib/m3a/m3a/m3pp/m3pp22_interleave_fitc.m).
15 *
16 * Interleaving is the cheap alternative to superposition. Superposing L
17 * two-phase processes gives the PRODUCT chain, of order 2^L; interleaving
18 * instead lays the L phase processes on a single BIRTH-DEATH chain of order
19 * L + 1, where phase h means "the first h components are in their fast phase".
20 * The off-diagonal rates are recovered by DIFFERENCING: the upper rate out of
21 * level j is component j's r1 minus the rates already spent on levels above it,
22 * and symmetrically downwards. That differencing is only meaningful when the
23 * components' rates are ordered, which is what m3pp22_interleave_fitc's linear
24 * program arranges before it ever fits a component -- it does not fit L
25 * processes and then hope they interleave, it SOLVES for off-diagonal rates
26 * that admit the interleaving and fits the components to those.
27 *
28 * The class matrices carry no cross terms: class j of component i fires at its
29 * phase-1 rate on levels h <= i and at its phase-2 rate above, so sum_c Dc = D1
30 * holds level by level.
31 *
32 * THE LINEAR PROGRAM IS A FEASIBILITY PROBLEM. The reference passes a ZERO
33 * objective to linprog, so every feasible point is optimal and which vertex
34 * comes back is the solver's choice; different LP backends hand different
35 * MMPP(2)s to the per-pair covariance split, and a covariance one accepts
36 * another can report infeasible. That is a property of the reference, not of
37 * this port, and it is why m3pp22_interleave_fitc reports the realised
38 * covariance per pair rather than asserting the requested one.
39 *
40 * Gated on transcendental arithmetic, and on double alone for the LP.
41 */
42
43#include <cstddef>
44#include <string>
45#include <vector>
46
58#include "line/num/number.h"
59#include "line/util/error.h"
60#include "line/util/matrix.h"
61#include "line/util/simplex.h"
62
63namespace line {
64namespace mam {
65
66/**
67 * Interleave L M3PP(2, m_i) into one M3PP of order L + 1 whose class list is
68 * the concatenation of theirs.
69 */
70template <class T>
71Mmap<T> m3pp2m_interleave(const std::vector<Mmap<T>>& parts) {
72 const T zero = num_traits<T>::from_int(0);
73 const std::size_t L = parts.size();
74 if (L == 0) throw InputError("m3pp2m_interleave: no components");
75 for (std::size_t i = 0; i < L; ++i)
76 if (parts[i].order() != 2)
77 throw InputError("m3pp2m_interleave: every component must have order 2");
78
79 // r(0, i): upper off-diagonal rate contributed by component i, differenced
80 // from the top down; r(1, i): lower rate, differenced from the bottom up.
81 std::vector<T> r0(L, zero), r1(L, zero);
82 r0[L - 1] = parts[L - 1].D0(0, 1);
83 for (std::size_t k = L - 1; k-- > 0;) {
84 T acc = zero;
85 for (std::size_t j = k + 1; j < L; ++j) acc += r0[j];
86 r0[k] = parts[k].D0(0, 1) - acc;
87 }
88 r1[0] = parts[0].D0(1, 0);
89 for (std::size_t i = 1; i < L; ++i) {
90 T acc = zero;
91 for (std::size_t j = 0; j < i; ++j) acc += r1[j];
92 r1[i] = parts[i].D0(1, 0) - acc;
93 }
94
95 std::size_t M = 0;
96 for (std::size_t i = 0; i < L; ++i) M += parts[i].classes();
97 const std::size_t n = 2 + (L - 1);
98
99 Mmap<T> s;
100 s.D0 = Matrix<T>(n, n, zero);
101 for (std::size_t i = 0; i < n; ++i)
102 for (std::size_t j = 0; j < n; ++j) {
103 if (j > i)
104 s.D0(i, j) = r0[j - 1];
105 else if (j < i)
106 s.D0(i, j) = r1[j];
107 }
108
109 for (std::size_t i = 0; i < L; ++i) {
110 for (std::size_t c = 0; c < parts[i].classes(); ++c) {
111 Matrix<T> Dc(n, n, zero);
112 for (std::size_t h = 0; h < n; ++h)
113 Dc(h, h) = h <= i ? parts[i].Dc[c](0, 0) : parts[i].Dc[c](1, 1);
114 s.Dc.push_back(Dc);
115 }
116 }
117
118 s.D1 = Matrix<T>(n, n, zero);
119 for (std::size_t c = 0; c < M; ++c)
120 for (std::size_t i = 0; i < n; ++i)
121 for (std::size_t j = 0; j < n; ++j) s.D1(i, j) += s.Dc[c](i, j);
122
123 for (std::size_t h = 0; h < n; ++h) {
124 T acc = zero;
125 for (std::size_t j = 0; j < n; ++j) acc += s.D0(h, j) + s.D1(h, j);
126 s.D0(h, h) = -acc;
127 }
128 return s;
129}
130
131/**
132 * Fit the counting characteristics of a GIVEN MMAP with an M3PP(2, m).
133 *
134 * @param mm the MMAP(n, m) to fit
135 * @param method 'exact_delta', 'approx_delta', 'approx_cov' or 'approx_ag'
136 * @param t the single finite time scale (the reference sets t1 = t2 = t3 = t)
137 * @param tinf the near-infinite time scale
138 */
139template <class T>
140Mmap<T> m3pp2m_fitc_theoretical(const Mmap<T>& mm, const std::string& method, const T& t,
141 const T& tinf) {
143 "m3pp2m_fitc_theoretical requires transcendental arithmetic");
144 const std::size_t m = mm.classes();
145 if (m == 0) throw InputError("m3pp2m_fitc_theoretical: the MMAP has no classes");
146 if (method == "approx_cov" && m > 2)
147 throw InputError("m3pp2m_fitc_theoretical: approximate covariance fitting only supports "
148 "two classes");
149
150 const T t1 = t, t2 = t, t3 = t;
151 const Map<T> joint = mm.map();
152
153 std::vector<T> ts;
154 ts.push_back(t1);
155 ts.push_back(t2);
156 ts.push_back(tinf);
157 ts.push_back(t3);
158 const std::vector<T> mu = map_count_mean(joint, ts);
159 const std::vector<T> vr = map_count_var(joint, ts);
160
161 const T a = mu[0] / t1;
162 const T bt1 = vr[0] / (a * t1);
163 const T bt2 = vr[1] / (a * t2);
164 const T binf = vr[2] / (a * tinf);
165
166 std::vector<unsigned> orders;
167 orders.push_back(1);
168 orders.push_back(2);
169 orders.push_back(3);
170 const std::vector<T> mt2 = map_count_moment(joint, t2, orders);
171 const T m3t2 = fitdetail::m3_from_raw(mt2[0], mt2[1], mt2[2]);
172
173 const std::vector<T> ai = mmap_count_mean(mm, num_traits<T>::from_int(1));
174
175 if (method == "exact_delta" || method == "approx_delta") {
176 std::vector<T> dvt3(m);
177 for (std::size_t i = 0; i < m; ++i) {
178 Mmap<T> two;
179 two.D0 = mm.D0;
180 two.D1 = mm.D1;
181 two.Dc.push_back(mm.Dc[i]);
182 Matrix<T> rest = mm.D1;
183 for (std::size_t r = 0; r < rest.rows(); ++r)
184 for (std::size_t c = 0; c < rest.cols(); ++c) rest(r, c) -= mm.Dc[i](r, c);
185 two.Dc.push_back(rest);
186 const std::vector<T> V = mmap_count_var(two, t3);
187 dvt3[i] = V[0] - V[1];
188 }
189 if (method == "exact_delta")
190 return m3pp2m_fitc(a, bt1, bt2, binf, m3t2, t1, t2, ai, dvt3, t3).mmap;
191 return m3pp2m_fitc_approx(a, bt1, bt2, binf, m3t2, t1, t2, ai, dvt3, t3).mmap;
192 }
193 if (method == "approx_cov") {
194 const std::vector<T> vi = mmap_count_var(mm, t3);
196 for (std::size_t i = 0; i < vi.size(); ++i) sum += vi[i];
197 const T s = (vr[3] - sum) / num_traits<T>::from_int(2);
198 return m3pp22_fitc_approx_cov(a, bt1, bt2, binf, m3t2, t1, t2, ai, s, t3).mmap;
199 }
200 if (method == "approx_ag") {
201 std::vector<T> gt3(m);
202 for (std::size_t i = 0; i < m; ++i) {
203 Mmap<T> two;
204 two.D0 = mm.D0;
205 two.D1 = mm.D1;
206 two.Dc.push_back(mm.Dc[i]);
207 Matrix<T> rest = mm.D1;
208 for (std::size_t r = 0; r < rest.rows(); ++r)
209 for (std::size_t c = 0; c < rest.cols(); ++c) rest(r, c) -= mm.Dc[i](r, c);
210 two.Dc.push_back(rest);
211 const std::vector<T> V = mmap_count_var(two, t3);
212 const Matrix<T> S = mmap_count_mcov(two, t3);
213 gt3[i] = V[0] + S(0, 1);
214 }
215 return m3pp2m_fitc_approx_ag(a, bt1, bt2, binf, m3t2, t1, t2, ai, gt3, t3).mmap;
216 }
217 throw InputError("m3pp2m_fitc_theoretical: unknown method '" + method + "'");
218}
219
220/** m3pp2m_fitc_theoretical with the reference's default scales t = 10, tinf = 1e4. */
221template <class T>
222Mmap<T> m3pp2m_fitc_theoretical(const Mmap<T>& mm, const std::string& method) {
225}
226
227/** Result of m3pp22_interleave_fitc. */
228template <class T>
230 Mmap<T> mmap; ///< the lumped process, of order L + 1
231 std::vector<Mmap<T>> parts; ///< the L M3PP(2, 2) components
232 std::vector<T> sigma; ///< the covariance realised for each pair
233};
234
235/**
236 * Fit L PAIRS of classes into one MMAP by lumped interleaving of L M3PP(2, 2).
237 *
238 * @param av (L x 2) per-class rates
239 * @param btv per-pair IDC at t
240 * @param binfv per-pair asymptotic IDC
241 * @param stv per-pair count covariance at t
242 * @param t the time scale
243 */
244template <class T>
245M3pp22InterleaveResult<T> m3pp22_interleave_fitc(const Matrix<T>& av, const std::vector<T>& btv,
246 const std::vector<T>& binfv,
247 const std::vector<T>& stv, const T& t) {
249 "m3pp22_interleave_fitc requires transcendental arithmetic");
250 using fitdetail::lambertw0;
251 using fitdetail::num_exp;
252 using fitdetail::num_sqrt;
253 using fitdetail::pw;
254
255 const T zero = num_traits<T>::from_int(0);
256 const T one = num_traits<T>::from_int(1);
257 const T two = num_traits<T>::from_int(2);
258 const std::size_t L = av.rows();
259 if (L == 0) throw InputError("m3pp22_interleave_fitc: no pairs");
260 if (av.cols() != 2) throw InputError("m3pp22_interleave_fitc: av must have two columns");
261 if (btv.size() != L || binfv.size() != L || stv.size() != L)
262 throw InputError("m3pp22_interleave_fitc: btv, binfv and stv must have one entry per pair");
263
264 // bounds on the upper off-diagonal element of each MMPP(2)
265 std::vector<T> uv(L, zero), dv(L, zero);
266 for (std::size_t i = 0; i < L; ++i) {
267 const T a = av(i, 0) + av(i, 1);
268 if (!(binfv[i] > btv[i] && btv[i] > one))
269 throw InputError("m3pp22_interleave_fitc: infeasible IDC pair, IDC(inf) must exceed "
270 "IDC(t) and IDC(t) must exceed one");
271 const T c = (binfv[i] - one) / (binfv[i] - btv[i]);
272 const T w = lambertw0(T(-c * num_exp(T(-c))), 200u);
273 const T d = (w + c) / t;
274 const T z = (binfv[i] - one) * pw(d, 3) * a;
275 uv[i] = d * z / (two * a * a * d * d + z);
276 dv[i] = d;
277 }
278
279 // feasibility LP over the 2L per-level off-diagonal rates
280 lp::LpModel<T> model(2 * L);
281 model.set_maximize(false);
282 for (std::size_t j = 0; j < 2 * L; ++j) {
283 model.set_lower(j, zero);
284 model.set_free_upper(j);
285 model.set_cost(j, zero);
286 }
287 const T eps = num_traits<T>::from_double(1e-6);
288 for (std::size_t i = 0; i < L; ++i) {
289 model.row_clear();
290 for (std::size_t j = i; j < L; ++j) model.row_add(j, one);
291 model.emit_le(T(dv[i] - eps));
292 model.row_clear();
293 for (std::size_t j = i; j < L; ++j) model.row_add(j, T(-one));
294 model.emit_le(T(-uv[i] - eps));
295 }
296 for (std::size_t i = 0; i < L; ++i) {
297 model.row_clear();
298 for (std::size_t j = i; j < L; ++j) model.row_add(j, one);
299 for (std::size_t j = 0; j <= i; ++j) model.row_add(L + j, one);
300 model.emit_eq(dv[i]);
301 }
302 const lp::LpSolution<T> sol = lp::simplex_solve(model);
303 if (!sol.ok())
304 throw NumericError("m3pp22_interleave_fitc: no feasible set of off-diagonal rates (" +
305 std::string(lp::lp_status_name(sol.status)) + ")");
306
308 for (std::size_t i = 0; i < L; ++i) {
309 T r1 = zero, r2 = zero;
310 for (std::size_t j = i; j < L; ++j) r1 += sol.x[j];
311 for (std::size_t j = 0; j <= i; ++j) r2 += sol.x[L + j];
312 const T a = av(i, 0) + av(i, 1);
313 const T d = r1 + r2;
314 const T z = (binfv[i] - one) * pw(d, 3) * a;
315 const T delta = num_sqrt(T(z / (two * r1 * r2)));
316 const T l2 = a - r2 / d * delta;
317 const T l1 = l2 + delta;
318
319 Map<T> base;
320 base.D0 = Matrix<T>(2, 2, zero);
321 base.D1 = Matrix<T>(2, 2, zero);
322 base.D0(0, 1) = r1;
323 base.D0(1, 0) = r2;
324 base.D1(0, 0) = l1;
325 base.D1(1, 1) = l2;
326 for (std::size_t h = 0; h < 2; ++h)
327 base.D0(h, h) = -(base.D0(h, 0) + base.D0(h, 1) + base.D1(h, 0) + base.D1(h, 1));
328
329 std::vector<T> ai;
330 ai.push_back(av(i, 0));
331 ai.push_back(av(i, 1));
332 const M3pp22FitcCovResult<T> f = m3pp22_fitc_approx_cov_multiclass(base, ai, stv[i], t);
333 out.parts.push_back(f.mmap);
334 out.sigma.push_back(f.sigma);
335 }
336
337 out.mmap = m3pp2m_interleave(out.parts);
338 return out;
339}
340
341} // namespace mam
342} // namespace line
343
344#endif // LINE_API_MAM_M3PP2M_INTERLEAVE_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
NumericError(const std::string &what)
Definition error.h:45
Sparse LP in the natural form, with per-variable bounds.
Definition simplex.h:112
void emit_eq(const T &rhs)
Definition simplex.h:217
void set_maximize(bool m)
true to maximize c'x (the default), false to minimize.
Definition simplex.h:174
void emit_le(const T &rhs)
Definition simplex.h:216
void set_free_upper(std::size_t j)
Definition simplex.h:147
void set_cost(std::size_t j, const T &v)
Definition simplex.h:164
void set_lower(std::size_t j, const T &v)
Definition simplex.h:129
void row_clear()
Discard whatever the row accumulator holds.
Definition simplex.h:179
void row_add(std::size_t j, const T &v)
row(j) += v, the accumulation the MATLAB reference performs.
Definition simplex.h:188
The exception types the port throws.
M3PP(2, 2) fitted to the count COVARIANCE between its two classes (matlab/lib/m3a/m3a/m3pp/m3pp22_fit...
Marked MMPP(2) with m classes, fitted to counting-process characteristics (matlab/lib/m3a/m3a/m3pp/m3...
M3PP(2, m) fitted to counting-process characteristics with an optimized per-class split (matlab/lib/m...
M3PP obtained by SUPERPOSING one second-order process per class (matlab/lib/m3a/m3a/m3pp/m3pp_superpo...
Mean of the counting process of a MAP at resolution t.
Power moments of the counts of a MAP in a window of length t.
Variance of the counting process of a MAP at resolution t.
Dense matrix and non-owning view.
Per-class variance of the counting process of a marked MAP.
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,...
MMPP(2) matching counting-process characteristics (matlab/lib/kpctoolbox/mmpp/mmpp2_fitc....
const char * lp_status_name(LpStatus s)
Definition simplex.h:84
LpSolution< T > simplex_solve(const LpModel< T > &model, std::size_t max_iterations=0)
Solve the model.
Definition simplex.h:286
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...
M3pp22InterleaveResult< T > m3pp22_interleave_fitc(const Matrix< T > &av, const std::vector< T > &btv, const std::vector< T > &binfv, const std::vector< T > &stv, const T &t)
Fit L PAIRS of classes into one MMAP by lumped interleaving of L M3PP(2, 2).
Mmap< T > m3pp2m_interleave(const std::vector< Mmap< T > > &parts)
Interleave L M3PP(2, m_i) into one M3PP of order L + 1 whose class list is the concatenation of their...
M3pp2mFitcResult< T > m3pp2m_fitc(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)
Fit an M3PP(2, m).
Definition m3pp2m_fitc.h:60
Matrix< T > mmap_count_mcov(const Mmap< T > &mm, const T &t)
Covariance matrix of the per-class counts over a window of length t.
Definition mmap_stats.h:222
std::vector< T > map_count_var(const Map< T > &m, const std::vector< T > &t)
Variance of the counting process of a MAP at resolution t.
std::vector< T > map_count_mean(const Map< T > &m, const std::vector< T > &t)
Mean of the counting process of a MAP at resolution t.
std::vector< T > mmap_count_mean(const Mmap< T > &mm, const T &t)
Per-class mean of the counting process over a window of length t.
Definition mmap_stats.h:182
M3pp22FitcCovResult< T > m3pp22_fitc_approx_cov(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 T &st3, const T &t3, const AugLagOptions< T > &opt)
Fit the underlying MMPP(2) by optimization, then apply the covariance split.
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...
std::vector< T > map_count_moment(const Map< T > &m, const T &t, const std::vector< unsigned > &orders)
Power moments of the counts of a MAP in a window of length t.
std::vector< T > mmap_count_var(const Mmap< T > &mm, const T &t)
Per-class variance of the counting process of a marked MAP.
M3pp22FitcCovResult< T > m3pp22_fitc_approx_cov_multiclass(const Map< T > &mmpp, const std::vector< T > &ai, const T &st3, const T &t3)
Split a GIVEN MMPP(2) into two classes, matching the per-class rates exactly and the count covariance...
Mmap< T > m3pp2m_fitc_theoretical(const Mmap< T > &mm, const std::string &method, const T &t, const T &tinf)
Fit the counting characteristics of a GIVEN MMAP with an M3PP(2, m).
Number-type abstraction for the templated API port.
Templated primal simplex with Bland's rule.
bool ok() const
Definition simplex.h:99
std::vector< T > x
primal solution in the ORIGINAL variable space
Definition simplex.h:96
Result of the covariance-matching M3PP(2, 2) fits.
Mmap< T > mmap
the fitted M3PP(2, 2)
T sigma
the covariance actually realised, after clamping
Result of m3pp22_interleave_fitc.
Mmap< T > mmap
the lumped process, of order L + 1
std::vector< Mmap< T > > parts
the L M3PP(2, 2) components
std::vector< T > sigma
the covariance realised for each pair
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
Map< T > map() const
Definition mmap_lambda.h:52
std::size_t classes() const
Definition mmap_lambda.h:51
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