LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_md1.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_MD1_H
6#define LINE_API_QSYS_QSYS_MD1_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Exact analysis of the M/D/1 queue: the M/G/1 moments with deterministic
12 * service plus the queue-length distribution.
13 *
14 * Port of matlab/src/api/qsys/qsys_md1.m. The mean measures are qsys_mg1's
15 * with cs = 0 and E[S^3] = 1/mu^3. The distribution follows from the
16 * embedded-chain recursion with a_0 = exp(-rho) and the Poisson tails
17 * Abar_k = P(N > k):
18 *
19 * p_0 = 1 - rho, p_n = (p_0 Abar_{n-1} + sum_{j=1}^{n-1} p_j Abar_{n-j}) / a_0,
20 *
21 * run until the mass left is below 1e-14 (or to nmax when given).
22 *
23 * ARITHMETIC: double, as the reference.
24 */
25
26#include <cmath>
27#include <vector>
28
31#include "line/util/error.h"
32
33namespace line {
34namespace qsys {
35
36/**
37 * @brief Exact M/D/1 queue.
38 *
39 * @param lambda arrival rate
40 * @param mu service rate (service time 1/mu)
41 * @param nmax truncation of the distribution; negative for MATLAB's default
42 */
43inline QsysRes qsys_md1(double lambda, double mu, long nmax = -1) {
44 using namespace resdetail;
45 if (!(lambda > 0.0) || !(mu > 0.0)) throw Error("qsys_md1: lambda and mu must be positive scalars");
46 const double rho = lambda / mu;
47 if (rho >= 1.0) throw_unstable("qsys_md1", "rho", rho);
48 QsysRes res = qsys_mg1_res(lambda, mu, 0.0, 1.0 / (mu * mu * mu));
49
50 const long cap = nmax >= 0 ? nmax : 100000;
51 std::vector<double> P(static_cast<std::size_t>(cap + 1), 0.0);
52 P[0] = 1.0 - rho;
53 const double a0 = std::exp(-rho);
54 std::vector<double> Ab; // Ab[k] = Abar_k, up to and including the first zero
55 for (long k = 0; k <= cap; ++k) {
56 Ab.push_back(gammainc_lower(rho, k + 1.0));
57 if (Ab.back() == 0.0) break;
58 }
59 auto abar = [&](long k) { return k < static_cast<long>(Ab.size()) ? Ab[static_cast<std::size_t>(k)] : 0.0; };
60 double mass = P[0];
61 long last = cap;
62 for (long n = 1; n <= cap; ++n) {
63 double s = P[0] * abar(n - 1);
64 for (long j = 1; j <= n - 1; ++j) {
65 const double ab = abar(n - j);
66 if (ab == 0.0) continue;
67 s += P[static_cast<std::size_t>(j)] * ab;
68 }
69 P[static_cast<std::size_t>(n)] = s / a0;
70 mass += P[static_cast<std::size_t>(n)];
71 if (nmax < 0 && 1.0 - mass < 1e-14) {
72 last = n;
73 break;
74 }
75 }
76 P.resize(static_cast<std::size_t>(last + 1));
77 res.set("queueLengthDist", P);
78 res.analyzer = "exact:M/D/1";
79 return res;
80}
81
82} // namespace qsys
83} // namespace line
84
85#endif // LINE_API_QSYS_QSYS_MD1_H
Error(const std::string &what)
Definition error.h:75
The exception types the port throws.
QsysRes qsys_mg1_res(double lambda, double mu, double cs, double ES3=-1.0)
The res output of qsys_mg1 (Pollaczek-Khinchine and Takacs).
Definition qsys_mg1.h:60
QsysResT< double > QsysRes
Definition qsys_res.h:96
QsysRes qsys_md1(double lambda, double mu, long nmax=-1)
Exact M/D/1 queue.
Definition qsys_md1.h:43
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Exact mean response time of the M/G/1 queue (Pollaczek-Khinchine).
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