LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
map_rand.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_MAP_RAND_H
6#define LINE_API_MAM_MAP_RAND_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * Random MAP, MMPP, MMAP, acyclic-PH and hyperexponential generators, plus the
12 * hyperexponential reader.
13 *
14 * Templated port of matlab/lib/kpctoolbox/map/map_rand.m, map_randn.m,
15 * matlab/lib/kpctoolbox/mmpp/mmpp_rand.m, matlab/lib/kpctoolbox/aph/aph_rand.m,
16 * hyper_rand.m, ph2hyper.m and matlab/lib/m3a/m3a/mmap/mmap_rand.m.
17 *
18 * The generators take an explicit engine, following ctmc_rand, so a caller can
19 * reproduce a draw; they therefore do NOT reproduce the MATLAB stream and are
20 * only distributionally equivalent to it. Everything is drawn on the raw
21 * matrices and then passed through map_normalize, which is what makes the
22 * result a generator rather than a nonnegative matrix pair.
23 *
24 * mmap_rand draws its class split once. MATLAB redraws it inside a loop over
25 * the phases and keeps only the last draw, so the two agree in law and differ
26 * only in how far the stream is advanced.
27 */
28
29#include <cmath>
30#include <cstddef>
31#include <random>
32#include <vector>
33
38#include "line/num/number.h"
39#include "line/util/error.h"
40#include "line/util/linalg.h"
41#include "line/util/matrix.h"
42
43namespace line {
44namespace mam {
45
46/** Random MAP of order K with uniform [0,1) entries, normalized. */
47template <class T, class Gen>
48Map<T> map_rand(std::size_t K, Gen& gen) {
49 std::uniform_real_distribution<double> unif(0.0, 1.0);
51 for (std::size_t i = 0; i < K; ++i)
52 for (std::size_t j = 0; j < K; ++j) {
53 D0(i, j) = num_traits<T>::from_double(unif(gen));
54 D1(i, j) = num_traits<T>::from_double(unif(gen));
55 }
56 return map_normalize(Map<T>{D0, D1});
57}
58
59/** Random MAP of order K with folded normal entries, normalized. */
60template <class T, class Gen>
61Map<T> map_randn(std::size_t K, double mu, double sigma, Gen& gen) {
62 std::normal_distribution<double> nrm(mu, sigma);
64 for (std::size_t i = 0; i < K; ++i)
65 for (std::size_t j = 0; j < K; ++j) {
66 D0(i, j) = num_traits<T>::from_double(std::fabs(nrm(gen)));
67 D1(i, j) = num_traits<T>::from_double(std::fabs(nrm(gen)));
68 }
69 return map_normalize(Map<T>{D0, D1});
70}
71
72/** Random MMPP of order K: the arrival matrix is diagonal, so arrivals do not switch phase. */
73template <class T, class Gen>
74Map<T> mmpp_rand(std::size_t K, Gen& gen) {
75 std::uniform_real_distribution<double> unif(0.0, 1.0);
77 for (std::size_t i = 0; i < K; ++i)
78 for (std::size_t j = 0; j < K; ++j) D0(i, j) = num_traits<T>::from_double(unif(gen));
79 for (std::size_t i = 0; i < K; ++i) {
80 for (std::size_t j = 0; j < K; ++j) {
81 const double v = unif(gen);
82 if (i == j) D1(i, j) = num_traits<T>::from_double(v);
83 }
84 }
85 return map_normalize(Map<T>{D0, D1});
86}
87
88/**
89 * Random M3PP of order K with `classes` marks (m3a/m3pp/m3pp_rand.m).
90 *
91 * The underlying process is `mmpp_rand`; the marks are a class-independent
92 * Bernoulli split of D1 with probabilities drawn from a uniform simplex, so
93 * sum_c Dc = D1 by construction. The reference wraps the draw in two further
94 * loops over the phases whose bodies overwrite the same matrices, so only the
95 * last draw survives; that is what a single draw here reproduces.
96 */
97template <class T, class Gen>
98Mmap<T> m3pp_rand(std::size_t K, std::size_t classes, Gen& gen) {
99 if (classes == 0) throw InputError("m3pp_rand: at least one class is required");
100 std::uniform_real_distribution<double> unif(0.0, 1.0);
101 const Map<T> base = mmpp_rand<T>(K, gen);
102 std::vector<T> p(classes);
103 T tot = num_traits<T>::from_int(0);
104 for (std::size_t c = 0; c < classes; ++c) {
105 p[c] = num_traits<T>::from_double(unif(gen));
106 tot += p[c];
107 }
108 Mmap<T> out;
109 out.D0 = base.D0;
110 out.D1 = base.D1;
111 for (std::size_t c = 0; c < classes; ++c) {
113 for (std::size_t i = 0; i < K; ++i)
114 for (std::size_t j = 0; j < K; ++j) Dc(i, j) = base.D1(i, j) * p[c] / tot;
115 out.Dc.push_back(Dc);
116 }
117 return out;
118}
119
120/** Random acyclic PH renewal process of order K, upper triangular in D0. */
121template <class T, class Gen>
122Map<T> aph_rand(std::size_t K, Gen& gen) {
123 std::uniform_real_distribution<double> unif(0.0, 1.0);
125 for (std::size_t i = 0; i < K; ++i)
126 for (std::size_t j = 0; j < K; ++j) D1(i, j) = num_traits<T>::from_double(unif(gen));
127 for (std::size_t i = 0; i < K; ++i)
128 for (std::size_t j = 0; j < K; ++j) {
129 const double v = unif(gen);
130 if (j >= i) D0(i, j) = num_traits<T>::from_double(v);
131 }
132 return map_normalize(map_renewal(Map<T>{D0, D1}));
133}
134
135/** Random hyperexponential of order k, given as a MAP. */
136template <class T, class Gen>
137Map<T> hyper_rand(std::size_t k, Gen& gen) {
138 std::uniform_real_distribution<double> unif(0.0, 1.0);
139 std::vector<T> v(k), alpha(k);
141 for (std::size_t i = 0; i < k; ++i) {
142 v[i] = num_traits<T>::from_double(unif(gen));
143 alpha[i] = num_traits<T>::from_double(unif(gen));
144 s += alpha[i];
145 }
146 for (std::size_t i = 0; i < k; ++i) alpha[i] = alpha[i] / s;
148 for (std::size_t i = 0; i < k; ++i) {
149 H0(i, i) = -v[i];
150 for (std::size_t j = 0; j < k; ++j) H1(i, j) = v[i] * alpha[j];
151 }
152 return Map<T>{H0, H1};
153}
154
155/** Random MMAP of the given order with a random split of D1 across the classes. */
156template <class T, class Gen>
157Mmap<T> mmap_rand(std::size_t order, std::size_t classes, Gen& gen) {
158 if (classes == 0) throw InputError("mmap_rand: at least one class is required");
159 const Map<T> base = map_rand<T>(order, gen);
160 std::uniform_real_distribution<double> unif(0.0, 1.0);
161 std::vector<T> p(classes);
163 for (std::size_t c = 0; c < classes; ++c) {
164 p[c] = num_traits<T>::from_double(unif(gen));
165 s += p[c];
166 }
167 Mmap<T> out;
168 out.D0 = base.D0;
169 out.D1 = base.D1;
170 for (std::size_t c = 0; c < classes; ++c) {
171 Matrix<T> Dc = base.D1;
172 for (std::size_t i = 0; i < Dc.rows(); ++i)
173 for (std::size_t j = 0; j < Dc.cols(); ++j) Dc(i, j) = Dc(i, j) * p[c] / s;
174 out.Dc.push_back(Dc);
175 }
176 return out;
177}
178
179/** Rates and branch probabilities of a hyperexponential given as a MAP. */
180template <class T>
182 std::vector<T> lambda;
183 std::vector<T> prob;
184};
185
186/** Reads a hyperexponential MAP back into rates and branch probabilities. */
187template <class T>
189 const std::size_t n = ph.D0.rows();
190 const T tol = num_traits<T>::from_double(1e-10);
191 for (std::size_t i = 0; i < n; ++i)
192 for (std::size_t j = 0; j < n; ++j)
193 if (i != j && num_abs(ph.D0(i, j)) > tol)
194 throw InputError("ph2hyper: the PH distribution is not hyperexponential");
195 Matrix<T> negD0(n, n, num_traits<T>::from_int(0));
196 HyperParams<T> out;
197 out.lambda.resize(n);
198 for (std::size_t i = 0; i < n; ++i) {
199 out.lambda[i] = -ph.D0(i, i);
200 negD0(i, i) = out.lambda[i];
201 }
202 out.prob = mc::dtmc_solve(matmul(inverse(negD0), ph.D1));
203 return out;
204}
205
206} // namespace mam
207} // namespace line
208
209#endif
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
Equilibrium distribution of a discrete-time Markov chain, and stochastic complementation.
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
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...
Map< T > mmpp_rand(std::size_t K, Gen &gen)
Random MMPP of order K: the arrival matrix is diagonal, so arrivals do not switch phase.
Definition map_rand.h:74
Map< T > aph_rand(std::size_t K, Gen &gen)
Random acyclic PH renewal process of order K, upper triangular in D0.
Definition map_rand.h:122
Map< T > map_renewal(const Map< T > &in)
Renewal process with the same inter-arrival distribution: D1 is replaced by (D1 e) pie,...
Mmap< T > m3pp_rand(std::size_t K, std::size_t classes, Gen &gen)
Random M3PP of order K with classes marks (m3a/m3pp/m3pp_rand.m).
Definition map_rand.h:98
Map< T > map_randn(std::size_t K, double mu, double sigma, Gen &gen)
Random MAP of order K with folded normal entries, normalized.
Definition map_rand.h:61
Map< T > map_rand(std::size_t K, Gen &gen)
Random MAP of order K with uniform [0,1) entries, normalized.
Definition map_rand.h:48
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...
Map< T > hyper_rand(std::size_t k, Gen &gen)
Random hyperexponential of order k, given as a MAP.
Definition map_rand.h:137
Mmap< T > mmap_rand(std::size_t order, std::size_t classes, Gen &gen)
Random MMAP of the given order with a random split of D1 across the classes.
Definition map_rand.h:157
HyperParams< T > ph2hyper(const Map< T > &ph)
Reads a hyperexponential MAP back into rates and branch probabilities.
Definition map_rand.h:188
std::vector< T > dtmc_solve(const Matrix< T > &P)
Stationary distribution of a stochastic matrix P.
Definition dtmc_solve.h:106
T num_abs(const T &v)
Definition number.h:172
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
Definition linalg.h:72
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
Definition linalg.h:36
Number-type abstraction for the templated API port.
Rates and branch probabilities of a hyperexponential given as a MAP.
Definition map_rand.h:181
std::vector< T > lambda
Definition map_rand.h:182
std::vector< T > prob
Definition map_rand.h:183
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