LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mgck_sync.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_MGCK_SYNC_H
6#define LINE_API_QSYS_QSYS_MGCK_SYNC_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Synchronous approximation of the M/G/c/K queue with dual bounds (German,
12 * EPEW 2025).
13 *
14 * Port of matlab/src/api/qsys/qsys_mgck_sync.m. The concurrent service clocks
15 * of the c servers are replaced by synchronized ones, so that at most one
16 * non-Markovian activity is enabled at any time and the process is solved
17 * exactly by Markov renewal theory through its embedded Markov chain. Every
18 * customer receives either its true service time or a shorter one (variant
19 * "faster") or a longer one ("slower"), which brackets the M/G/c/K measures:
20 * the faster model gives a lower bound on the mean number in system, on the
21 * response time and on P(full), and an upper bound on P(idle) and the
22 * throughput; the slower model the opposite bounds. The argument is
23 * customer-wise and holds without losses; with blocking the bounds are
24 * observed, not proven.
25 *
26 * MODEL WITHOUT EXPANSION (nphases = 0), state i = jobs in system. All jobs in
27 * service share one clock. "faster": an arrival that finds a free server joins
28 * the running batch and completes with it. "slower": such an arrival preempts
29 * the clock, so the whole batch restarts with it. On expiry min(i,c) jobs
30 * depart and the next min(i-c,c) start together. For c = 1 both variants are
31 * the exact M/G/1/K queue.
32 *
33 * MODEL WITH EXPANSION (nphases = N >= 1, deterministic service only), state
34 * (a, q, B): a = 1 when the owner server is busy, q the waiting jobs, B the
35 * sorted multiset of phases completed by the other busy servers. While the
36 * owner is busy a tick of length d/N advances every phase and the owner
37 * completes after N ticks; a newcomer to another server starts at phase 0
38 * ("faster") or -1 ("slower"). With the owner idle the ticks continue; an
39 * arrival takes the owner role and preempts the tick, the other phases being
40 * rounded up ("faster") or kept ("slower"). An arrival to the empty system
41 * takes the owner role with probability 1/c.
42 *
43 * Embedded chain (German 2000, cascaded embedding for the expansion):
44 * P = I_E - diag^{-1}(Q_E) Q_E + Omega Delta + Psi Qbar
45 * C = -diag^{-1}(Q_E) + Psi, pi = u C/(u C e), u = u P
46 * with Omega = I_G E[exp(Q_G S)] and Psi = I_G E[int_0^S exp(Q_G t) dt], both
47 * evaluated by uniformization of Q_G at rate lambda from the service-law
48 * coefficients of qsys_svclaw_coeffs.h.
49 *
50 * A truncated uniformization series (residual above 1e-6) warns and continues,
51 * as the reference does.
52 *
53 * ARITHMETIC. Gated on num_traits<T>::has_transcendental: the coefficients need
54 * exp and the series truncation is a tolerance.
55 *
56 * References:
57 * [1] R. German, On Synchronous Approximations of Non-Markovian Processes with
58 * Dual Bounds, EPEW 2025.
59 * [2] R. German, Performance Analysis of Communication Systems with
60 * Non-Markovian Stochastic Petri Nets, Wiley, 2000.
61 */
62
63#include <algorithm>
64#include <cctype>
65#include <cstddef>
66#include <cstdio>
67#include <iostream>
68#include <map>
69#include <string>
70#include <vector>
71
74#include "line/num/number.h"
75#include "line/util/error.h"
77#include "line/util/linalg.h"
78#include "line/util/lu.h"
79#include "line/util/matrix.h"
80
81namespace line {
82namespace qsys {
83
84/** Return value of qsys_mgck_sync, mirroring the MATLAB result struct. */
85template <class T>
87 std::vector<T> queueLengthDist; ///< P(n jobs in system), n = 0..K
88 T p0; ///< P(idle)
89 T pK; ///< P(full)
90 T meanQueueLength; ///< mean number in system
91 T meanQueueLengthQ; ///< mean number waiting
92 T throughput; ///< lambda (1 - pK)
93 T lossProbability; ///< pK (PASTA)
94 T meanSojournTime; ///< meanQueueLength / throughput
95 T meanWaitingTime; ///< meanQueueLengthQ / throughput
96 T utilization; ///< mean busy servers / c in the synchronized model
97 std::string variant; ///< "faster" or "slower"
98 std::size_t nphases = 0;
99 std::size_t stateSpaceSize = 0;
100 std::string analyzer;
101};
102
104
105/** Row-masked copy: rows with keep[i] false are zeroed, i.e. diag(keep) M. */
106template <class T>
107Matrix<T> rowmask(const Matrix<T>& M, const std::vector<bool>& keep) {
108 Matrix<T> R = M;
109 const T zero = num_traits<T>::from_int(0);
110 for (std::size_t i = 0; i < M.rows(); ++i)
111 if (!keep[i])
112 for (std::size_t j = 0; j < M.cols(); ++j) R(i, j) = zero;
113 return R;
114}
115
116/** Column-masked copy: M diag(keep). */
117template <class T>
118Matrix<T> colmask(const Matrix<T>& M, const std::vector<bool>& keep) {
119 Matrix<T> R = M;
120 const T zero = num_traits<T>::from_int(0);
121 for (std::size_t j = 0; j < M.cols(); ++j)
122 if (!keep[j])
123 for (std::size_t i = 0; i < M.rows(); ++i) R(i, j) = zero;
124 return R;
125}
126
127template <class T>
128Matrix<T> matadd(const Matrix<T>& A, const Matrix<T>& B) {
129 Matrix<T> C = A;
130 for (std::size_t i = 0; i < A.rows(); ++i)
131 for (std::size_t j = 0; j < A.cols(); ++j) C(i, j) += B(i, j);
132 return C;
133}
134
135/**
136 * i_unif: Om = E[exp(QG S)], Ps = E[int_0^S exp(QG t) dt] from c_n at rate
137 * theta, with d_n = (1 - sum_{k<=n} c_k)/theta.
138 */
139template <class T>
140void unif(const Matrix<T>& QG, const std::vector<T>& cn, const T& theta, Matrix<T>& Om,
141 Matrix<T>& Ps) {
142 const std::size_t dim = QG.rows();
143 const T zero = num_traits<T>::from_int(0);
144 Matrix<T> P = eye<T>(dim);
145 for (std::size_t i = 0; i < dim; ++i)
146 for (std::size_t j = 0; j < dim; ++j) P(i, j) += QG(i, j) / theta;
147 const std::size_t n = cn.size();
148 // tailc(k) = sum_{m>=k} c_m; dn(k) = tailc(k+1)/theta, dn(last) = 0
149 std::vector<T> dn(n, zero);
150 {
151 T tail = zero;
152 for (std::size_t k = n; k-- > 0;) {
153 dn[k] = tail / theta;
154 tail += cn[k];
155 }
156 }
157 Om = Matrix<T>(dim, dim, zero);
158 Ps = Matrix<T>(dim, dim, zero);
159 Matrix<T> Pn = eye<T>(dim);
160 for (std::size_t k = 0; k < n; ++k) {
161 for (std::size_t i = 0; i < dim; ++i)
162 for (std::size_t j = 0; j < dim; ++j) {
163 const T v = Pn(i, j);
164 if (v == zero) continue;
165 Om(i, j) += cn[k] * v;
166 Ps(i, j) += dn[k] * v;
167 }
168 if (k + 1 < n) Pn = matmul(Pn, P);
169 }
170}
171
172/** i_emc: stationary law of the embedded chain at regenerations, converted to time. */
173template <class T>
174std::vector<T> emc(const Matrix<T>& Q, const std::vector<bool>& inE, const Matrix<T>& OmDelta,
175 const Matrix<T>& Psi, const Matrix<T>& Qbar) {
176 const std::size_t dim = Q.rows();
177 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
178 const Matrix<T> QE = rowmask(Q, inE);
179 std::vector<T> dinv(dim, zero);
180 for (std::size_t i = 0; i < dim; ++i)
181 if (QE(i, i) != zero) dinv[i] = one / QE(i, i);
182 Matrix<T> P = matadd(OmDelta, matmul(Psi, Qbar));
183 for (std::size_t i = 0; i < dim; ++i) {
184 if (inE[i]) P(i, i) += one;
185 for (std::size_t j = 0; j < dim; ++j) P(i, j) -= dinv[i] * QE(i, j);
186 }
187 Matrix<T> C = Psi;
188 for (std::size_t i = 0; i < dim; ++i) C(i, i) -= dinv[i];
189 T rowdev = zero;
190 for (std::size_t i = 0; i < dim; ++i) {
191 T s = zero;
192 for (std::size_t j = 0; j < dim; ++j) s += P(i, j);
193 const T d = num_abs(T(s - one));
194 if (d > rowdev) rowdev = d;
195 }
196 if (rowdev > num_traits<T>::from_double(1e-8))
197 throw NumericError(ErrorKind{"NonConvergence"},
198 "qsys_mgck_sync: embedded chain rows deviate from 1 by " +
199 num_traits<T>::to_string(rowdev) + "; raise nmax");
200 // A = (P - I)^T with its last row replaced by (C e)^T, b = e_dim
201 Matrix<T> A(dim, dim, zero);
202 for (std::size_t i = 0; i < dim; ++i)
203 for (std::size_t j = 0; j < dim; ++j) A(i, j) = P(j, i) - (i == j ? one : zero);
204 for (std::size_t j = 0; j < dim; ++j) {
205 T s = zero;
206 for (std::size_t k = 0; k < dim; ++k) s += C(j, k);
207 A(dim - 1, j) = s;
208 }
209 std::vector<T> b(dim, zero);
210 b[dim - 1] = one;
211 const std::vector<T> u = solve(A, b);
212 std::vector<T> pi = vecmul(u, C);
213 T tot = zero;
214 for (const T& v : pi) tot += v;
215 for (T& v : pi) v /= tot;
216 return pi;
217}
218
219template <class T>
220std::vector<T> coefficients(const ServiceLaw<T>& svc, const T& lambda, const T& tol,
221 std::size_t nmaxCap) {
222 // a truncated series warns and the solve continues, as in the reference
223 return qsys_svclaw_coeffs(svc, lambda, tol, nmaxCap).cn;
224}
225
226/** i_plain: synchronous approximation without state expansion; state i <-> i jobs. */
227template <class T>
228std::vector<T> plain(const T& lambda, std::size_t c, std::size_t K, const ServiceLaw<T>& svc,
229 bool faster, const T& tol, std::size_t nmaxCap, std::vector<std::size_t>& level) {
230 const std::size_t dim = K + 1;
231 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
232 Matrix<T> Q(dim, dim, zero), Qbar(dim, dim, zero), Delta(dim, dim, zero);
233 Q(0, 0) = -lambda;
234 Q(0, 1) = lambda;
235 for (std::size_t i = 1; i <= K; ++i) {
236 if (i < K) {
237 Q(i, i) = -lambda;
238 if (!faster && i < c) Qbar(i, i + 1) = lambda; // joining a partial batch restarts its clock
239 else Q(i, i + 1) = lambda;
240 }
241 Delta(i, i > c ? i - c : 0) = one;
242 }
243 std::vector<bool> inE(dim, false), inG(dim, true);
244 inE[0] = true;
245 inG[0] = false;
246 const std::vector<T> cn = coefficients(svc, lambda, tol, nmaxCap);
247 Matrix<T> Om, Ps;
248 unif(rowmask(Q, inG), cn, lambda, Om, Ps);
249 Om = rowmask(Om, inG);
250 Ps = rowmask(Ps, inG);
251 level.resize(dim);
252 for (std::size_t i = 0; i < dim; ++i) level[i] = i;
253 return emc(Q, inE, matmul(Om, Delta), Ps, Qbar);
254}
255
256/** A sparse transition list, summed into a dense matrix as MATLAB's sparse() does. */
257template <class T>
258struct Triplets {
259 std::vector<std::size_t> from, to;
260 std::vector<T> val;
261 void add(std::size_t i, std::size_t j, const T& v) {
262 from.push_back(i);
263 to.push_back(j);
264 val.push_back(v);
265 }
266 Matrix<T> dense(std::size_t dim) const {
267 Matrix<T> M(dim, dim, num_traits<T>::from_int(0));
268 for (std::size_t k = 0; k < val.size(); ++k) M(from[k], to[k]) += val[k];
269 return M;
270 }
271};
272
273/**
274 * i_tick: advance every non-owner phase by one tick; a completed job frees its
275 * server for a waiting one, which starts at phase 0 on the boundary.
276 */
277inline std::vector<int> tick(int a, int q, const std::vector<int>& B, int nph) {
278 std::vector<int> Bn;
279 int done = 0;
280 for (int b : B) {
281 if (b + 1 >= nph) ++done;
282 else Bn.push_back(b + 1);
283 }
284 const int start = std::min(done, q);
285 for (int k = 0; k < start; ++k) Bn.push_back(0);
286 std::sort(Bn.begin(), Bn.end());
287 std::vector<int> s;
288 s.push_back(a);
289 s.push_back(q - start);
290 s.insert(s.end(), Bn.begin(), Bn.end());
291 return s;
292}
293
294inline std::vector<int> make_state(int a, int q, const std::vector<int>& B) {
295 std::vector<int> s;
296 s.push_back(a);
297 s.push_back(q);
298 s.insert(s.end(), B.begin(), B.end());
299 return s;
300}
301
302/** i_expanded: synchronous phase expansion; states are [a, q, sorted B phases]. */
303template <class T>
304std::vector<T> expanded(const T& lambda, std::size_t c, std::size_t K, const T& d, std::size_t nphU,
305 bool faster, const T& tol, std::size_t nmaxCap,
306 std::vector<std::size_t>& level) {
307 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
308 const int lo = faster ? 0 : -1;
309 const int nph = static_cast<int>(nphU);
310 const int ci = static_cast<int>(c), Ki = static_cast<int>(K);
311 std::map<std::vector<int>, std::size_t> keys;
312 std::vector<std::vector<int>> states;
313 auto add = [&](const std::vector<int>& s) -> std::size_t {
314 const auto it = keys.find(s);
315 if (it != keys.end()) return it->second;
316 states.push_back(s);
317 const std::size_t id = states.size() - 1;
318 keys[s] = id;
319 return id;
320 };
321 add(make_state(0, 0, std::vector<int>()));
322 // transitions: Q, Qbar, DBA (tick, owner busy), DA (owner completion), DB (tick, owner idle)
323 Triplets<T> TQ, TQbar, TDBA, TDA, TDB;
324 std::vector<bool> inA, inB;
325 const T cT = num_traits<T>::from_int(static_cast<long>(c));
326 for (std::size_t head = 0; head < states.size(); ++head) {
327 const std::vector<int> s = states[head]; // copy: add() may reallocate
328 const int a = s[0], q = s[1];
329 const std::vector<int> B(s.begin() + 2, s.end());
330 const int tot = a + q + static_cast<int>(B.size());
331 inA.resize(states.size(), false);
332 inB.resize(states.size(), false);
333 if (tot == 0) {
334 TQ.add(head, head, -lambda);
335 TQ.add(head, add(make_state(1, 0, std::vector<int>())), lambda / cT);
336 if (c > 1)
337 TQ.add(head, add(make_state(0, 0, std::vector<int>(1, 0))),
338 lambda * (cT - one) / cT);
339 inA[head] = false;
340 inB[head] = false;
341 } else if (a == 1) {
342 inA[head] = true;
343 inB[head] = false;
344 if (tot < Ki) {
345 std::vector<int> t;
346 if (1 + static_cast<int>(B.size()) < ci) {
347 std::vector<int> Bn = B;
348 Bn.push_back(lo);
349 std::sort(Bn.begin(), Bn.end());
350 t = make_state(1, 0, Bn);
351 } else {
352 t = make_state(1, q + 1, B);
353 }
354 TQ.add(head, head, -lambda);
355 TQ.add(head, add(t), lambda);
356 }
357 TDBA.add(head, add(tick(1, q, B, nph)), one);
358 TDA.add(head, add(q > 0 ? make_state(1, q - 1, B) : make_state(0, 0, B)), one);
359 } else {
360 inA[head] = false;
361 inB[head] = true;
362 if (tot < Ki) {
363 std::vector<int> Bn;
364 if (faster) {
365 for (int b : B)
366 if (b + 1 < nph) Bn.push_back(b + 1); // a credited phase may complete the service
367 } else {
368 Bn = B;
369 }
370 TQ.add(head, head, -lambda);
371 TQbar.add(head, add(make_state(1, 0, Bn)), lambda);
372 }
373 TDB.add(head, add(tick(0, q, B, nph)), one);
374 }
375 }
376 const std::size_t dim = states.size();
377 inA.resize(dim, false);
378 inB.resize(dim, false);
379 std::vector<bool> inE(dim, false);
380 for (std::size_t i = 0; i < dim; ++i) inE[i] = !inA[i] && !inB[i];
381 const Matrix<T> Q = TQ.dense(dim), Qbar = TQbar.dense(dim);
382 const Matrix<T> DBA = TDBA.dense(dim), DA = TDA.dense(dim), DB = TDB.dense(dim);
383
384 const T theta = d / num_traits<T>::from_int(static_cast<long>(nphU));
385 const std::vector<T> cn =
386 coefficients(ServiceLaw<T>::deterministic(theta), lambda, tol, nmaxCap);
387 Matrix<T> eA, iA;
388 unif(rowmask(Q, inA), cn, lambda, eA, iA);
389 const Matrix<T> OmTick = matmul(rowmask(eA, inA), DBA); // one tick with the owner busy
390 const Matrix<T> PsiTick = colmask(rowmask(iA, inA), inA);
391 Matrix<T> PsiA = PsiTick;
392 Matrix<T> W = OmTick;
393 for (std::size_t j = 1; j < nphU; ++j) {
394 PsiA = matadd(PsiA, matmul(W, PsiTick));
395 W = matmul(W, OmTick);
396 }
397 const Matrix<T> OmA = matmul(W, DA); // owner service: N ticks then its completion
398 Matrix<T> eB, iB;
399 unif(rowmask(Q, inB), cn, lambda, eB, iB);
400 const Matrix<T> OmB = matmul(rowmask(eB, inB), DB);
401 const Matrix<T> PsiB = colmask(rowmask(iB, inB), inB);
402 level.resize(dim);
403 for (std::size_t i = 0; i < dim; ++i)
404 level[i] = static_cast<std::size_t>(states[i][0] + states[i][1]) + (states[i].size() - 2);
405 (void)zero;
406 return emc(Q, inE, matadd(OmA, OmB), matadd(PsiA, PsiB), Qbar);
407}
408
409} // namespace mgck_sync_detail
410
411/**
412 * Synchronous approximation of the M/G/c/K queue with dual bounds.
413 *
414 * @param lambda Poisson arrival rate (> 0)
415 * @param c number of identical FCFS servers (>= 1)
416 * @param K system capacity, jobs in service included (K >= c)
417 * @param svc service law, as for qsys_mapg1k
418 * @param variant "faster" (lower bounds on the queue) or "slower" (upper bounds)
419 * @param nphases 0 for the approximation without expansion; N >= 1 for the
420 * synchronous phase expansion, deterministic service only
421 * @param tol uniformization truncation tolerance
422 * @param nmaxCap cap on the uniformization order
423 */
424template <class T>
425MgckSyncResult<T> qsys_mgck_sync(const T& lambda, std::size_t c, std::size_t K,
426 const ServiceLaw<T>& svc, const std::string& variant,
427 std::size_t nphases, const T& tol, std::size_t nmaxCap) {
429 "qsys_mgck_sync requires transcendental arithmetic");
430 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
431 if (!(lambda > zero)) throw InputError("qsys_mgck_sync: lambda must be a positive real scalar");
432 if (c < 1) throw InputError("qsys_mgck_sync: c must be a positive integer");
433 if (K < c) throw InputError("qsys_mgck_sync: K must be an integer >= c");
434 std::string v = variant;
435 std::transform(v.begin(), v.end(), v.begin(),
436 [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
437 if (v != "faster" && v != "slower")
438 throw InputError("qsys_mgck_sync: Unknown variant '" + variant +
439 "'; use 'faster' or 'slower'.");
440 const bool faster = (v == "faster");
441
442 std::vector<std::size_t> level;
443 std::vector<T> pi;
444 if (nphases == 0) {
445 pi = mgck_sync_detail::plain(lambda, c, K, svc, faster, tol, nmaxCap, level);
446 } else {
448 throw UnsupportedError(ErrorKind{"Distribution"},
449 "qsys_mgck_sync: The synchronous phase expansion (nphases >= 1) requires a "
450 "deterministic service time.");
451 pi = mgck_sync_detail::expanded(lambda, c, K, svc.det, nphases, faster, tol, nmaxCap,
452 level);
453 }
454
456 r.queueLengthDist.assign(K + 1, zero);
457 for (std::size_t s = 0; s < pi.size(); ++s) r.queueLengthDist[level[s]] += pi[s];
458 T L = zero, Lq = zero, busy = zero;
459 for (std::size_t n = 0; n <= K; ++n) {
460 const T pn = r.queueLengthDist[n];
461 L += num_traits<T>::from_int(static_cast<long>(n)) * pn;
462 if (n > c) Lq += num_traits<T>::from_int(static_cast<long>(n - c)) * pn;
463 busy += num_traits<T>::from_int(static_cast<long>(n < c ? n : c)) * pn;
464 }
465 r.p0 = r.queueLengthDist[0];
466 r.pK = r.queueLengthDist[K];
467 r.meanQueueLength = L;
468 r.meanQueueLengthQ = Lq;
469 r.throughput = lambda * (one - r.pK);
470 r.lossProbability = r.pK;
471 r.meanSojournTime = L / r.throughput;
472 r.meanWaitingTime = Lq / r.throughput;
473 r.utilization = busy / num_traits<T>::from_int(static_cast<long>(c));
474 r.variant = v;
475 r.nphases = nphases;
476 r.stateSpaceSize = pi.size();
477 if (nphases == 0)
478 r.analyzer = "sync." + v + ":M/G/" + std::to_string(c) + "/" + std::to_string(K);
479 else
480 r.analyzer = "sync." + v + ".n" + std::to_string(nphases) + ":M/D/" + std::to_string(c) +
481 "/" + std::to_string(K);
482 return r;
483}
484
485/** qsys_mgck_sync with the reference defaults tol = 1e-12, nmax = 200000. */
486template <class T>
487MgckSyncResult<T> qsys_mgck_sync(const T& lambda, std::size_t c, std::size_t K,
488 const ServiceLaw<T>& svc, const std::string& variant,
489 std::size_t nphases = 0) {
490 return qsys_mgck_sync(lambda, c, K, svc, variant, nphases,
491 T(num_traits<T>::from_double(1e-12)), static_cast<std::size_t>(200000));
492}
493
494} // namespace qsys
495} // namespace line
496
497#endif // LINE_API_QSYS_QSYS_MGCK_SYNC_H
InputError(const std::string &what)
Definition error.h:105
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:112
UnsupportedError(const std::string &what)
Definition error.h:119
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
Running progress log of a LINE solver run (the "solver console").
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
std::vector< int > make_state(int a, int q, const std::vector< int > &B)
void unif(const Matrix< T > &QG, const std::vector< T > &cn, const T &theta, Matrix< T > &Om, Matrix< T > &Ps)
i_unif: Om = E[exp(QG S)], Ps = E[int_0^S exp(QG t) dt] from c_n at rate theta, with d_n = (1 - sum_{...
std::vector< T > emc(const Matrix< T > &Q, const std::vector< bool > &inE, const Matrix< T > &OmDelta, const Matrix< T > &Psi, const Matrix< T > &Qbar)
i_emc: stationary law of the embedded chain at regenerations, converted to time.
std::vector< int > tick(int a, int q, const std::vector< int > &B, int nph)
i_tick: advance every non-owner phase by one tick; a completed job frees its server for a waiting one...
Matrix< T > colmask(const Matrix< T > &M, const std::vector< bool > &keep)
Column-masked copy: M diag(keep).
std::vector< T > expanded(const T &lambda, std::size_t c, std::size_t K, const T &d, std::size_t nphU, bool faster, const T &tol, std::size_t nmaxCap, std::vector< std::size_t > &level)
i_expanded: synchronous phase expansion; states are [a, q, sorted B phases].
Matrix< T > matadd(const Matrix< T > &A, const Matrix< T > &B)
std::vector< T > coefficients(const ServiceLaw< T > &svc, const T &lambda, const T &tol, std::size_t nmaxCap)
std::vector< T > plain(const T &lambda, std::size_t c, std::size_t K, const ServiceLaw< T > &svc, bool faster, const T &tol, std::size_t nmaxCap, std::vector< std::size_t > &level)
i_plain: synchronous approximation without state expansion; state i <-> i jobs.
Matrix< T > rowmask(const Matrix< T > &M, const std::vector< bool > &keep)
Row-masked copy: rows with keep[i] false are zeroed, i.e.
SvclawCoeffsResult< T > qsys_svclaw_coeffs(const ServiceLaw< T > &svc, const T &theta, const T &tol, std::size_t nmaxCap)
qsys_svclaw_coeffs: uniformization coefficients of a service-time descriptor; see qsys_svclaw_coeffs....
MgckSyncResult< T > qsys_mgck_sync(const T &lambda, std::size_t c, std::size_t K, const ServiceLaw< T > &svc, const std::string &variant, std::size_t nphases, const T &tol, std::size_t nmaxCap)
Synchronous approximation of the M/G/c/K queue with dual bounds.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
T num_abs(const T &v)
Definition number.h:184
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Definition linalg.h:50
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
Definition linalg.h:36
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
Matrix< T > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
Number-type abstraction for the templated API port.
The MAP/G/1/K queue with tail drop: Markovian arrivals, an arbitrary service law F,...
Uniformization coefficients of a service-time descriptor, shared by the qsys functions that take the ...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition error.h:37
Return value of qsys_mgck_sync, mirroring the MATLAB result struct.
T meanSojournTime
meanQueueLength / throughput
T meanQueueLengthQ
mean number waiting
T utilization
mean busy servers / c in the synchronized model
T meanQueueLength
mean number in system
T meanWaitingTime
meanQueueLengthQ / throughput
T throughput
lambda (1 - pK)
std::string variant
"faster" or "slower"
std::vector< T > queueLengthDist
P(n jobs in system), n = 0..K.
Service-time descriptor, the C++ form of the MATLAB svc struct.
static ServiceLaw deterministic(const T &d)
Constant service time d.
T det
deterministic service time
A sparse transition list, summed into a dense matrix as MATLAB's sparse() does.
void add(std::size_t i, std::size_t j, const T &v)
Matrix< T > dense(std::size_t dim) const