LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
mmap_compress.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_MMAP_COMPRESS_H
6#define LINE_API_MAM_MMAP_COMPRESS_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * Compression of a marked MAP into a smaller representation, and the two M3A
12 * primitives it is built from: the class-conditional backward moments and the
13 * probabilistic mixture of MAPs.
14 *
15 * Templated port of matlab/src/api/mam/mmap_compress.m (the 'default' /
16 * 'mixture' / 'mixture.order1' method), matlab/lib/m3a/m3a/mmap/
17 * mmap_backward_moment.m and mmap_mixture.m.
18 *
19 * ORDER-1 MIXTURE. One component per class, recombined by mmap_mixture with
20 * the class probabilities p_c as mixing weights. Component c must carry the
21 * law of the inter-arrival time CONDITIONED ON THE ARRIVAL THAT ENDS IT BEING
22 * OF CLASS c, because mmap_mixture marks the arrival LEAVING component c with
23 * class c, so the class of an arrival and the interval preceding it are both
24 * governed by the component active during that interval. That conditional law
25 * is the class-c BACKWARD moment set B(c, 1:3),
26 *
27 * B(c,k) = k! pie (-D0)^-(k+1) D1^(c) e / p_c,
28 *
29 * i.e. E[T^k | class of the ending arrival = c]. It is NOT the forward moment
30 * and it is NOT the class-c marginal MAP of mmap_maps, whose mean is
31 * 1/lambda_c, the time between successive class-c arrivals; mixing those with
32 * weights lambda_c/Lambda inflates the mean to K/Lambda.
33 *
34 * PRESERVED exactly: the aggregate moments 1..3, through the mixture law
35 * M_k = sum_c B(k,c) p_c (M1 always, since aph2_adjust never alters M1; M2 and
36 * M3 whenever the triple is APH(2)-feasible); the class probabilities p_c and
37 * hence the per-class rates lambda_c = p_c / M1; the marking consistency
38 * D1 = sum_c D1^(c); and MAP feasibility, since mmap_normalize closes the
39 * result.
40 *
41 * LOST by construction: every autocorrelation. mmap_mixture re-enters each
42 * component at its map_pie on every arrival, so the intervals are i.i.d. and
43 * the result is a RENEWAL process: acf -> 0, IDC -> the SCV-determined renewal
44 * value, and the class sequence becomes i.i.d. Retaining the class-transition
45 * matrix sigma is what the order-2 method buys with its K^2 components.
46 *
47 * ARITHMETIC. mmap_backward_moment and mmap_mixture are finite rational
48 * expressions in the descriptor entries and instantiate at Rational, which is
49 * what makes the preservation claims above CHECKABLE rather than merely
50 * plausible: at exact arithmetic the class probabilities of the compressed
51 * MMAP equal those of the original digit for digit, so any drift the tests see
52 * is a real modelling loss and not rounding. mmap_compress itself is gated on
53 * num_traits<T>::has_transcendental because aph2_fit is.
54 *
55 * THE OTHER METHODS dispatch to their fitting families, all ported in their
56 * own headers: 'mixture.order2' to mmap_mixture_fit_mmap (mmap_modulate.h),
57 * 'mamap2' and 'mamap2.fb' to mamap2m_fit_mmap and mamap2m_fit_gamma_fb_mmap
58 * (mamap2m_fit.h), and the four 'm3pp.*' variants to m3pp2m_fitc_theoretical
59 * (m3pp2m_interleave.h) at the reference's scales t = 1, tinf = 1e6. Those
60 * families carry their own acceptance contracts where the reference solves a
61 * program (quadprog, YALMIP): read the header of each before comparing digits.
62 * Every method ends in mmap_normalize, as mmap_compress.m does.
63 */
64
65#include <cstddef>
66#include <string>
67#include <vector>
68
77#include "line/num/number.h"
78#include "line/util/error.h"
79#include "line/util/linalg.h"
80#include "line/util/matrix.h"
81
82namespace line {
83namespace mam {
84
85/**
86 * Probabilistic mixture of MAPs (mmap_mixture.m).
87 *
88 * The phase space is the disjoint union of the component phase spaces, the
89 * hidden generator is block diagonal, and on completion of an interval in
90 * component i the process jumps into component j with probability alpha_j,
91 * entering it at its own map_pie. The arrival that LEAVES component i is
92 * marked with class i, so the resulting MMAP has one class per component:
93 *
94 * D0 = blkdiag(D0^1, ..., D0^I)
95 * D1 block (i,j) = alpha_j (D1^i e) pie^j
96 * D1^(c) block (i,j) = D1 block (i,j) if i == c, else 0.
97 *
98 * The result is a renewal process by construction; see the header note.
99 */
100template <class T>
101Mmap<T> mmap_mixture(const std::vector<T>& alpha, const std::vector<Map<T>>& maps) {
102 const std::size_t I = maps.size();
103 if (I == 0) throw InputError("mmap_mixture: no components");
104 if (alpha.size() != I) throw InputError("mmap_mixture: one weight per component is required");
105 const T zero = num_traits<T>::from_int(0);
106
107 std::vector<std::size_t> sz(I), off(I);
108 std::size_t total = 0;
109 for (std::size_t i = 0; i < I; ++i) {
110 sz[i] = maps[i].order();
111 off[i] = total;
112 total += sz[i];
113 }
114
115 std::vector<std::vector<T>> pies(I);
116 for (std::size_t j = 0; j < I; ++j) pies[j] = map_pie(maps[j]);
117
118 Mmap<T> out;
119 out.D0 = Matrix<T>(total, total, zero);
120 out.D1 = Matrix<T>(total, total, zero);
121 out.Dc.assign(I, Matrix<T>(total, total, zero));
122
123 for (std::size_t i = 0; i < I; ++i) {
124 for (std::size_t a = 0; a < sz[i]; ++a)
125 for (std::size_t b = 0; b < sz[i]; ++b) out.D0(off[i] + a, off[i] + b) = maps[i].D0(a, b);
126 // The completion rate out of each phase of component i.
127 std::vector<T> t(sz[i], zero);
128 for (std::size_t a = 0; a < sz[i]; ++a)
129 for (std::size_t b = 0; b < sz[i]; ++b) t[a] += maps[i].D1(a, b);
130 for (std::size_t j = 0; j < I; ++j)
131 for (std::size_t a = 0; a < sz[i]; ++a)
132 for (std::size_t b = 0; b < sz[j]; ++b) {
133 const T v = alpha[j] * t[a] * pies[j][b];
134 out.D1(off[i] + a, off[j] + b) = v;
135 out.Dc[i](off[i] + a, off[j] + b) = v;
136 }
137 }
138 return mmap_normalize(out);
139}
140
141/** The compression methods of mmap_compress.m. */
143 MixtureOrder1, ///< 'default', 'mixture', 'mixture.order1'
144 MixtureOrder2, ///< 'mixture.order2'
145 Mamap2, ///< 'mamap2'
146 Mamap2Fb, ///< 'mamap2.fb'
147 M3ppApproxCov, ///< 'm3pp.approx_cov'
148 M3ppApproxAg, ///< 'm3pp.approx_ag'
149 M3ppExactDelta, ///< 'm3pp.exact_delta'
150 M3ppApproxDelta ///< 'm3pp.approx_delta'
151};
152
153/**
154 * Compress an MMAP (mmap_compress.m).
155 *
156 * A class that never arrives (p_c <= 1e-14, the reference's
157 * GlobalConstants.Zero) gets Exp(1) as its component, exactly as in the
158 * reference: it carries zero mixture weight, so any proper MAP leaves the
159 * result unchanged and the 0/0 normalization of its backward moments is
160 * avoided.
161 */
162template <class T>
165 "mmap_compress requires transcendental arithmetic");
166 const T t1 = num_traits<T>::from_int(1), tinf = num_traits<T>::from_double(1e6);
167 switch (method) {
169 break;
177 return mmap_normalize(m3pp2m_fitc_theoretical(in, std::string("approx_cov"), t1, tinf));
179 return mmap_normalize(m3pp2m_fitc_theoretical(in, std::string("approx_ag"), t1, tinf));
181 return mmap_normalize(m3pp2m_fitc_theoretical(in, std::string("exact_delta"), t1, tinf));
183 return mmap_normalize(m3pp2m_fitc_theoretical(in, std::string("approx_delta"), t1, tinf));
184 }
185
186 const std::size_t K = in.classes();
187 if (K == 0) throw InputError("mmap_compress: the MMAP has no classes");
188 const std::vector<T> p = mmap_pc(in);
189 std::vector<unsigned> orders;
190 orders.push_back(1u);
191 orders.push_back(2u);
192 orders.push_back(3u);
193
194 const T zeroTol = num_traits<T>::from_double(1e-14);
195 // never-arriving-class 0/0 row: see _kb/03-api-layer.md (cpp port notes: mam)
196 std::vector<Map<T>> comps;
197 comps.reserve(K);
198 for (std::size_t k = 0; k < K; ++k) {
199 if (p[k] <= zeroTol) {
200 comps.push_back(map_exponential(T(num_traits<T>::from_int(1))));
201 continue;
202 }
203 Mmap<T> one = in;
204 one.Dc.assign(1, in.Dc[k]); // same D0 and D1, hence the same pie
205 const std::vector<std::vector<T>> B = mmap_backward_moment(one, orders, true);
206 comps.push_back(aph2_fit(B[0][0], B[0][1], B[0][2]).aph);
207 }
208 return mmap_normalize(mmap_mixture(p, comps));
209}
210
211/** mmap_compress with the default method, the order-1 mixture. */
212template <class T>
216
217} // namespace mam
218} // namespace line
219
220#endif // LINE_API_MAM_MMAP_COMPRESS_H
APH(2) fit of three moments, with a fallback to adjusted moments (matlab/lib/m3a/m3a/aph2/aph2_fit....
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LUMPED interleaving of several M3PP(2, m), and the two fitters built on it (matlab/lib/m3a/m3a/m3pp/m...
Fit a MAMAP(2,m): a second-order acyclic MAP marked with m classes, matching the forward and backward...
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
MAP constructors and structural transformations.
Dense matrix and non-owning view.
Class-conditional backward moments of an MMAP (matlab/lib/m3a/m3a/mmap/mmap_backward_moment....
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
Modulate a family of marked MAPs by an environment chain.
Mmap< T > mmap_normalize(const Mmap< T > &in)
Clamp negative off-diagonal and per-class entries to zero and rebuild D1 and the diagonal of D0 from ...
MmapCompressMethod
The compression methods of mmap_compress.m.
@ M3ppApproxDelta
'm3pp.approx_delta'
@ MixtureOrder1
'default', 'mixture', 'mixture.order1'
@ MixtureOrder2
'mixture.order2'
@ M3ppExactDelta
'm3pp.exact_delta'
@ M3ppApproxCov
'm3pp.approx_cov'
Aph2FitResult< T > aph2_fit(const T &M1, const T &M2, const T &M3)
Fit an APH(2) to (M1, M2, M3), relaxing the moments if necessary.
Definition aph2_fit.h:42
Mmap< T > mmap_mixture(const std::vector< T > &alpha, const std::vector< Map< T > > &maps)
Probabilistic mixture of MAPs (mmap_mixture.m).
Mmap< T > mmap_mixture_fit_mmap(const Mmap< T > &mm)
mmap_mixture_fit driven from an MMAP (mmap_mixture_fit_mmap.m): the triple sigma and the cross moment...
Map< T > map_exponential(const T &lambda)
Two-phase MAP constructor for a Poisson process of rate lambda.
Definition map_moment.h:213
Mmap< T > mmap_compress(const Mmap< T > &in, MmapCompressMethod method)
Compress an MMAP (mmap_compress.m).
Mmap< T > mamap2m_fit_gamma_fb_mmap(const Mmap< T > &mm)
Fit a MAMAP(2,m) to an MMAP through the (F, B) pair alone (mamap2m_fit_gamma_fb_mmap....
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).
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
Definition map_moment.h:89
Mmap< T > mamap2m_fit_mmap(const Mmap< T > &mm, const std::vector< T > &fbsWeights=std::vector< T >())
Fit a MAMAP(2,m) to the characteristics of an MMAP (mamap2m_fit_mmap.m): its three moments,...
std::vector< T > mmap_pc(const Mmap< T > &m)
Class probabilities seen by an arriving job, pc = pie (-D0)^-1 D1^(c) e.
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).
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Number-type abstraction for the templated API port.
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
An MMAP: the underlying MAP plus the per-class arrival matrices.
Definition mmap_lambda.h:45
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