LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mg1kk.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_MG1KK_H
6#define LINE_API_QSYS_QSYS_MG1KK_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Exact analysis of the finite-source M/G/1/K/K (machine-repair) queue.
12 *
13 * Port of matlab/src/api/qsys/qsys_mg1kk.m. K sources each operate for an
14 * Exp(alpha) time and then queue FCFS at one server of general service law B.
15 * Takacs (1962) gives the idle probability from the service LST at the
16 * points k alpha,
17 *
18 * P0 = 1 / (1 + K alpha E[S] sum_{n=0}^{K-1} C(K-1, n) prod_{k=1}^n (1 - B(k alpha))/B(k alpha)),
19 *
20 * whence X = (1 - P0)/E[S] and E[T] = K/X - 1/alpha. The queue-length law
21 * comes from the chain embedded at departures, whose transition probabilities
22 * are alternating sums of B(k alpha); MATLAB's check that its P0 agrees with
23 * Takacs' is kept: when the alternating sums cancel it warns and returns the
24 * distribution (and the variance) as NaN, the means being the Takacs sum.
25 *
26 * ARITHMETIC: double, as the reference. MATLAB's overdetermined `A \ b`
27 * (least squares on the consistent system [P' - I; 1] pi = [0; 1]) is solved
28 * here by LU on the square system that replaces one of the K dependent
29 * balance rows by the normalization; the solution of a consistent system is
30 * unique, so the two agree to rounding.
31 */
32
33#include <algorithm>
34#include <cmath>
35#include <cstdio>
36#include <functional>
37#include <iostream>
38#include <limits>
39#include <string>
40#include <vector>
41
44#include "line/util/error.h"
46#include "line/util/lu.h"
47#include "line/util/matrix.h"
48
49namespace line {
50namespace qsys {
51
52/**
53 * @brief Exact M/G/1/K/K finite-source queue, service given by its LST.
54 *
55 * @param alpha request rate of one operating source
56 * @param K number of sources
57 * @param lst service-time LST at real arguments
58 * @param ES mean service time
59 */
60inline QsysRes qsys_mg1kk(double alpha, unsigned K, const std::function<double(double)>& lst, double ES) {
62 if (!(alpha > 0.0)) throw Error("qsys_mg1kk: alpha must be a positive real scalar");
63 if (K < 1) throw Error("qsys_mg1kk: K must be a positive integer");
64 if (!lst) throw Error("qsys_mg1kk: svc must be a Distribution or an LST function handle");
65 if (!(ES > 0.0)) throw Error("qsys_mg1kk: with an LST handle the mean service time ES must be given");
66
67 std::vector<double> Bs(K + 1);
68 for (unsigned k = 0; k <= K; ++k) Bs[k] = lst(k * alpha);
69 double Bn = 1.0, S = 1.0;
70 for (unsigned nn = 1; nn + 1 <= K; ++nn) {
71 Bn = Bn * (1.0 - Bs[nn]) / Bs[nn];
72 S += nchoosek_r(K - 1.0, nn) * Bn;
73 }
74 const double P0 = 1.0 / (1.0 + K * alpha * ES * S);
75 const double X = (1.0 - P0) / ES;
76 const double ET = K / X - 1.0 / alpha;
77 const double EW = ET - ES;
78
79 Matrix<double> P(K, K, 0.0);
80 for (unsigned j = 0; j < K; ++j) {
81 const unsigned nop = j == 0 ? K - 1 : K - j;
82 const unsigned base = j == 0 ? 0 : j - 1;
83 for (unsigned i = 0; i <= nop; ++i) {
84 double acc = 0.0;
85 for (unsigned l = 0; l <= i; ++l)
86 acc += ((l % 2) ? -1.0 : 1.0) * nchoosek_r(i, l) * Bs[nop - i + l];
87 P(j, base + i) += nchoosek_r(nop, i) * acc;
88 }
89 }
90 for (unsigned j = 0; j < K; ++j) {
91 double rs = 0.0;
92 for (unsigned i = 0; i < K; ++i) {
93 P(j, i) = std::max(P(j, i), 0.0);
94 rs += P(j, i);
95 }
96 for (unsigned i = 0; i < K; ++i) P(j, i) /= rs;
97 }
98 // [P' - I] with its last balance row replaced by the normalization
99 Matrix<double> A(K, K, 0.0);
100 std::vector<double> b(K, 0.0);
101 for (unsigned r = 0; r + 1 < K; ++r)
102 for (unsigned cc = 0; cc < K; ++cc) A(r, cc) = P(cc, r) - (r == cc ? 1.0 : 0.0);
103 for (unsigned cc = 0; cc < K; ++cc) A(K - 1, cc) = 1.0;
104 b[K - 1] = 1.0;
105 std::vector<double> Pi = solve(A, b);
106
107 std::vector<double> pn(K + 1, 0.0);
108 double mass = 0.0;
109 for (unsigned n = 0; n < K; ++n) {
110 pn[n] = X * Pi[n] / ((static_cast<double>(K) - n) * alpha);
111 mass += pn[n];
112 }
113 pn[K] = 1.0 - mass;
114 if (std::fabs(pn[0] - P0) > 1e-6 * std::max(P0, 1e-12) || pn[K] < -1e-9) {
115 char buf[384];
116 std::snprintf(buf, sizeof buf,
117 "qsys_mg1kk: the embedded-chain distribution disagrees with the Takacs P_0 (%g vs %g): "
118 "the alternating arrival sums lost accuracy at K=%u; queueLengthDist, arrivalDist "
119 "and varQueueLength are returned as NaN, the means are unaffected.",
120 pn[0], P0, K);
121 // the means above are the Takacs sum of positive terms and do not
122 // read the chain, so only the distribution is withdrawn
124 std::cerr << "[LINE] Warning: " << buf << std::endl;
125 std::fill(pn.begin(), pn.end(), std::numeric_limits<double>::quiet_NaN());
126 std::fill(Pi.begin(), Pi.end(), std::numeric_limits<double>::quiet_NaN());
127 } else {
128 pn[K] = std::max(pn[K], 0.0);
129 }
130 double EN = 0.0, EN2 = 0.0;
131 for (unsigned n = 0; n <= K; ++n) {
132 EN += n * pn[n];
133 EN2 += static_cast<double>(n) * n * pn[n];
134 }
135 QsysRes r;
136 r.set("meanQueueLength", X * ET);
137 r.set("varQueueLength", EN2 - EN * EN);
138 r.set("meanQueueLengthQ", X * EW);
139 r.set("meanWaitingTime", EW);
140 r.set("meanSojournTime", ET);
141 r.set("throughput", X);
142 r.set("utilization", 1.0 - P0);
143 r.set("meanOperating", K - X * ET);
144 r.set("queueLengthDist", pn);
145 r.set("arrivalDist", Pi);
146 r.analyzer = "exact:M/G/1/" + std::to_string(K) + "/" + std::to_string(K);
147 return r;
148}
149
150/**
151 * @brief Exact M/G/1/K/K finite-source queue, service given as a Distribution
152 * (its LST and mean, as MATLAB's svc.evalLST / svc.getMean).
153 */
154inline QsysRes qsys_mg1kk(double alpha, unsigned K, const lang::Distrib<double>& svc) {
155 const lang::Distrib<double> d = svc;
156 return qsys_mg1kk(alpha, K, [d](double s) { return lang::dist_lst(d, s); }, lang::dist_moment(d, 1));
157}
158
159} // namespace qsys
160} // namespace line
161
162#endif // LINE_API_QSYS_QSYS_MG1KK_H
Error(const std::string &what)
Definition error.h:75
static VerboseLevel get_verbose()
The session verbosity.
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
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.
T dist_lst(const Distrib< T > &d, const T &s)
sn.lst: the Laplace-Stieltjes transform E[exp(-sX)].
T dist_moment(const Distrib< T > &d, unsigned k)
The k-th raw moment.
double nchoosek_r(double n, double k)
The reference's nchoosek_r: exp(gammaln) rounded to the integer it is while that integer is exactly r...
Definition qsys_res.h:230
QsysRes qsys_mg1kk(double alpha, unsigned K, const std::function< double(double)> &lst, double ES)
Exact M/G/1/K/K finite-source queue, service given by its LST.
Definition qsys_mg1kk.h:60
QsysResT< double > QsysRes
Definition qsys_res.h:96
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
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
The res struct the exact queueing-system functions return, and the numeric helpers those functions sh...
std::string analyzer
Definition qsys_res.h:59
void set(const std::string &name, const T &v)
Sets (or overwrites) a scalar field.
Definition qsys_res.h:62