LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mmapmap1.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_QSYS_QSYS_MMAPMAP1_H
6#define LINE_API_QSYS_QSYS_MMAPMAP1_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * The MMAP[K]/MAP[K]/1 FCFS queue in which each class's service phase is CARRIED.
12 *
13 * THE SERVICE LAW. Each class owns ONE service process whose successive draws are
14 * chained in the start order of THAT class: a class-k job starts in the phase the
15 * previous class-k draw ended in, whatever was served in between. That is how JMT
16 * and both LDES engines sample a MAP service, and the law SolverCTMC follows
17 * through the per-class carried-phase local variable. A renewal class keeps its
18 * marginal phase-type law, which is exact for it.
19 *
20 * THE REDUCTION. Under FCFS at one server the service-start order IS the arrival
21 * order, so the landing phase of each class-k draw can be decided at the ARRIVAL of
22 * its job. With V_k = (-S0)^-1 S1 a draw started in h ends in j with probability
23 * V_k(h,j); conditioned on (h,j) it is phase type: S0 restricted to the phases i
24 * with V_k(i,j) > 0, moved by the Doob transform S0(i,i')V_k(i',j)/V_k(i,j) and
25 * started in h. The queue is a MMAP[K']/PH[K']/1 FCFS queue whose arrival
26 * environment is (arrival phase, carried phase of every carried class) and whose
27 * marks are (k,h,j); mmapph1fcfs (He, 2012) solves it exactly and the class
28 * measures are the sums over each class's marks.
29 *
30 * References:
31 * Q. He, "Analysis of a continuous time SM[K]/PH[K]/1/FCFS queue: Age process,
32 * sojourn times, and queue lengths", Journal of Systems Science and Complexity,
33 * 25(1):133-155, 2012.
34 */
35
36#include <cmath>
37#include <cstddef>
38#include <string>
39#include <vector>
40
44#include "line/num/number.h"
45#include "line/util/error.h"
46#include "line/util/linalg.h"
47#include "line/util/lu.h"
48#include "line/util/matrix.h"
49
50namespace line {
51namespace qsys {
52
53/** Return value of qsys_mmapmap1, mirroring the MATLAB struct. */
54template <class T>
56 std::vector<T> meanQueueLength; ///< E[N_k]
57 std::vector<T> meanSojournTime; ///< E[W_k], by Little's law
58 std::vector<T> arrivalRate; ///< class arrival rates
59 std::vector<bool> carry; ///< classes whose service phase is carried
60 std::size_t markCount; ///< marks of the lifted MMAP
61};
62
63namespace mmapmap1detail {
64
65/** D1 = (-D0 e) alpha to within tolerance, as mam_is_renewal_map. */
66template <class T>
67bool is_renewal(const mam::Map<T>& m) {
68 const std::size_t n = m.D0.rows();
69 if (n == 1) return true;
70 const std::vector<T> alpha = mam::map_pie(m);
71 double err = 0.0, nrm = 0.0;
72 for (std::size_t i = 0; i < n; ++i) {
73 double ti = 0.0;
74 for (std::size_t j = 0; j < n; ++j) ti += num_traits<T>::to_double(m.D1(i, j));
75 for (std::size_t j = 0; j < n; ++j) {
76 const double d1 = num_traits<T>::to_double(m.D1(i, j));
77 const double e = d1 - ti * num_traits<T>::to_double(alpha[j]);
78 err += e * e;
79 nrm += d1 * d1;
80 }
81 }
82 return std::sqrt(err) < 1e-9 * std::max(1.0, std::sqrt(nrm));
83}
84
85} // namespace mmapmap1detail
86
87/**
88 * MMAP[K]/MAP[K]/1 FCFS with per-class carried service phases, exactly.
89 *
90 * @param arrival marked arrival process (D0, D1, Dc[0..K-1])
91 * @param svc svc[k] = (S0, S1), the service MAP of class k
92 * @param carry per-class flags of the carried classes; empty for every arriving
93 * class whose MAP is not renewal
94 */
95template <class T>
96MmapMap1Result<T> qsys_mmapmap1(const mam::Mmap<T>& arrival, const std::vector<mam::Map<T> >& svc,
97 std::vector<bool> carry = std::vector<bool>()) {
98 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
99 const std::size_t K = arrival.Dc.size();
100 if (svc.size() != K)
101 throw InputError("qsys_mmapmap1: svc must give one service MAP per arrival mark");
102 const std::size_t ma = arrival.D0.rows();
103 const std::vector<T> piA = mam::map_prob(mam::Map<T>{arrival.D0, arrival.D1});
104 std::vector<T> lamk(K, zero);
105 for (std::size_t k = 0; k < K; ++k)
106 for (std::size_t i = 0; i < ma; ++i)
107 for (std::size_t j = 0; j < ma; ++j) lamk[k] += piA[i] * arrival.Dc[k](i, j);
108 if (carry.empty()) {
109 carry.assign(K, false);
110 for (std::size_t k = 0; k < K; ++k) carry[k] = !mmapmap1detail::is_renewal(svc[k]);
111 }
112 if (carry.size() != K) throw InputError("qsys_mmapmap1: carry must give one flag per class");
113 // A class that never arrives draws no service, so it has nothing to carry
114 for (std::size_t k = 0; k < K; ++k) carry[k] = carry[k] && lamk[k] > zero;
115
116 std::vector<std::size_t> pk(K, 1), cidx;
117 std::size_t P = 1;
118 for (std::size_t k = 0; k < K; ++k) {
119 if (!carry[k]) continue;
120 pk[k] = svc[k].D0.rows();
121 cidx.push_back(k);
122 P *= pk[k];
123 }
124
125 mam::Mmap<T> lifted;
126 lifted.D0 = mam::kron(arrival.D0, eye<T>(P));
127 lifted.D1 = Matrix<T>(ma * P, ma * P, zero);
128 std::vector<mam::PhService<T> > ph;
129 std::vector<std::size_t> markClass;
130 const T tiny = num_traits<T>::from_double(1e-14);
131 for (std::size_t k = 0; k < K; ++k) {
132 const Matrix<T>& S0 = svc[k].D0;
133 const Matrix<T>& S1 = svc[k].D1;
134 if (!carry[k]) {
135 lifted.Dc.push_back(mam::kron(arrival.Dc[k], eye<T>(P)));
136 ph.push_back(mam::PhService<T>{mam::map_pie(svc[k]), S0});
137 markClass.push_back(k);
138 continue;
139 }
140 const std::size_t p = pk[k];
141 Matrix<T> negS0(p, p, zero);
142 for (std::size_t i = 0; i < p; ++i)
143 for (std::size_t j = 0; j < p; ++j) negS0(i, j) = -S0(i, j);
144 Matrix<T> V = matmul(inverse(negS0), S1);
145 for (std::size_t i = 0; i < p; ++i)
146 for (std::size_t j = 0; j < p; ++j)
147 if (V(i, j) < tiny && V(i, j) > -tiny) V(i, j) = zero;
148 for (std::size_t h = 0; h < p; ++h) {
149 for (std::size_t j = 0; j < p; ++j) {
150 if (!(V(h, j) > zero)) continue;
151 // carried coordinate of class k moves h -> j, the others stay
152 Matrix<T> E = eye<T>(1);
153 for (std::size_t kk : cidx) {
154 Matrix<T> Ek;
155 if (kk == k) {
156 Ek = Matrix<T>(p, p, zero);
157 Ek(h, j) = V(h, j);
158 } else {
159 Ek = eye<T>(pk[kk]);
160 }
161 E = mam::kron(E, Ek);
162 }
163 std::vector<std::size_t> Jset;
164 for (std::size_t i = 0; i < p; ++i)
165 if (V(i, j) > zero) Jset.push_back(i);
166 const std::size_t nJ = Jset.size();
167 Matrix<T> G(nJ, nJ, zero);
168 std::vector<T> sig(nJ, zero);
169 for (std::size_t a = 0; a < nJ; ++a) {
170 const std::size_t i = Jset[a];
171 G(a, a) = S0(i, i);
172 for (std::size_t bb = 0; bb < nJ; ++bb) {
173 if (bb == a) continue;
174 const std::size_t i2 = Jset[bb];
175 G(a, bb) = S0(i, i2) * V(i2, j) / V(i, j);
176 }
177 if (i == h) sig[a] = one;
178 }
179 lifted.Dc.push_back(mam::kron(arrival.Dc[k], E));
180 ph.push_back(mam::PhService<T>{sig, G});
181 markClass.push_back(k);
182 }
183 }
184 }
185 for (std::size_t m = 0; m < lifted.Dc.size(); ++m)
186 for (std::size_t i = 0; i < ma * P; ++i)
187 for (std::size_t j = 0; j < ma * P; ++j) lifted.D1(i, j) += lifted.Dc[m](i, j);
188
189 const std::vector<T> Qm = mam::mmapph1fcfs_ncmean(lifted, ph);
191 r.meanQueueLength.assign(K, zero);
192 r.meanSojournTime.assign(K, zero);
193 for (std::size_t m = 0; m < markClass.size(); ++m) r.meanQueueLength[markClass[m]] += Qm[m];
194 for (std::size_t k = 0; k < K; ++k)
195 if (lamk[k] > zero) r.meanSojournTime[k] = r.meanQueueLength[k] / lamk[k];
196 r.arrivalRate = lamk;
197 r.carry = carry;
198 r.markCount = markClass.size();
199 return r;
200}
201
202} // namespace qsys
203} // namespace line
204
205#endif // LINE_API_QSYS_QSYS_MMAPMAP1_H
InputError(const std::string &what)
Definition error.h:105
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LU factorization with partial pivoting, templated on the number type.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
The MMAP[K]/PH[K]/1 FCFS queue: per-class mean number in system and per-class queue-length distributi...
std::vector< T > mmapph1fcfs_ncmean(const Mmap< T > &arrival, const std::vector< PhService< T > > &svc)
Per-class mean number of customers in the system, BUTools' 'ncMoms', 1.
Matrix< T > kron(const Matrix< T > &A, const Matrix< T > &B)
Kronecker product.
Definition mmap_lambda.h:57
std::vector< T > map_prob(const Map< T > &m)
Stationary distribution of the phase process, pi (D0 + D1) = 0.
Definition map_moment.h:73
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
bool is_renewal(const mam::Map< T > &m)
D1 = (-D0 e) alpha to within tolerance, as mam_is_renewal_map.
MmapMap1Result< T > qsys_mmapmap1(const mam::Mmap< T > &arrival, const std::vector< mam::Map< T > > &svc, std::vector< bool > carry=std::vector< bool >())
MMAP[K]/MAP[K]/1 FCFS with per-class carried service phases, exactly.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
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
Matrix< T > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
Number-type abstraction for the templated API port.
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
One class's phase-type service law, He's (sigma_k, S_k).
Definition mmapph1fcfs.h:71
Return value of qsys_mmapmap1, mirroring the MATLAB struct.
std::vector< T > arrivalRate
class arrival rates
std::vector< T > meanQueueLength
E[N_k].
std::vector< bool > carry
classes whose service phase is carried
std::vector< T > meanSojournTime
E[W_k], by Little's law.
std::size_t markCount
marks of the lifted MMAP