LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mapphc.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_MAPPHC_H
6#define LINE_API_QSYS_QSYS_MAPPHC_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * The MAP/PH/c FCFS queue, solved exactly.
12 *
13 * THE STATE SPACE, AND WHY IT IS A MULTISET. With c identical servers the
14 * server identities carry no information, so the service phases are held as a
15 * MULTISET: a configuration is n = (n_1..n_ms) with sum(n) = k servers busy in
16 * phase i. There are binomial(ms+k-1,k) of those, the count of Asmussen and
17 * Moller (2001), against ms^k for the ordered space -- for ms = 5, c = 6 that
18 * is 210 against 15625, which is what makes the exact solve feasible at all.
19 * Levels 0..c-1 are the boundary (level = servers busy), levels >= c repeat and
20 * carry the queue, so the tail is matrix-geometric in R.
21 *
22 * THE WAITING TIME, AND WHY IT IS ONE LINEAR ODE. An arrival that finds j
23 * customers waiting ahead of it waits for exactly j+1 service completions, so
24 * Wq is the (j+1)-st event time of the configuration MAP (Lc, Cdep) started at
25 * the arrival-epoch configuration. The level distribution seen by an arrival is
26 * matrix-geometric, x_j = pi_c R^j kron(D1,I)/lambda, and folding over j gives
27 *
28 * G'(t) = G(t) Lj + R G(t) Cj, G(0) = (I-R)^-1 kron(D1,I)/lambda,
29 * P(Wq > t) = pi_c G(t) e.
30 *
31 * That is LINEAR in G, so Wq is matrix-exponential; vectorizing it column-major
32 * turns it into a single expm of order (ma*nc)^2. Its Laplace transform obeys
33 * the generalized Sylvester equation g(sI-Lj) - R g Cj = G(0), and
34 * differentiating that identity leaves the OPERATOR unchanged and only moves
35 * the right-hand side, so every moment is one more solve with the same matrix.
36 *
37 * WHY NOT REUSE qsys_mapmc. That function is MAP/M/c: its phase is the arrival
38 * phase alone, because an exponential server has nothing to remember. Here the
39 * phase must also carry which service phase each busy server occupies, and the
40 * down block is a completion followed by an IMMEDIATE restart at alpha, which
41 * has no counterpart in the exponential case. The two agree exactly when
42 * ms = 1, and the test file checks that they do.
43 *
44 * ARITHMETIC. Gated on num_traits<T>::has_transcendental: logarithmic reduction
45 * drives R to a tolerance and never terminates in a finite number of field
46 * operations, as in qbd_r.h. Everything consuming R is finite exact matrix
47 * algebra, except the CCDF, which needs expm.
48 *
49 * References:
50 * S. Asmussen and J.R. Moller, "Calculation of the steady state waiting time
51 * distribution in GI/PH/c and MAP/PH/c queues", Queueing Systems 37(1):9-29,
52 * 2001.
53 * D.P. Gaver, P.A. Jacobs, G. Latouche, "Finite birth-and-death models in
54 * randomly changing environments", Adv. Appl. Probab. 16:715-731, 1984.
55 */
56
57#include <cstddef>
58#include <vector>
59
62#include "line/api/mam/qbd_r.h"
63#include "line/num/number.h"
64#include "line/util/error.h"
65#include "line/util/expm.h"
66#include "line/util/linalg.h"
67#include "line/util/lu.h"
68#include "line/util/matrix.h"
69
70namespace line {
71namespace qsys {
72
73/** Return value of qsys_mapphc, mirroring the MATLAB struct. */
74template <class T>
76 T meanQueueLength; ///< E[N], number in system
77 T meanWaitingTime; ///< E[Wq], time in queue
78 T meanSojournTime; ///< E[Wq] + E[service]
79 T utilization; ///< rho = lambda E[service] / c, per server
80 std::vector<T> queueLengthDist; ///< P(N = n), n = 0, 1, ...
81 std::vector<T> waitingTimeMoments;///< E[Wq^k], k = 1..num_w_moms
82 std::vector<T> waitingTimeCCDF; ///< P(Wq > t) at the requested points
83 std::vector<T> waitingTimePoints; ///< the requested points
84 T probWait; ///< P(Wq > 0), an arrival finds every server busy
85 std::size_t phaseCount; ///< binomial(ms+c-1,c), the repeating config count
86};
87
88namespace mapphcdetail {
89
90/** Elementwise A + B; the qbd_detail twin is not reachable from here. */
91template <class T>
92Matrix<T> madd2(const Matrix<T>& A, const Matrix<T>& B) {
93 if (A.rows() != B.rows() || A.cols() != B.cols())
94 throw InputError("qsys_mapphc: shape mismatch");
95 Matrix<T> C = A;
96 for (std::size_t i = 0; i < A.rows(); ++i)
97 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) += B(i, j);
98 return C;
99}
100
101/** Kronecker product; C++ has no shared templated kron. */
102template <class T>
103Matrix<T> mkron(const Matrix<T>& A, const Matrix<T>& B) {
104 Matrix<T> C(A.rows() * B.rows(), A.cols() * B.cols(), num_traits<T>::from_int(0));
105 for (std::size_t i = 0; i < A.rows(); ++i)
106 for (std::size_t j = 0; j < A.cols(); ++j)
107 for (std::size_t p = 0; p < B.rows(); ++p)
108 for (std::size_t q = 0; q < B.cols(); ++q)
109 C(i * B.rows() + p, j * B.cols() + q) = A(i, j) * B(p, q);
110 return C;
111}
112
113/**
114 * Compositions of k into ms nonnegative parts, in a fixed order. Shared with the
115 * exact M/PH/c LD-QBD blocks, so a configuration index means the same thing in
116 * both.
117 */
118inline std::vector<std::vector<int> > multisets(std::size_t ms, std::size_t k) {
119 return mam::ph_multisets(ms, k);
120}
121
122inline std::size_t find_cfg(const std::vector<std::vector<int> >& rows, const std::vector<int>& key) {
123 for (std::size_t i = 0; i < rows.size(); ++i)
124 if (rows[i] == key) return i;
125 throw InputError("qsys_mapphc: configuration not found");
126}
127
128} // namespace mapphcdetail
129
130/**
131 * MAP/PH/c FCFS, exactly.
132 *
133 * @param arrival arrival MAP (D0, D1) of order ma
134 * @param alpha PH service initial vector of order ms
135 * @param S PH service sub-generator of order ms
136 * @param c number of servers, c >= 1
137 * @param dist_size cap on the queue length probabilities materialized
138 * @param num_w_moms how many waiting-time moments to return
139 * @param w_points times at which to evaluate P(Wq > t)
140 */
141template <class T>
142MapPhcResult<T> qsys_mapphc(const mam::Map<T>& arrival, const std::vector<T>& alpha,
143 const Matrix<T>& S, unsigned c, std::size_t dist_size,
144 std::size_t num_w_moms, const std::vector<T>& w_points) {
146 "qsys_mapphc requires transcendental arithmetic");
147 using mapphcdetail::find_cfg;
148 using mapphcdetail::madd2;
149 using mapphcdetail::mkron;
150 using mapphcdetail::multisets;
151
152 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
153 const Matrix<T>& D0 = arrival.D0;
154 const Matrix<T>& D1 = arrival.D1;
155 const std::size_t ma = D0.rows();
156 const std::size_t ms = S.rows();
157 if (D0.cols() != ma || D1.rows() != ma || D1.cols() != ma)
158 throw InputError("qsys_mapphc: D0 and D1 must be square and of equal order");
159 if (S.cols() != ms || alpha.size() != ms)
160 throw InputError("qsys_mapphc: alpha and S must have matching order");
161 if (c < 1) throw InputError("qsys_mapphc: c must be a positive integer");
162 if (dist_size == 0) throw InputError("qsys_mapphc: dist_size must be positive");
163
164 std::vector<T> s0(ms, zero);
165 for (std::size_t i = 0; i < ms; ++i) {
166 T r = zero;
167 for (std::size_t j = 0; j < ms; ++j) r += S(i, j);
168 s0[i] = -r;
169 }
170
171 const T lambda = mam::map_lambda(arrival);
172 if (lambda <= zero) throw InputError("qsys_mapphc: non-positive arrival rate");
173 // E[service] = -alpha S^-1 e
174 const std::vector<T> negSinv_e = line::solve(S, ones<T>(ms));
175 T mean_service = zero;
176 for (std::size_t i = 0; i < ms; ++i) mean_service -= alpha[i] * negSinv_e[i];
177 const T ct = num_traits<T>::from_int(static_cast<long>(c));
178 const T rho = lambda * mean_service / ct;
179 if (rho >= one) throw InputError("qsys_mapphc: load rho must be strictly less than 1");
180
181 std::vector<std::vector<std::vector<int> > > cfg(c + 1);
182 for (unsigned k = 0; k <= c; ++k) cfg[k] = multisets(ms, k);
183
184 std::vector<Matrix<T> > Lcfg, Up, Dn;
185 for (unsigned k = 0; k <= c; ++k) {
186 const std::vector<std::vector<int> >& Ck = cfg[k];
187 const std::size_t nk = Ck.size();
188 Matrix<T> Lk(nk, nk, zero);
189 for (std::size_t row = 0; row < nk; ++row) {
190 const std::vector<int>& n = Ck[row];
191 for (std::size_t i = 0; i < ms; ++i) {
192 if (n[i] == 0) continue;
193 const T ni = num_traits<T>::from_int(n[i]);
194 for (std::size_t j = 0; j < ms; ++j) {
195 if (j == i) continue;
196 std::vector<int> m = n;
197 --m[i];
198 ++m[j];
199 Lk(row, find_cfg(Ck, m)) += ni * S(i, j);
200 }
201 Lk(row, row) += ni * S(i, i);
202 }
203 }
204 Lcfg.push_back(Lk);
205
206 if (k < c) {
207 const std::vector<std::vector<int> >& Ck1 = cfg[k + 1];
208 Matrix<T> Uk(nk, Ck1.size(), zero);
209 for (std::size_t row = 0; row < nk; ++row) {
210 for (std::size_t j = 0; j < ms; ++j) {
211 std::vector<int> m = Ck[row];
212 ++m[j];
213 Uk(row, find_cfg(Ck1, m)) += alpha[j];
214 }
215 }
216 Up.push_back(Uk);
217 } else {
218 Up.push_back(Matrix<T>(1, 1, zero));
219 }
220
221 if (k > 0) {
222 const std::vector<std::vector<int> >& Ckm = cfg[k - 1];
223 Matrix<T> Dk(nk, Ckm.size(), zero);
224 for (std::size_t row = 0; row < nk; ++row) {
225 const std::vector<int>& n = Ck[row];
226 for (std::size_t i = 0; i < ms; ++i) {
227 if (n[i] == 0) continue;
228 std::vector<int> m = n;
229 --m[i];
230 Dk(row, find_cfg(Ckm, m)) += num_traits<T>::from_int(n[i]) * s0[i];
231 }
232 }
233 Dn.push_back(Dk);
234 } else {
235 Dn.push_back(Matrix<T>(1, 1, zero));
236 }
237 }
238
239 // Completion WITH an immediate restart: the repeating down block
240 const std::vector<std::vector<int> >& Cc = cfg[c];
241 const std::size_t nc = Cc.size();
242 Matrix<T> Cdep(nc, nc, zero);
243 for (std::size_t row = 0; row < nc; ++row) {
244 const std::vector<int>& n = Cc[row];
245 for (std::size_t i = 0; i < ms; ++i) {
246 if (n[i] == 0) continue;
247 const T ni = num_traits<T>::from_int(n[i]);
248 for (std::size_t j = 0; j < ms; ++j) {
249 std::vector<int> m = n;
250 --m[i];
251 ++m[j];
252 Cdep(row, find_cfg(Cc, m)) += ni * s0[i] * alpha[j];
253 }
254 }
255 }
256
257 const Matrix<T> Ima = eye<T>(ma);
258 const Matrix<T> Inc = eye<T>(nc);
259 const Matrix<T> A_up = mkron(D1, Inc);
260 const Matrix<T> A_loc = madd2(mkron(D0, Inc), mkron(Ima, Lcfg[c]));
261 const Matrix<T> A_dn = mkron(Ima, Cdep);
262 const Matrix<T> R = mam::qbd_R_logred(A_dn, A_loc, A_up);
263
264 const std::size_t n_op = nc * ma;
265 Matrix<T> ImR(n_op, n_op, zero);
266 for (std::size_t i = 0; i < n_op; ++i)
267 for (std::size_t j = 0; j < n_op; ++j) ImR(i, j) = (i == j ? one : zero) - R(i, j);
268 const Matrix<T> ImRinv = inverse(ImR);
269 const std::vector<T> sum_geom = mulvec(ImRinv, ones<T>(n_op));
270
271 // Boundary levels 0..c, with the tail folded into level c through R
272 std::vector<std::size_t> sz(c + 1), off(c + 2, 0);
273 for (unsigned k = 0; k <= c; ++k) {
274 sz[k] = ma * cfg[k].size();
275 off[k + 1] = off[k] + sz[k];
276 }
277 const std::size_t tot = off[c + 1];
278 Matrix<T> Q(tot, tot, zero);
279 const Matrix<T> RA_dn = matmul(R, A_dn);
280 for (unsigned k = 0; k <= c; ++k) {
281 const Matrix<T> Ick = eye<T>(cfg[k].size());
282 const Matrix<T> diag = (k < c) ? madd2(mkron(D0, Ick), mkron(Ima, Lcfg[k]))
283 : madd2(A_loc, RA_dn);
284 for (std::size_t i = 0; i < sz[k]; ++i)
285 for (std::size_t j = 0; j < sz[k]; ++j) Q(off[k] + i, off[k] + j) += diag(i, j);
286 if (k < c) {
287 const Matrix<T> up = mkron(D1, Up[k]);
288 for (std::size_t i = 0; i < up.rows(); ++i)
289 for (std::size_t j = 0; j < up.cols(); ++j) Q(off[k] + i, off[k + 1] + j) += up(i, j);
290 }
291 if (k > 0) {
292 const Matrix<T> dn = mkron(Ima, Dn[k]);
293 for (std::size_t i = 0; i < dn.rows(); ++i)
294 for (std::size_t j = 0; j < dn.cols(); ++j) Q(off[k] + i, off[k - 1] + j) += dn(i, j);
295 }
296 }
297
298 // pi Q = 0 with the normalization replacing the last equation, transposed
299 // into the column-vector solve line::solve expects.
300 Matrix<T> M(tot, tot, zero);
301 for (std::size_t i = 0; i < tot; ++i)
302 for (std::size_t j = 0; j < tot; ++j) M(j, i) = Q(i, j);
303 for (std::size_t col = 0; col < tot; ++col) M(tot - 1, col) = zero;
304 for (std::size_t i = 0; i < off[c]; ++i) M(tot - 1, i) = one;
305 for (std::size_t i = 0; i < sz[c]; ++i) M(tot - 1, off[c] + i) = sum_geom[i];
306 std::vector<T> b(tot, zero);
307 b[tot - 1] = one;
308 const std::vector<T> pi_vec = line::solve(M, b);
309
310 std::vector<T> pi_c(n_op);
311 for (std::size_t i = 0; i < n_op; ++i) pi_c[i] = pi_vec[off[c] + i];
312
313 // Queue length distribution
314 std::vector<T> ql;
315 for (unsigned k = 0; k < c; ++k) {
316 T s = zero;
317 for (std::size_t i = 0; i < sz[k]; ++i) s += pi_vec[off[k] + i];
318 ql.push_back(s);
319 }
320 std::vector<T> tail = pi_c;
321 T acc = zero;
322 for (std::size_t i = 0; i < ql.size(); ++i) acc += ql[i];
323 {
324 T s = zero;
325 for (std::size_t i = 0; i < n_op; ++i) s += tail[i];
326 ql.push_back(s);
327 acc += s;
328 }
329 while (acc < one - num_traits<T>::from_double(1e-12) && ql.size() < dist_size) {
330 std::vector<T> next(n_op, zero);
331 for (std::size_t j = 0; j < n_op; ++j)
332 for (std::size_t i = 0; i < n_op; ++i) next[j] += tail[i] * R(i, j);
333 tail = next;
334 T s = zero;
335 for (std::size_t i = 0; i < n_op; ++i) s += tail[i];
336 ql.push_back(s);
337 acc += s;
338 }
339 // E[N] in CLOSED FORM. dist_size caps the probabilities RETURNED, not the mean:
340 // summing the truncated list loses the matrix-geometric tail, which at rho -> 1
341 // carries a first-order share of the mass. With pi_{c+j} = pi_c R^j,
342 // sum_j (c+j) pi_c R^j e = pi_c [c (I-R)^-1 + R (I-R)^-2] e.
343 T meanQL = zero;
344 for (unsigned k = 0; k < c; ++k)
345 meanQL += num_traits<T>::from_int(static_cast<long>(k)) * ql[k];
346 const std::vector<T> u_tail = mulvec(ImRinv, ones<T>(n_op));
347 const std::vector<T> r_tail = mulvec(R, mulvec(ImRinv, u_tail));
348 const T c_scal = num_traits<T>::from_int(static_cast<long>(c));
349 for (std::size_t i = 0; i < n_op; ++i)
350 meanQL += pi_c[i] * (c_scal * u_tail[i] + r_tail[i]);
351
352 // Waiting time
353 const Matrix<T> Lj = mkron(Ima, Lcfg[c]);
354 const Matrix<T> Cj = mkron(Ima, Cdep);
355 Matrix<T> G0 = matmul(ImRinv, mkron(D1, Inc));
356 for (std::size_t i = 0; i < n_op; ++i)
357 for (std::size_t j = 0; j < n_op; ++j) G0(i, j) = G0(i, j) / lambda;
358 T probWait = zero;
359 {
360 const std::vector<T> v = mulvec(G0, ones<T>(n_op));
361 for (std::size_t i = 0; i < n_op; ++i) probWait += pi_c[i] * v[i];
362 }
363
364 // X(-Lj) - R X Cj = rhs, vectorized column-major
365 const std::size_t n2 = n_op * n_op;
366 Matrix<T> Kop(n2, n2, zero);
367 for (std::size_t a = 0; a < n_op; ++a) {
368 for (std::size_t bcol = 0; bcol < n_op; ++bcol) {
369 // contribution of X(-Lj): entry (i,a) gets -Lj(bcol,a) X(i,bcol)
370 for (std::size_t i = 0; i < n_op; ++i)
371 Kop(a * n_op + i, bcol * n_op + i) += -Lj(bcol, a);
372 // contribution of -R X Cj: entry (i,a) gets -R(i,p) X(p,bcol) Cj(bcol,a)
373 for (std::size_t i = 0; i < n_op; ++i)
374 for (std::size_t p = 0; p < n_op; ++p)
375 Kop(a * n_op + i, bcol * n_op + p) += -R(i, p) * Cj(bcol, a);
376 }
377 }
378 std::vector<T> wMoms;
379 Matrix<T> gPrev(n_op, n_op, zero);
380 for (std::size_t k = 1; k <= num_w_moms; ++k) {
381 std::vector<T> rhs(n2, zero);
382 for (std::size_t j = 0; j < n_op; ++j)
383 for (std::size_t i = 0; i < n_op; ++i)
384 rhs[j * n_op + i] = (k == 1)
385 ? G0(i, j)
386 : -num_traits<T>::from_int(static_cast<long>(k - 1)) * gPrev(i, j);
387 const std::vector<T> gv = line::solve(Kop, rhs);
388 Matrix<T> g(n_op, n_op, zero);
389 for (std::size_t j = 0; j < n_op; ++j)
390 for (std::size_t i = 0; i < n_op; ++i) g(i, j) = gv[j * n_op + i];
391 const std::vector<T> v = mulvec(g, ones<T>(n_op));
392 T mk = zero;
393 for (std::size_t i = 0; i < n_op; ++i) mk += pi_c[i] * v[i];
394 mk = mk * num_traits<T>::from_int(static_cast<long>(k));
395 if (k % 2 == 0) mk = -mk;
396 wMoms.push_back(mk);
397 gPrev = g;
398 }
399
400 std::vector<T> wCCDF;
401 if (!w_points.empty()) {
402 Matrix<T> Kt(n2, n2, zero);
403 for (std::size_t a = 0; a < n_op; ++a) {
404 for (std::size_t bcol = 0; bcol < n_op; ++bcol) {
405 for (std::size_t i = 0; i < n_op; ++i)
406 Kt(a * n_op + i, bcol * n_op + i) += Lj(bcol, a);
407 for (std::size_t i = 0; i < n_op; ++i)
408 for (std::size_t p = 0; p < n_op; ++p)
409 Kt(a * n_op + i, bcol * n_op + p) += R(i, p) * Cj(bcol, a);
410 }
411 }
412 std::vector<T> v0(n2, zero);
413 for (std::size_t j = 0; j < n_op; ++j)
414 for (std::size_t i = 0; i < n_op; ++i) v0[j * n_op + i] = G0(i, j);
415 for (std::size_t it = 0; it < w_points.size(); ++it) {
416 Matrix<T> Kts(n2, n2, zero);
417 for (std::size_t i = 0; i < n2; ++i)
418 for (std::size_t j = 0; j < n2; ++j) Kts(i, j) = Kt(i, j) * w_points[it];
419 const std::vector<T> vt = mulvec(line::expm(Kts), v0);
420 Matrix<T> Gt(n_op, n_op, zero);
421 for (std::size_t j = 0; j < n_op; ++j)
422 for (std::size_t i = 0; i < n_op; ++i) Gt(i, j) = vt[j * n_op + i];
423 const std::vector<T> v = mulvec(Gt, ones<T>(n_op));
424 T s = zero;
425 for (std::size_t i = 0; i < n_op; ++i) s += pi_c[i] * v[i];
426 wCCDF.push_back(s);
427 }
428 }
429
431 r.meanQueueLength = meanQL;
432 r.meanWaitingTime = wMoms.empty() ? zero : wMoms[0];
433 r.meanSojournTime = r.meanWaitingTime + mean_service;
434 r.utilization = rho;
435 r.queueLengthDist = ql;
436 r.waitingTimeMoments = wMoms;
437 r.waitingTimeCCDF = wCCDF;
438 r.waitingTimePoints = w_points;
439 r.probWait = probWait;
440 r.phaseCount = nc;
441 return r;
442}
443
444template <class T>
445MapPhcResult<T> qsys_mapphc(const mam::Map<T>& arrival, const std::vector<T>& alpha,
446 const Matrix<T>& S, unsigned c) {
447 return qsys_mapphc(arrival, alpha, S, c, static_cast<std::size_t>(500),
448 static_cast<std::size_t>(3), std::vector<T>());
449}
450
451} // namespace qsys
452} // namespace line
453
454#endif // LINE_API_QSYS_QSYS_MAPPHC_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
The exception types the port throws.
Matrix exponential by scaling and squaring with a diagonal Pade approximant.
Port of ldqbd_mphc.m and ph_multisets.m: the exact level-dependent QBD blocks of an M/PH/c queue.
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.
std::vector< std::vector< int > > ph_multisets(std::size_t p, std::size_t k)
Configurations of k identical servers over p service phases.
Definition ldqbd_mphc.h:63
Matrix< T > qbd_R_logred(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, unsigned iter_max, const T &tol)
R by logarithmic reduction (qbd_R_logred.m).
Definition qbd_r.h:217
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
MapPhcResult< T > qsys_mapphc(const mam::Map< T > &arrival, const std::vector< T > &alpha, const Matrix< T > &S, unsigned c, std::size_t dist_size, std::size_t num_w_moms, const std::vector< T > &w_points)
MAP/PH/c FCFS, exactly.
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
std::vector< T > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
Definition linalg.h:62
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Definition lu.h:158
std::vector< T > ones(std::size_t n)
Column vector of ones, the ubiquitous e in MAP algebra.
Definition linalg.h:104
Matrix< T > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
Matrix< T > expm(const Matrix< T > &A)
Matrix exponential exp(A).
Definition expm.h:141
Number-type abstraction for the templated API port.
Quasi-birth-death processes: the rate matrix R, the fundamental matrix G, the caudal characteristic,...
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
Return value of qsys_mapphc, mirroring the MATLAB struct.
Definition qsys_mapphc.h:75
std::vector< T > waitingTimePoints
the requested points
Definition qsys_mapphc.h:83
T meanWaitingTime
E[Wq], time in queue.
Definition qsys_mapphc.h:77
T probWait
P(Wq > 0), an arrival finds every server busy.
Definition qsys_mapphc.h:84
std::size_t phaseCount
binomial(ms+c-1,c), the repeating config count
Definition qsys_mapphc.h:85
std::vector< T > waitingTimeMoments
E[Wq^k], k = 1..num_w_moms.
Definition qsys_mapphc.h:81
std::vector< T > waitingTimeCCDF
P(Wq > t) at the requested points.
Definition qsys_mapphc.h:82
T utilization
rho = lambda E[service] / c, per server
Definition qsys_mapphc.h:79
T meanQueueLength
E[N], number in system.
Definition qsys_mapphc.h:76
std::vector< T > queueLengthDist
P(N = n), n = 0, 1, ...
Definition qsys_mapphc.h:80
T meanSojournTime
E[Wq] + E[service].
Definition qsys_mapphc.h:78