LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mmck.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_MMCK_H
6#define LINE_API_QSYS_QSYS_MMCK_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Exact analysis of the M/M/c/K queue (truncated Erlang form).
12 *
13 * Templated port of matlab/src/api/qsys/qsys_mmck.m, cross-checked against
14 * jar/src/main/java/jline/api/qsys/Qsys_mmck.java.
15 *
16 * a = lambda/mu, rho = a/c
17 * p_n = a^n/n! p_0 0 <= n <= c
18 * p_n = a^c/c! rho^(n-c) p_0 c <= n <= K
19 * p_0 = 1 / [ sum_{n<c} a^n/n! + (a^c/c!) sum_{n=0}^{K-c} rho^n ]
20 *
21 * All exponents are integers and the normalization is a finite sum, so the
22 * whole computation stays in the field of the inputs and is exact for
23 * T = Rational. The exact instantiation is not academic here: MATLAB builds
24 * the unnormalized vector as a^n/n! and warns about overflow, which is the
25 * failure mode for large a and K; in rational arithmetic the intermediate
26 * magnitudes are irrelevant.
27 *
28 * Unlike the unbounded M/M/c, no stability condition is needed: a finite
29 * capacity makes every load admissible, rho >= 1 included.
30 */
31
32#include <cstddef>
33#include <vector>
34
36#include "line/num/number.h"
37#include "line/util/error.h"
38
39namespace line {
40namespace qsys {
41
42template <class T>
43struct MmckResult {
44 T meanQueueLength; ///< L, mean number in system
45 T meanQueueLengthQ; ///< Lq, mean number waiting
46 T meanWaitingTime; ///< Wq = Lq/lambda_eff
47 T meanSojournTime; ///< W = L /lambda_eff
48 T utilization; ///< per-server utilization lambda_eff/(c mu)
49 T throughput; ///< lambda_eff = lambda (1 - p_K)
50 T lossProbability; ///< p_K
51 std::vector<T> queueLengthDist; ///< p_0 ... p_K
52};
53
54/**
55 * @brief Exact analysis of the M/M/c/K queue (truncated Erlang form).
56 *
57 * @param lambda Poisson arrival rate
58 * @param mu exponential service rate of one server
59 * @param c number of servers, c >= 1
60 * @param K system capacity, K >= c
61 */
62template <class T>
63MmckResult<T> qsys_mmck(const T& lambda, const T& mu, unsigned c, unsigned K) {
64 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
65 if (lambda <= zero) throw InputError("qsys_mmck: lambda must be a positive real scalar");
66 if (mu <= zero) throw InputError("qsys_mmck: mu must be a positive real scalar");
67 if (c < 1) throw InputError("qsys_mmck: c must be a positive integer");
68 if (K < c) throw InputError("qsys_mmck: K must be an integer >= c");
69
70 const T a = lambda / mu;
71 const T rho = a / num_traits<T>::from_int(static_cast<long>(c));
72
73 std::vector<T> p(K + 1, zero);
74 for (unsigned n = 0; n < c; ++n) p[n] = num_pow_int(a, n) / num_factorial<T>(n);
75 const T ac_over_cfact = num_pow_int(a, c) / num_factorial<T>(c);
76 for (unsigned n = c; n <= K; ++n) p[n] = ac_over_cfact * num_pow_int(rho, n - c);
77
78 T S = zero;
79 for (const T& v : p) S += v;
80 if (S <= zero) throw NumericError("qsys_mmck: stationary distribution failed to normalize");
81 for (T& v : p) v /= S;
82
84 r.queueLengthDist = p;
85 T L = zero, Lq = zero;
86 for (unsigned n = 0; n <= K; ++n) {
87 L += num_traits<T>::from_int(static_cast<long>(n)) * p[n];
88 if (n > c) Lq += num_traits<T>::from_int(static_cast<long>(n - c)) * p[n];
89 }
90 r.meanQueueLength = L;
91 r.meanQueueLengthQ = Lq;
92 r.lossProbability = p[K];
93 const T lambdaEff = lambda * (one - p[K]);
94 r.throughput = lambdaEff;
95 r.utilization = lambdaEff / (num_traits<T>::from_int(static_cast<long>(c)) * mu);
96 if (lambdaEff > zero) {
97 r.meanWaitingTime = Lq / lambdaEff;
98 r.meanSojournTime = L / lambdaEff;
99 } else {
100 r.meanWaitingTime = zero;
101 r.meanSojournTime = zero;
102 }
103 return r;
104}
105
106} // namespace qsys
107} // namespace line
108
109#endif // LINE_API_QSYS_QSYS_MMCK_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
MmckResult< T > qsys_mmck(const T &lambda, const T &mu, unsigned c, unsigned K)
Exact analysis of the M/M/c/K queue (truncated Erlang form).
Definition qsys_mmck.h:63
T num_factorial(unsigned n)
Factorial as a value of T.
Definition number.h:184
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition number.h:192
Number-type abstraction for the templated API port.
Shared return type and arithmetic helpers for the templated qsys port.
std::vector< T > queueLengthDist
p_0 ... p_K
Definition qsys_mmck.h:51
T meanWaitingTime
Wq = Lq/lambda_eff.
Definition qsys_mmck.h:46
T meanQueueLengthQ
Lq, mean number waiting.
Definition qsys_mmck.h:45
T meanQueueLength
L, mean number in system.
Definition qsys_mmck.h:44
T utilization
per-server utilization lambda_eff/(c mu)
Definition qsys_mmck.h:48
T meanSojournTime
W = L /lambda_eff.
Definition qsys_mmck.h:47
T throughput
lambda_eff = lambda (1 - p_K)
Definition qsys_mmck.h:49