LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_phm1.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_PHM1_H
6#define LINE_API_QSYS_QSYS_PHM1_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Exact PH/M/1, the GI/M/1 queue with phase-type interarrival times.
12 *
13 * Templated port of matlab/src/api/qsys/qsys_phm1.m, cross-checked against
14 * jar/src/main/java/jline/api/qsys/Qsys_phm1.java.
15 *
16 * With psi_A(s) = alpha (sI - T)^-1 (-T e) the interarrival LST, sigma is the
17 * root in (0,1) of the GI/M/1 equation
18 *
19 * sigma = psi_A(mu(1 - sigma)),
20 *
21 * and then L = rho/(1-sigma), Lq = rho sigma/(1-sigma), Wq = Lq/lambda,
22 * W = Wq + 1/mu. Since psi_A is completely monotone and psi_A(0) = 1, the
23 * function f(sigma) = sigma - psi_A(mu(1-sigma)) is negative just above zero
24 * and positive just below one whenever rho < 1, so the bracket [0,1] always
25 * contains the root and bisection is unconditional. MATLAB brackets on
26 * [1e-12, 1-1e-12] with fzero and falls back to fixed-point iteration; the
27 * port bisects on the same bracket, which reaches the same root -- f is
28 * strictly increasing there -- without the fallback.
29 *
30 * ARITHMETIC. sigma is defined by a transcendental equation and reached by a
31 * tolerance-driven iteration, so the function is gated.
32 *
33 * At k = 1 with T = [-lambda] the arrival process is Poisson, psi_A is
34 * lambda/(s+lambda), the root is sigma = rho and every metric collapses onto
35 * the M/M/1 values. That identity is the sharpest available check on the port.
36 */
37
38#include <cstddef>
39#include <vector>
40
42#include "line/num/number.h"
43#include "line/util/error.h"
44#include "line/util/linalg.h"
45#include "line/util/lu.h"
46#include "line/util/matrix.h"
47
48namespace line {
49namespace qsys {
50
51template <class T>
52struct PhM1Result {
55 T meanWaitingTime; ///< Wq
57 T utilization; ///< rho = lambda/mu
58 T sigma; ///< GI/M/1 root in (0,1)
59};
60
61namespace detail {
62
63/** psi_A(s) = alpha (sI - T)^-1 (-T e), the interarrival LST. */
64template <class T>
65T ph_lst(const std::vector<T>& alpha, const Matrix<T>& Tm, const T& s) {
66 const std::size_t k = Tm.rows();
67 Matrix<T> A(k, k);
68 for (std::size_t i = 0; i < k; ++i)
69 for (std::size_t j = 0; j < k; ++j) A(i, j) = (i == j ? s : num_traits<T>::from_int(0)) - Tm(i, j);
70 std::vector<T> t_vec(k, num_traits<T>::from_int(0));
71 for (std::size_t i = 0; i < k; ++i)
72 for (std::size_t j = 0; j < k; ++j) t_vec[i] -= Tm(i, j);
73 const std::vector<T> y = line::solve(A, t_vec);
74 T out = num_traits<T>::from_int(0);
75 for (std::size_t i = 0; i < k; ++i) out += alpha[i] * y[i];
76 return out;
77}
78
79} // namespace detail
80
81/**
82 * @brief Exact PH/M/1, the GI/M/1 queue with phase-type interarrival times.
83 *
84 * @param alpha PH entry probability vector, length k
85 * @param Tm PH sub-generator, k x k
86 * @param mu exponential service rate
87 * @param tol width of the bisection bracket at which to stop (MATLAB's
88 * fzero stops at the double round-off level, so 1e-16 is the
89 * matching default)
90 */
91template <class T>
92PhM1Result<T> qsys_phm1(const std::vector<T>& alpha, const Matrix<T>& Tm, const T& mu,
93 const T& tol) {
95 "qsys_phm1 requires transcendental arithmetic");
96 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
97 if (mu <= zero) throw InputError("qsys_phm1: service rate mu must be positive");
98 const std::size_t k = Tm.rows();
99 if (Tm.cols() != k) throw InputError("qsys_phm1: T must be square");
100 if (alpha.size() != k) throw InputError("qsys_phm1: alpha length must match T dimension");
101
102 // Mean interarrival time = alpha (-T)^-1 e.
103 Matrix<T> negT(k, k);
104 for (std::size_t i = 0; i < k; ++i)
105 for (std::size_t j = 0; j < k; ++j) negT(i, j) = -Tm(i, j);
106 const std::vector<T> m = line::solve(negT, ones<T>(k));
107 T mean_ia = zero;
108 for (std::size_t i = 0; i < k; ++i) mean_ia += alpha[i] * m[i];
109 if (mean_ia <= zero) throw InputError("qsys_phm1: non-positive mean interarrival time");
110 const T lambda = one / mean_ia;
111 const T rho = lambda / mu;
112 if (rho >= one) throw InputError("qsys_phm1: load rho must be strictly less than 1");
113
114 T a = T(num_traits<T>::from_double(1e-12));
115 T b = one - T(num_traits<T>::from_double(1e-12));
116 for (unsigned it = 0; it < 4000u; ++it) {
117 if (b - a <= tol) break;
118 const T mid = (a + b) / num_traits<T>::from_int(2);
119 const T f = mid - detail::ph_lst(alpha, Tm, T(mu * (one - mid)));
120 if (f < zero)
121 a = mid;
122 else
123 b = mid;
124 }
125 const T sigma = (a + b) / num_traits<T>::from_int(2);
126
128 r.sigma = sigma;
129 r.utilization = rho;
130 r.meanQueueLength = rho / (one - sigma);
131 r.meanWaitingQueue = rho * sigma / (one - sigma);
132 r.meanWaitingTime = r.meanWaitingQueue / lambda;
133 r.meanSojournTime = r.meanWaitingTime + one / mu;
134 return r;
135}
136
137/** qsys_phm1 with the fzero-equivalent default bracket tolerance. */
138template <class T>
139PhM1Result<T> qsys_phm1(const std::vector<T>& alpha, const Matrix<T>& Tm, const T& mu) {
140 return qsys_phm1(alpha, Tm, mu, T(num_traits<T>::from_double(1e-16)));
141}
142
143} // namespace qsys
144} // namespace line
145
146#endif // LINE_API_QSYS_QSYS_PHM1_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.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
PhM1Result< T > qsys_phm1(const std::vector< T > &alpha, const Matrix< T > &Tm, const T &mu, const T &tol)
Exact PH/M/1, the GI/M/1 queue with phase-type interarrival times.
Definition qsys_phm1.h:92
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
Number-type abstraction for the templated API port.
Shared return type and arithmetic helpers for the templated qsys port.
T sigma
GI/M/1 root in (0,1).
Definition qsys_phm1.h:58
T utilization
rho = lambda/mu
Definition qsys_phm1.h:57